@@ -82,3 +82,130 @@ class User(BubbleBaseModel, typename="user"):
8282
8383 with pytest .raises (UnknownFieldError , match = "unknown field: nonexistent" ):
8484 await User .update (uid = "abc123" , nonexistent = "value" )
85+
86+
87+ @respx .mock
88+ async def test_create_translates_field_aliases (configured_client : None ) -> None :
89+ """Verify create() translates Python field names to Bubble aliases."""
90+
91+ class Order (BubbleBaseModel , typename = "order" ):
92+ company : str = Field (alias = "Buying company" )
93+ status : str
94+
95+ route = respx .post ("https://example.com/order" ).mock (
96+ return_value = httpx .Response (200 , json = {"status" : "success" , "id" : "new123" })
97+ )
98+
99+ order = await Order .create (company = "Acme Corp" , status = "pending" )
100+
101+ assert route .call_count == 1
102+ request_body = json .loads (route .calls [0 ].request .content )
103+ assert request_body == {"Buying company" : "Acme Corp" , "status" : "pending" }
104+ assert order .company == "Acme Corp"
105+ assert order .status == "pending"
106+ assert order .uid == "new123"
107+
108+
109+ async def test_create_raises_for_unknown_field () -> None :
110+ """Verify create() raises UnknownFieldError for fields not in the model."""
111+
112+ class User (BubbleBaseModel , typename = "user" ):
113+ name : str
114+
115+ with pytest .raises (UnknownFieldError , match = "unknown field: nonexistent" ):
116+ await User .create (name = "test" , nonexistent = "value" )
117+
118+
119+ @respx .mock
120+ async def test_create_or_update_translates_match_aliases (configured_client : None ) -> None :
121+ """Verify create_or_update() translates match field names to Bubble aliases."""
122+ from bubble_data_api_client .types import OnMultiple
123+
124+ class Order (BubbleBaseModel , typename = "order" ):
125+ external_id : str = Field (alias = "External ID" )
126+ company : str = Field (alias = "Buying company" )
127+
128+ # mock find returning no results (will create)
129+ find_route = respx .get ("https://example.com/order" ).mock (
130+ return_value = httpx .Response (200 , json = {"response" : {"results" : [], "count" : 0 , "remaining" : 0 }})
131+ )
132+ # mock create
133+ create_route = respx .post ("https://example.com/order" ).mock (
134+ return_value = httpx .Response (200 , json = {"status" : "success" , "id" : "new123" })
135+ )
136+
137+ _order , created = await Order .create_or_update (
138+ match = {"external_id" : "ext-001" },
139+ data = {"company" : "Acme Corp" },
140+ on_multiple = OnMultiple .ERROR ,
141+ )
142+
143+ assert created is True
144+ assert find_route .call_count == 1
145+ # verify find used aliased field name in constraint
146+ find_request_url = str (find_route .calls [0 ].request .url )
147+ assert "External%20ID" in find_request_url or "External+ID" in find_request_url
148+
149+ assert create_route .call_count == 1
150+ request_body = json .loads (create_route .calls [0 ].request .content )
151+ assert request_body == {"External ID" : "ext-001" , "Buying company" : "Acme Corp" }
152+
153+
154+ @respx .mock
155+ async def test_create_or_update_translates_data_aliases (configured_client : None ) -> None :
156+ """Verify create_or_update() translates data field names to Bubble aliases."""
157+ from bubble_data_api_client .types import OnMultiple
158+
159+ class Order (BubbleBaseModel , typename = "order" ):
160+ external_id : str = Field (alias = "External ID" )
161+ company : str = Field (alias = "Buying company" )
162+
163+ # mock find returning one result (will update)
164+ respx .get ("https://example.com/order" ).mock (
165+ return_value = httpx .Response (
166+ 200 , json = {"response" : {"results" : [{"_id" : "existing123" }], "count" : 1 , "remaining" : 0 }}
167+ )
168+ )
169+ # mock update
170+ update_route = respx .patch ("https://example.com/order/existing123" ).mock (return_value = httpx .Response (204 ))
171+
172+ _order , created = await Order .create_or_update (
173+ match = {"external_id" : "ext-001" },
174+ data = {"company" : "Updated Corp" },
175+ on_multiple = OnMultiple .ERROR ,
176+ )
177+
178+ assert created is False
179+ assert update_route .call_count == 1
180+ request_body = json .loads (update_route .calls [0 ].request .content )
181+ assert request_body == {"Buying company" : "Updated Corp" }
182+
183+
184+ async def test_create_or_update_raises_for_unknown_match_field () -> None :
185+ """Verify create_or_update() raises UnknownFieldError for unknown match fields."""
186+ from bubble_data_api_client .types import OnMultiple
187+
188+ class User (BubbleBaseModel , typename = "user" ):
189+ name : str
190+
191+ with pytest .raises (UnknownFieldError , match = "unknown field: nonexistent" ):
192+ await User .create_or_update (
193+ match = {"nonexistent" : "value" },
194+ data = {"name" : "test" },
195+ on_multiple = OnMultiple .ERROR ,
196+ )
197+
198+
199+ async def test_create_or_update_raises_for_unknown_data_field () -> None :
200+ """Verify create_or_update() raises UnknownFieldError for unknown data fields."""
201+ from bubble_data_api_client .types import OnMultiple
202+
203+ class User (BubbleBaseModel , typename = "user" ):
204+ name : str
205+
206+ with pytest .raises (UnknownFieldError , match = "unknown field: nonexistent" ):
207+ await User .create_or_update (
208+ match = {"name" : "test" },
209+ data = {"nonexistent" : "value" },
210+ on_multiple = OnMultiple .ERROR ,
211+ )
0 commit comments