KevinIsInCoding Claude Sonnet 4.6 commited on
Commit
01a873b
·
unverified ·
1 Parent(s): b720671

test(wave-1): add test infrastructure and conftest.py (#16)

Browse files

Set up pytest with pytest-mock, pytest-httpx, freezegun, and pytest-cov.
Adds tests/conftest.py with shared fixtures (als_patient, mock_client) and
factory functions (make_text_block, make_tool_use_block, make_message, FakeStream)
that all subsequent test waves will depend on.

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>

pyproject.toml CHANGED
@@ -12,3 +12,27 @@ dependencies = [
12
  "rich>=13.0.0",
13
  "gradio>=6.14.0,<7.0.0",
14
  ]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  "rich>=13.0.0",
13
  "gradio>=6.14.0,<7.0.0",
14
  ]
15
+
16
+ [project.optional-dependencies]
17
+ dev = [
18
+ "pytest>=8.0",
19
+ "pytest-mock>=3.14",
20
+ "pytest-httpx>=0.35",
21
+ "freezegun>=1.5",
22
+ "pytest-cov",
23
+ ]
24
+
25
+ [tool.pytest.ini_options]
26
+ testpaths = ["tests"]
27
+
28
+ [tool.coverage.run]
29
+ omit = [
30
+ "app.py",
31
+ "config.py",
32
+ "translations.py",
33
+ "tools.py",
34
+ "beacon_logging.py",
35
+ "clinical_trials_guru.py",
36
+ "main.py",
37
+ "_console.py",
38
+ ]
tests/__init__.py ADDED
File without changes
tests/agents/__init__.py ADDED
File without changes
tests/conftest.py ADDED
@@ -0,0 +1,111 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Shared fixtures and mock builders for the Beacon test suite.
3
+
4
+ Factory functions (not fixtures) build Anthropic mock objects so they can be
5
+ called from any scope without fixture injection.
6
+ """
7
+ from __future__ import annotations
8
+
9
+ from typing import Iterator
10
+ from unittest.mock import MagicMock
11
+
12
+ import pytest
13
+
14
+ from models import PatientProfile
15
+
16
+
17
+ # ---------------------------------------------------------------------------
18
+ # Anthropic mock-object factories
19
+ # ---------------------------------------------------------------------------
20
+
21
+ def make_text_block(text: str) -> MagicMock:
22
+ """Return a mock ContentBlock with type='text' and .text=text."""
23
+ block = MagicMock()
24
+ block.type = "text"
25
+ block.text = text
26
+ return block
27
+
28
+
29
+ def make_tool_use_block(name: str, input_data: dict, tool_use_id: str = "tu_001") -> MagicMock:
30
+ """Return a mock ContentBlock with type='tool_use'."""
31
+ block = MagicMock()
32
+ block.type = "tool_use"
33
+ block.name = name
34
+ block.id = tool_use_id
35
+ block.input = input_data
36
+ return block
37
+
38
+
39
+ def make_message(
40
+ content: list,
41
+ stop_reason: str = "end_turn",
42
+ model: str = "claude-sonnet-4-6",
43
+ ) -> MagicMock:
44
+ """Return a mock Message with .content, .stop_reason, and .model."""
45
+ msg = MagicMock()
46
+ msg.content = content
47
+ msg.stop_reason = stop_reason
48
+ msg.model = model
49
+ return msg
50
+
51
+
52
+ class FakeStream:
53
+ """
54
+ Minimal stand-in for the object returned by ``client.messages.stream()``.
55
+
56
+ Usage::
57
+
58
+ fake = FakeStream(tokens=["Hello", " world"], final_message=make_message([...]))
59
+ with fake as stream:
60
+ for chunk in stream.text_stream:
61
+ ...
62
+ msg = stream.get_final_message()
63
+ """
64
+
65
+ def __init__(self, tokens: list[str], final_message: MagicMock) -> None:
66
+ self._tokens = tokens
67
+ self._final_message = final_message
68
+
69
+ def __enter__(self) -> "FakeStream":
70
+ return self
71
+
72
+ def __exit__(self, *args: object) -> None:
73
+ pass
74
+
75
+ @property
76
+ def text_stream(self) -> Iterator[str]:
77
+ yield from self._tokens
78
+
79
+ def get_final_message(self) -> MagicMock:
80
+ return self._final_message
81
+
82
+
83
+ # ---------------------------------------------------------------------------
84
+ # Fixtures
85
+ # ---------------------------------------------------------------------------
86
+
87
+ @pytest.fixture
88
+ def als_patient() -> PatientProfile:
89
+ """ALS patient fixture used across most tests."""
90
+ return PatientProfile(
91
+ disease="Amyotrophic Lateral Sclerosis",
92
+ age=52,
93
+ onset_months=18,
94
+ diagnosis_months=12,
95
+ benchmarks={"alsfrs_r": "38", "forced_vital_capacity_percent": "72"},
96
+ zip_code="02115",
97
+ country_code="US",
98
+ lat=42.3370,
99
+ lon=-71.1061,
100
+ radius_miles=100,
101
+ phases=["2", "3"],
102
+ include_eap=False,
103
+ include_observational=False,
104
+ lang="en",
105
+ )
106
+
107
+
108
+ @pytest.fixture
109
+ def mock_client() -> MagicMock:
110
+ """Bare MagicMock Anthropic client — callers set side_effects as needed."""
111
+ return MagicMock()