Coverage for tests / test_union_body_discriminator.py: 100%
28 statements
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
« prev ^ index » next coverage.py v7.13.3, created at 2026-04-06 01:24 +0000
1from typing import Annotated, Any, Literal 1efgh
3from fastapi import FastAPI 1efgh
4from fastapi.testclient import TestClient 1efgh
5from inline_snapshot import snapshot 1efgh
6from pydantic import BaseModel, Field 1efgh
9def test_discriminator_pydantic_v2() -> None: 1efgh
10 from pydantic import Tag 1adbc
12 app = FastAPI() 1adbc
14 class FirstItem(BaseModel): 1adbc
15 value: Literal["first"] 1abc
16 price: int 1abc
18 class OtherItem(BaseModel): 1adbc
19 value: Literal["other"] 1abc
20 price: float 1abc
22 Item = Annotated[ 1adbc
23 Annotated[FirstItem, Tag("first")] | Annotated[OtherItem, Tag("other")],
24 Field(discriminator="value"),
25 ]
27 @app.post("/items/") 1adbc
28 def save_union_body_discriminator( 1adbc
29 item: Item, q: Annotated[str, Field(description="Query string")]
30 ) -> dict[str, Any]:
31 return {"item": item} 1adbc
33 client = TestClient(app) 1adbc
34 response = client.post("/items/?q=first", json={"value": "first", "price": 100}) 1adbc
35 assert response.status_code == 200, response.text 1adbc
36 assert response.json() == {"item": {"value": "first", "price": 100}} 1adbc
38 response = client.post("/items/?q=other", json={"value": "other", "price": 100.5}) 1adbc
39 assert response.status_code == 200, response.text 1adbc
40 assert response.json() == {"item": {"value": "other", "price": 100.5}} 1adbc
42 response = client.get("/openapi.json") 1adbc
43 assert response.status_code == 200, response.text 1adbc
44 assert response.json() == snapshot( 1adbc
45 {
46 "openapi": "3.1.0",
47 "info": {"title": "FastAPI", "version": "0.1.0"},
48 "paths": {
49 "/items/": {
50 "post": {
51 "summary": "Save Union Body Discriminator",
52 "operationId": "save_union_body_discriminator_items__post",
53 "parameters": [
54 {
55 "name": "q",
56 "in": "query",
57 "required": True,
58 "schema": {
59 "type": "string",
60 "description": "Query string",
61 "title": "Q",
62 },
63 }
64 ],
65 "requestBody": {
66 "required": True,
67 "content": {
68 "application/json": {
69 "schema": {
70 "oneOf": [
71 {"$ref": "#/components/schemas/FirstItem"},
72 {"$ref": "#/components/schemas/OtherItem"},
73 ],
74 "discriminator": {
75 "propertyName": "value",
76 "mapping": {
77 "first": "#/components/schemas/FirstItem",
78 "other": "#/components/schemas/OtherItem",
79 },
80 },
81 "title": "Item",
82 }
83 }
84 },
85 },
86 "responses": {
87 "200": {
88 "description": "Successful Response",
89 "content": {
90 "application/json": {
91 "schema": {
92 "type": "object",
93 "additionalProperties": True,
94 "title": "Response Save Union Body Discriminator Items Post",
95 }
96 }
97 },
98 },
99 "422": {
100 "description": "Validation Error",
101 "content": {
102 "application/json": {
103 "schema": {
104 "$ref": "#/components/schemas/HTTPValidationError"
105 }
106 }
107 },
108 },
109 },
110 }
111 }
112 },
113 "components": {
114 "schemas": {
115 "FirstItem": {
116 "properties": {
117 "value": {
118 "type": "string",
119 "const": "first",
120 "title": "Value",
121 },
122 "price": {"type": "integer", "title": "Price"},
123 },
124 "type": "object",
125 "required": ["value", "price"],
126 "title": "FirstItem",
127 },
128 "HTTPValidationError": {
129 "properties": {
130 "detail": {
131 "items": {
132 "$ref": "#/components/schemas/ValidationError"
133 },
134 "type": "array",
135 "title": "Detail",
136 }
137 },
138 "type": "object",
139 "title": "HTTPValidationError",
140 },
141 "OtherItem": {
142 "properties": {
143 "value": {
144 "type": "string",
145 "const": "other",
146 "title": "Value",
147 },
148 "price": {"type": "number", "title": "Price"},
149 },
150 "type": "object",
151 "required": ["value", "price"],
152 "title": "OtherItem",
153 },
154 "ValidationError": {
155 "properties": {
156 "ctx": {"title": "Context", "type": "object"},
157 "input": {"title": "Input"},
158 "loc": {
159 "items": {
160 "anyOf": [{"type": "string"}, {"type": "integer"}]
161 },
162 "type": "array",
163 "title": "Location",
164 },
165 "msg": {"type": "string", "title": "Message"},
166 "type": {"type": "string", "title": "Error Type"},
167 },
168 "type": "object",
169 "required": ["loc", "msg", "type"],
170 "title": "ValidationError",
171 },
172 }
173 },
174 }
175 )