@@ -115,6 +115,40 @@ def test_path_params_update(self, client: Cloudflare) -> None:
115115 value = "Some Value" ,
116116 )
117117
118+ @parametrize
119+ @pytest .mark .respx (base_url = base_url )
120+ def test_update_uses_multipart_form_data (self , client : Cloudflare , respx_mock : MockRouter ) -> None :
121+ """Test that values.update sends data as multipart/form-data, not JSON.
122+
123+ This is a regression test for https://github.com/cloudflare/cloudflare-python/issues/2519
124+ """
125+ respx_mock .put (
126+ "/accounts/023e105f4ecef8ad9ca31a8372d0c353/storage/kv/namespaces/0f2ac74b498b48028cb68387c421e279/values/My-Key"
127+ ).mock (return_value = httpx .Response (200 , json = {"success" : True , "result" : {}}))
128+
129+ client .kv .namespaces .values .update (
130+ key_name = "My-Key" ,
131+ account_id = "023e105f4ecef8ad9ca31a8372d0c353" ,
132+ namespace_id = "0f2ac74b498b48028cb68387c421e279" ,
133+ metadata = '{"someMetadataKey": "someMetadataValue"}' ,
134+ value = "Some Value" ,
135+ )
136+
137+ # Verify the request was made
138+ assert respx_mock .calls .call_count == 1
139+ request = respx_mock .calls [0 ].request
140+
141+ # Verify Content-Type is multipart/form-data (with boundary)
142+ content_type = request .headers .get ("content-type" , "" )
143+ assert content_type .startswith ("multipart/form-data" ), (
144+ f"Expected Content-Type to start with 'multipart/form-data', got '{ content_type } '"
145+ )
146+
147+ # Verify the body contains separate form fields for value and metadata
148+ body = request .content .decode ("utf-8" )
149+ assert "Some Value" in body , "Value should be in the multipart body"
150+ assert "someMetadataKey" in body , "Metadata should be in the multipart body"
151+
118152 @parametrize
119153 def test_method_delete (self , client : Cloudflare ) -> None :
120154 value = client .kv .namespaces .values .delete (
@@ -351,6 +385,40 @@ async def test_path_params_update(self, async_client: AsyncCloudflare) -> None:
351385 value = "Some Value" ,
352386 )
353387
388+ @parametrize
389+ @pytest .mark .respx (base_url = base_url )
390+ async def test_update_uses_multipart_form_data (self , async_client : AsyncCloudflare , respx_mock : MockRouter ) -> None :
391+ """Test that values.update sends data as multipart/form-data, not JSON.
392+
393+ This is a regression test for https://github.com/cloudflare/cloudflare-python/issues/2519
394+ """
395+ respx_mock .put (
396+ "/accounts/023e105f4ecef8ad9ca31a8372d0c353/storage/kv/namespaces/0f2ac74b498b48028cb68387c421e279/values/My-Key"
397+ ).mock (return_value = httpx .Response (200 , json = {"success" : True , "result" : {}}))
398+
399+ await async_client .kv .namespaces .values .update (
400+ key_name = "My-Key" ,
401+ account_id = "023e105f4ecef8ad9ca31a8372d0c353" ,
402+ namespace_id = "0f2ac74b498b48028cb68387c421e279" ,
403+ metadata = '{"someMetadataKey": "someMetadataValue"}' ,
404+ value = "Some Value" ,
405+ )
406+
407+ # Verify the request was made
408+ assert respx_mock .calls .call_count == 1
409+ request = respx_mock .calls [0 ].request
410+
411+ # Verify Content-Type is multipart/form-data (with boundary)
412+ content_type = request .headers .get ("content-type" , "" )
413+ assert content_type .startswith ("multipart/form-data" ), (
414+ f"Expected Content-Type to start with 'multipart/form-data', got '{ content_type } '"
415+ )
416+
417+ # Verify the body contains separate form fields for value and metadata
418+ body = request .content .decode ("utf-8" )
419+ assert "Some Value" in body , "Value should be in the multipart body"
420+ assert "someMetadataKey" in body , "Metadata should be in the multipart body"
421+
354422 @parametrize
355423 async def test_method_delete (self , async_client : AsyncCloudflare ) -> None :
356424 value = await async_client .kv .namespaces .values .delete (
0 commit comments