Skip to content

Commit 1263a21

Browse files
committed
expand end to end integration test coverage
1 parent 374759a commit 1263a21

3 files changed

Lines changed: 61 additions & 2 deletions

File tree

src/bubble_data_api_client/client/raw_client.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,12 @@ async def create(self, typename: str, data: typing.Any) -> httpx.Response:
8585
"""Create a new thing with the given data."""
8686
return await self._transport.post(url=f"/{typename}", json=data)
8787

88+
# https://manual.bubble.io/core-resources/api/the-bubble-api/the-data-api/data-api-requests#bulk-create-things
8889
async def bulk_create(self, typename: str, data: list[typing.Any]) -> httpx.Response:
89-
"""Create multiple things in a single request using newline-delimited JSON."""
90+
"""Create multiple things in a single request using newline-delimited JSON.
91+
92+
Response is text/plain with one JSON object per line: {"status":"success","id":"..."}
93+
"""
9094
return await self._transport.post_text(
9195
url=f"/{typename}/bulk",
9296
content="\n".join(json.dumps(item) for item in data),

src/tests/integration/test_raw_client.py

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
import http
2+
import json
3+
import warnings
24
from collections.abc import AsyncGenerator
35

46
import httpx
@@ -38,6 +40,56 @@ async def test_retrieve_success(typename: str, test_thing_id: str, bubble_raw_cl
3840
assert response_body["response"]["text"] == "integration test"
3941

4042

43+
async def test_bulk_create_success(typename: str, bubble_raw_client: raw_client.RawClient):
44+
"""Test that bulk_create creates multiple things and returns their IDs."""
45+
created_ids: list[str] = []
46+
47+
try:
48+
response = await bubble_raw_client.bulk_create(
49+
typename=typename,
50+
data=[{"text": "bulk test 1"}, {"text": "bulk test 2"}],
51+
)
52+
53+
# bubble returns text/plain with newline-delimited JSON
54+
assert response.status_code == http.HTTPStatus.OK
55+
assert response.headers["content-type"] == "text/plain"
56+
lines = response.text.strip().split("\n")
57+
assert len(lines) == 2
58+
59+
# parse each line and extract IDs for cleanup
60+
for line in lines:
61+
result = json.loads(line)
62+
assert result["status"] == "success"
63+
assert "id" in result
64+
created_ids.append(result["id"])
65+
66+
# verify both items exist with correct data
67+
expected_texts = ["bulk test 1", "bulk test 2"]
68+
for uid, expected_text in zip(created_ids, expected_texts, strict=True):
69+
retrieve_response = await bubble_raw_client.retrieve(typename=typename, uid=uid)
70+
assert retrieve_response.status_code == http.HTTPStatus.OK
71+
assert retrieve_response.json()["response"]["text"] == expected_text
72+
73+
finally:
74+
# cleanup all created items
75+
for uid in created_ids:
76+
try:
77+
await bubble_raw_client.delete(typename=typename, uid=uid)
78+
except Exception as e:
79+
warnings.warn(f"cleanup failed for {uid}: {e}", stacklevel=2)
80+
81+
82+
async def test_update_success(typename: str, test_thing_id: str, bubble_raw_client: raw_client.RawClient):
83+
"""Test that we can update a thing."""
84+
response = await bubble_raw_client.update(typename=typename, uid=test_thing_id, data={"text": "updated text"})
85+
# 204 No Content = success
86+
assert response.status_code == http.HTTPStatus.NO_CONTENT
87+
88+
# verify the update
89+
response = await bubble_raw_client.retrieve(typename=typename, uid=test_thing_id)
90+
assert response.json()["response"]["text"] == "updated text"
91+
92+
4193
async def test_delete_success(typename: str, bubble_raw_client: raw_client.RawClient):
4294
"""Test that we can delete a thing."""
4395
response_create = await bubble_raw_client.create(typename, data={"text": "integration test delete success"})

src/tests/unit/client/test_raw_client.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,10 @@ async def test_replace(configured_client: None) -> None:
4040
@respx.mock
4141
async def test_bulk_create(configured_client: None) -> None:
4242
"""Test that bulk_create posts newline-delimited JSON."""
43+
# bubble returns text/plain with one JSON object per line
44+
mock_response_text = '{"status":"success","id":"1234x5678"}\n{"status":"success","id":"1234x5679"}'
4345
route = respx.post("https://example.com/customer/bulk").mock(
44-
return_value=httpx.Response(200, json={"status": "success", "count": 2})
46+
return_value=httpx.Response(200, text=mock_response_text, headers={"content-type": "text/plain"})
4547
)
4648

4749
async with raw_client.RawClient() as client:
@@ -51,6 +53,7 @@ async def test_bulk_create(configured_client: None) -> None:
5153
)
5254

5355
assert response.status_code == 200
56+
assert response.headers["content-type"] == "text/plain"
5457
assert route.call_count == 1
5558
# verify it sent newline-delimited JSON
5659
request_content = route.calls[0].request.content.decode()

0 commit comments

Comments
 (0)