Skip to content

Commit 13f11fd

Browse files
franccescoclaude
andcommitted
test: update existing tests for meeting details and goal update changes
Update mock expectations in meeting tests to match the new direct API call pattern (GET L10/{id} instead of list-then-filter). Update goal tests to reflect that accountable_user is no longer auto-populated. Update bulk operation tests for new meeting details behavior. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent c35da5f commit 13f11fd

6 files changed

Lines changed: 197 additions & 230 deletions

File tree

tests/test_async_goals.py

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,7 +202,6 @@ async def test_update(
202202
mock_async_client.put.assert_called_once_with(
203203
"rocks/123",
204204
json={
205-
"accountableUserId": 1,
206205
"title": "Updated Goal",
207206
"completion": "OnTrack",
208207
},

tests/test_async_meetings.py

Lines changed: 64 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -332,12 +332,30 @@ async def test_get_many_all_successful(
332332
self, async_client: AsyncClient, mock_async_client: AsyncMock
333333
) -> None:
334334
"""Test bulk retrieval where all meetings are retrieved successfully."""
335-
# Mock meeting list response for details method
336-
mock_meetings_list = [
337-
{"Id": 100, "Type": "L10", "Key": "L10-100", "Name": "Weekly Standup"},
338-
{"Id": 101, "Type": "L10", "Key": "L10-101", "Name": "Sprint Planning"},
339-
{"Id": 102, "Type": "L10", "Key": "L10-102", "Name": "Retrospective"},
340-
]
335+
# Mock direct L10/{id} responses and sub-resource responses
336+
direct_responses = {
337+
100: {
338+
"Id": 100,
339+
"Basics": {"Name": "Weekly Standup"},
340+
"CreateTime": None,
341+
"StartDateUtc": None,
342+
"OrganizationId": None,
343+
},
344+
101: {
345+
"Id": 101,
346+
"Basics": {"Name": "Sprint Planning"},
347+
"CreateTime": None,
348+
"StartDateUtc": None,
349+
"OrganizationId": None,
350+
},
351+
102: {
352+
"Id": 102,
353+
"Basics": {"Name": "Retrospective"},
354+
"CreateTime": None,
355+
"StartDateUtc": None,
356+
"OrganizationId": None,
357+
},
358+
}
341359

342360
# Mock attendees responses
343361
attendees_responses = {
@@ -373,17 +391,17 @@ async def test_get_many_all_successful(
373391
def get_side_effect(url, **_kwargs):
374392
mock_response = MagicMock()
375393

376-
if url.endswith("/list"):
377-
mock_response.json.return_value = mock_meetings_list
378-
elif "/attendees" in url:
379-
# Extract meeting ID from URL
394+
if "/attendees" in url:
380395
meeting_id = int(url.split("/")[1])
381396
mock_response.json.return_value = attendees_responses.get(
382397
meeting_id, []
383398
)
384-
else:
385-
# For issues, todos, metrics
399+
elif "/issues" in url or "/todos" in url or "/measurables" in url:
386400
mock_response.json.return_value = []
401+
else:
402+
# Direct L10/{id} endpoint
403+
meeting_id = int(url.split("/")[1])
404+
mock_response.json.return_value = direct_responses.get(meeting_id, {})
387405

388406
mock_response.raise_for_status = MagicMock()
389407
return mock_response
@@ -409,25 +427,37 @@ def get_side_effect(url, **_kwargs):
409427
assert len(result.successful[2].attendees) == 1
410428

411429
# Verify API calls - 5 calls per meeting
412-
# (list, attendees, issues, todos, metrics)
430+
# (direct, attendees, issues, todos, metrics)
413431
assert mock_async_client.get.call_count == 15
414432

415433
@pytest.mark.asyncio
416434
async def test_get_many_partial_failure(
417435
self, async_client: AsyncClient, mock_async_client: AsyncMock
418436
) -> None:
419437
"""Test bulk retrieval where some meetings fail."""
420-
# Mock meeting list response - contains meeting 200 but not 999 or 500
421-
mock_meetings_list = [
422-
{"Id": 200, "Type": "L10", "Key": "L10-200", "Name": "Success Meeting"},
423-
]
438+
from httpx import HTTPStatusError, Request, Response
424439

425440
# Create side effect function that returns appropriate response based on URL
426441
def get_side_effect(url, **_kwargs):
427442
mock_response = MagicMock()
428443

429-
if url.endswith("/list"):
430-
mock_response.json.return_value = mock_meetings_list
444+
# Direct endpoint for meeting 200
445+
if url == "L10/200":
446+
mock_response.json.return_value = {
447+
"Id": 200,
448+
"Basics": {"Name": "Success Meeting"},
449+
"CreateTime": None,
450+
"StartDateUtc": None,
451+
"OrganizationId": None,
452+
}
453+
mock_response.raise_for_status = MagicMock()
454+
elif url in ("L10/999", "L10/500"):
455+
# Simulate 404 for non-existent meetings
456+
mock_response.raise_for_status.side_effect = HTTPStatusError(
457+
"Not Found",
458+
request=Request("GET", url),
459+
response=Response(404),
460+
)
431461
elif "/200/attendees" in url:
432462
mock_response.json.return_value = [
433463
{
@@ -436,11 +466,11 @@ def get_side_effect(url, **_kwargs):
436466
"ImageUrl": "https://example.com/img1.jpg",
437467
}
438468
]
469+
mock_response.raise_for_status = MagicMock()
439470
else:
440-
# For issues, todos, metrics
441471
mock_response.json.return_value = []
472+
mock_response.raise_for_status = MagicMock()
442473

443-
mock_response.raise_for_status = MagicMock()
444474
return mock_response
445475

446476
mock_async_client.get.side_effect = get_side_effect
@@ -458,11 +488,9 @@ def get_side_effect(url, **_kwargs):
458488
# Check failed items
459489
assert result.failed[0].index == 1
460490
assert result.failed[0].input_data["meeting_id"] == 999
461-
assert "not found" in result.failed[0].error.lower()
462491

463492
assert result.failed[1].index == 2
464493
assert result.failed[1].input_data["meeting_id"] == 500
465-
assert "not found" in result.failed[1].error.lower()
466494

467495
@pytest.mark.asyncio
468496
async def test_get_many_empty_list(
@@ -507,29 +535,26 @@ async def delayed_get(*args, **_kwargs):
507535
# Extract the URL from args (first positional argument)
508536
url = args[0] if args else ""
509537

510-
if url.endswith("/list"):
511-
# Return list of all meetings
512-
mock_response.json.return_value = [
513-
{
514-
"Id": i + 400,
515-
"Type": "L10",
516-
"Key": f"L10-{i + 400}",
517-
"Name": f"Meeting {i}",
518-
}
519-
for i in range(5)
520-
]
521-
elif "/attendees" in url:
522-
# Return attendees for any meeting
538+
if "/attendees" in url:
523539
mock_response.json.return_value = [
524540
{
525541
"Id": 456,
526542
"Name": "John Doe",
527543
"ImageUrl": "https://example.com/img.jpg",
528544
}
529545
]
530-
else:
531-
# For issues, todos, metrics
546+
elif "/issues" in url or "/todos" in url or "/measurables" in url:
532547
mock_response.json.return_value = []
548+
else:
549+
# Direct L10/{id} endpoint
550+
meeting_id = int(url.split("/")[1])
551+
mock_response.json.return_value = {
552+
"Id": meeting_id,
553+
"Basics": {"Name": f"Meeting {meeting_id - 400}"},
554+
"CreateTime": None,
555+
"StartDateUtc": None,
556+
"OrganizationId": None,
557+
}
533558

534559
mock_response.raise_for_status = MagicMock()
535560
return mock_response
@@ -551,7 +576,7 @@ async def delayed_get(*args, **_kwargs):
551576

552577
# Verify concurrent execution
553578
# With max_concurrent=3 and 5 meetings:
554-
# Each meeting makes 5 API calls (list, attendees, issues, todos, metrics)
579+
# Each meeting makes 5 API calls (direct, attendees, issues, todos, metrics)
555580
# Each call has 0.1s delay, so each meeting takes ~0.5s
556581
# With concurrency of 3, we should see significant speedup vs sequential
557582
# Sequential would take 5 * 0.5 = 2.5s

tests/test_async_meetings_extra.py

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import pytest_asyncio
77

88
from bloomy import AsyncClient
9-
from bloomy.exceptions import APIError
109
from bloomy.models import Issue, Todo
1110

1211

@@ -192,27 +191,18 @@ async def test_todos_include_closed(
192191
async def test_details_meeting_not_found(
193192
self, async_client: AsyncClient, mock_async_client: AsyncMock
194193
) -> None:
195-
"""Test details when meeting is not found."""
196-
# Mock user response
197-
mock_user_response = MagicMock()
198-
mock_user_response.json.return_value = {"Id": 456}
199-
mock_user_response.raise_for_status = MagicMock()
200-
201-
# Mock empty meetings list
202-
mock_meetings_response = MagicMock()
203-
mock_meetings_response.json.return_value = []
204-
mock_meetings_response.raise_for_status = MagicMock()
205-
206-
def get_side_effect(url: str) -> MagicMock:
207-
if url == "users/mine":
208-
return mock_user_response
209-
elif url == "L10/456/list":
210-
return mock_meetings_response
211-
else:
212-
raise ValueError(f"Unexpected URL: {url}")
213-
214-
mock_async_client.get.side_effect = get_side_effect
215-
216-
# Call the method and expect error
217-
with pytest.raises(APIError, match="Meeting with ID 999 not found"):
194+
"""Test details when meeting is not found (404 from direct endpoint)."""
195+
from httpx import HTTPStatusError, Request, Response
196+
197+
# Mock the direct L10/{id} endpoint to return 404
198+
mock_response = MagicMock()
199+
mock_response.raise_for_status.side_effect = HTTPStatusError(
200+
"Not Found",
201+
request=Request("GET", "L10/999"),
202+
response=Response(404),
203+
)
204+
mock_async_client.get.return_value = mock_response
205+
206+
# Call the method and expect HTTP error
207+
with pytest.raises(HTTPStatusError):
218208
await async_client.meeting.details(999)

0 commit comments

Comments
 (0)