Skip to content

Commit 034399b

Browse files
committed
clean up unit test imports, rename builder fixture to mock_builder
1 parent cd9adce commit 034399b

8 files changed

Lines changed: 178 additions & 170 deletions

tests/sdk/test_async_execution.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@
1414
TASK_COMPLETION_SHORT,
1515
MockExecutionView,
1616
)
17-
from runloop_api_client.sdk.async_execution import AsyncExecution, _AsyncStreamingGroup
17+
from runloop_api_client.sdk import AsyncExecution
18+
from runloop_api_client.sdk.async_execution import _AsyncStreamingGroup
1819

1920
# Legacy aliases for backward compatibility
2021
SHORT_SLEEP = TASK_COMPLETION_SHORT

tests/sdk/test_async_execution_result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
import pytest
99

1010
from tests.sdk.conftest import MockExecutionView
11-
from runloop_api_client.sdk.async_execution_result import AsyncExecutionResult
11+
from runloop_api_client.sdk import AsyncExecutionResult
1212

1313

1414
class TestAsyncExecutionResult:

tests/sdk/test_async_ops.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,19 +24,17 @@
2424
AsyncAgent,
2525
AsyncDevbox,
2626
AsyncScorer,
27+
AsyncAgentOps,
2728
AsyncScenario,
2829
AsyncSnapshot,
2930
AsyncBlueprint,
30-
AsyncStorageObject,
31-
)
32-
from runloop_api_client.sdk.async_ import (
33-
AsyncAgentOps,
3431
AsyncDevboxOps,
3532
AsyncScorerOps,
3633
AsyncRunloopSDK,
3734
AsyncScenarioOps,
3835
AsyncSnapshotOps,
3936
AsyncBlueprintOps,
37+
AsyncStorageObject,
4038
AsyncStorageObjectOps,
4139
)
4240
from runloop_api_client.lib.polling import PollingConfig

tests/sdk/test_async_scenario_builder.py

Lines changed: 82 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,7 @@
66

77
import pytest
88

9-
from runloop_api_client.sdk.async_snapshot import AsyncSnapshot
10-
from runloop_api_client.sdk.async_blueprint import AsyncBlueprint
11-
from runloop_api_client.sdk.async_scenario_builder import AsyncScenarioBuilder
9+
from runloop_api_client.sdk import AsyncSnapshot, AsyncBlueprint, AsyncScenarioBuilder
1210
from runloop_api_client.types.scoring_function_param import ScorerTestBasedScoringFunctionTestFile
1311

1412

@@ -34,7 +32,7 @@ def mock_snapshot(self, mock_async_client: MagicMock) -> AsyncSnapshot:
3432
return AsyncSnapshot(mock_async_client, "snap-123")
3533

3634
@pytest.fixture
37-
def builder(self, mock_async_client: MagicMock) -> AsyncScenarioBuilder:
35+
def mock_builder(self, mock_async_client: MagicMock) -> AsyncScenarioBuilder:
3836
"""Create an AsyncScenarioBuilder instance with mock client."""
3937
return AsyncScenarioBuilder("test-scenario", mock_async_client)
4038

@@ -48,113 +46,117 @@ def test_instantiation(self, mock_async_client: MagicMock) -> None:
4846
assert repr(builder) == "<AsyncScenarioBuilder name='my-scenario'>"
4947

5048
def test_from_blueprint_and_snapshot(
51-
self, builder: AsyncScenarioBuilder, mock_blueprint: AsyncBlueprint, mock_snapshot: AsyncSnapshot
49+
self, mock_builder: AsyncScenarioBuilder, mock_blueprint: AsyncBlueprint, mock_snapshot: AsyncSnapshot
5250
) -> None:
5351
"""Test blueprint/snapshot setting returns self and are mutually exclusive."""
5452
# from_blueprint returns self and sets blueprint
55-
result = builder.from_blueprint(mock_blueprint)
56-
assert result is builder
57-
assert builder._blueprint is mock_blueprint
58-
assert builder._snapshot is None
53+
result = mock_builder.from_blueprint(mock_blueprint)
54+
assert result is mock_builder
55+
assert mock_builder._blueprint is mock_blueprint
56+
assert mock_builder._snapshot is None
5957

6058
# from_snapshot returns self, sets snapshot, and clears blueprint
61-
result = builder.from_snapshot(mock_snapshot)
62-
assert result is builder
63-
assert builder._snapshot is mock_snapshot
64-
assert builder._blueprint is None
59+
result = mock_builder.from_snapshot(mock_snapshot)
60+
assert result is mock_builder
61+
assert mock_builder._snapshot is mock_snapshot
62+
assert mock_builder._blueprint is None
6563

6664
# from_blueprint clears snapshot
67-
builder.from_blueprint(mock_blueprint)
68-
assert builder._blueprint is mock_blueprint
69-
assert builder._snapshot is None
65+
mock_builder.from_blueprint(mock_blueprint)
66+
assert mock_builder._blueprint is mock_blueprint
67+
assert mock_builder._snapshot is None
7068

71-
def test_scorers(self, builder: AsyncScenarioBuilder) -> None:
69+
def test_scorers(self, mock_builder: AsyncScenarioBuilder) -> None:
7270
"""Test all scorer types, optional params, and multiple scorers."""
7371
# Test scorer with test files
7472
test_files: list[ScorerTestBasedScoringFunctionTestFile] = [
7573
{"file_path": "test_main.py", "file_contents": "def test_foo(): pass"}
7674
]
77-
result = builder.add_test_command_scorer(
75+
result = mock_builder.add_test_command_scorer(
7876
"test-scorer", test_command="pytest", weight=2.0, test_files=test_files
7977
)
80-
assert result is builder
81-
assert builder._scorers[0]["name"] == "test-scorer"
82-
assert builder._scorers[0]["weight"] == 2.0
83-
assert builder._scorers[0]["scorer"]["type"] == "test_based_scorer"
84-
assert builder._scorers[0]["scorer"].get("test_command") == "pytest"
85-
assert builder._scorers[0]["scorer"].get("test_files") == test_files
78+
assert result is mock_builder
79+
assert mock_builder._scorers[0]["name"] == "test-scorer"
80+
assert mock_builder._scorers[0]["weight"] == 2.0
81+
assert mock_builder._scorers[0]["scorer"]["type"] == "test_based_scorer"
82+
assert mock_builder._scorers[0]["scorer"].get("test_command") == "pytest"
83+
assert mock_builder._scorers[0]["scorer"].get("test_files") == test_files
8684

8785
# Command scorer
88-
builder.add_shell_command_scorer("cmd-scorer", command="./check.sh")
89-
assert builder._scorers[1]["scorer"]["type"] == "command_scorer"
90-
assert builder._scorers[1]["scorer"].get("command") == "./check.sh"
86+
mock_builder.add_shell_command_scorer("cmd-scorer", command="./check.sh")
87+
assert mock_builder._scorers[1]["scorer"]["type"] == "command_scorer"
88+
assert mock_builder._scorers[1]["scorer"].get("command") == "./check.sh"
9189

9290
# Bash scorer
93-
builder.add_bash_script_scorer("bash-scorer", bash_script="echo 'score=1.0'")
94-
assert builder._scorers[2]["scorer"]["type"] == "bash_script_scorer"
95-
assert builder._scorers[2]["scorer"].get("bash_script") == "echo 'score=1.0'"
91+
mock_builder.add_bash_script_scorer("bash-scorer", bash_script="echo 'score=1.0'")
92+
assert mock_builder._scorers[2]["scorer"]["type"] == "bash_script_scorer"
93+
assert mock_builder._scorers[2]["scorer"].get("bash_script") == "echo 'score=1.0'"
9694

9795
# Python scorer with optional params
98-
builder.add_python_script_scorer(
96+
mock_builder.add_python_script_scorer(
9997
"python-scorer",
10098
python_script="print('1.0')",
10199
python_version_constraint=">=3.10",
102100
requirements_contents="numpy",
103101
)
104-
assert builder._scorers[3]["scorer"]["type"] == "python_script_scorer"
105-
assert builder._scorers[3]["scorer"].get("python_version_constraint") == ">=3.10"
106-
assert builder._scorers[3]["scorer"].get("requirements_contents") == "numpy"
102+
assert mock_builder._scorers[3]["scorer"]["type"] == "python_script_scorer"
103+
assert mock_builder._scorers[3]["scorer"].get("python_version_constraint") == ">=3.10"
104+
assert mock_builder._scorers[3]["scorer"].get("requirements_contents") == "numpy"
107105

108106
# AST grep scorer with optional lang
109-
builder.add_ast_grep_scorer("ast-scorer", pattern="$A.foo()", search_directory="/src", lang="python")
110-
assert builder._scorers[4]["scorer"]["type"] == "ast_grep_scorer"
111-
assert builder._scorers[4]["scorer"].get("pattern") == "$A.foo()"
112-
assert builder._scorers[4]["scorer"].get("lang") == "python"
107+
mock_builder.add_ast_grep_scorer("ast-scorer", pattern="$A.foo()", search_directory="/src", lang="python")
108+
assert mock_builder._scorers[4]["scorer"]["type"] == "ast_grep_scorer"
109+
assert mock_builder._scorers[4]["scorer"].get("pattern") == "$A.foo()"
110+
assert mock_builder._scorers[4]["scorer"].get("lang") == "python"
113111

114112
# Custom scorer with optional params
115-
builder.add_custom_scorer("custom-scorer", custom_scorer_type="my_scorer", scorer_params={"threshold": 0.5})
116-
assert builder._scorers[5]["scorer"]["type"] == "custom_scorer"
117-
assert builder._scorers[5]["scorer"].get("custom_scorer_type") == "my_scorer"
118-
assert builder._scorers[5]["scorer"].get("scorer_params") == {"threshold": 0.5}
113+
mock_builder.add_custom_scorer(
114+
"custom-scorer", custom_scorer_type="my_scorer", scorer_params={"threshold": 0.5}
115+
)
116+
assert mock_builder._scorers[5]["scorer"]["type"] == "custom_scorer"
117+
assert mock_builder._scorers[5]["scorer"].get("custom_scorer_type") == "my_scorer"
118+
assert mock_builder._scorers[5]["scorer"].get("scorer_params") == {"threshold": 0.5}
119119

120120
# Verify multiple scorers accumulated
121-
assert len(builder._scorers) == 6
121+
assert len(mock_builder._scorers) == 6
122122

123-
def test_add_scorer_rejects_invalid_weight(self, builder: AsyncScenarioBuilder) -> None:
123+
def test_add_scorer_rejects_invalid_weight(self, mock_builder: AsyncScenarioBuilder) -> None:
124124
"""Test that adding a scorer with zero or negative weight raises ValueError."""
125125
with pytest.raises(ValueError, match="Scorer weight must be positive"):
126-
builder.add_bash_script_scorer("bad", bash_script="echo 1", weight=0.0)
126+
mock_builder.add_bash_script_scorer("bad", bash_script="echo 1", weight=0.0)
127127

128128
with pytest.raises(ValueError, match="Scorer weight must be positive"):
129-
builder.add_bash_script_scorer("bad", bash_script="echo 1", weight=-1.0)
129+
mock_builder.add_bash_script_scorer("bad", bash_script="echo 1", weight=-1.0)
130130

131-
def test_build_params_validation(self, builder: AsyncScenarioBuilder) -> None:
131+
def test_build_params_validation(self, mock_builder: AsyncScenarioBuilder) -> None:
132132
"""Test _build_params raises for missing required fields."""
133133
# Missing problem statement
134-
builder.add_test_command_scorer("test", test_command="pytest")
134+
mock_builder.add_test_command_scorer("test", test_command="pytest")
135135
with pytest.raises(ValueError, match="Problem statement is required"):
136-
builder._build_params()
136+
mock_builder._build_params()
137137

138138
# Missing scorer (new builder)
139-
builder2 = AsyncScenarioBuilder("test2", builder._client)
139+
builder2 = AsyncScenarioBuilder("test2", mock_builder._client)
140140
builder2.with_problem_statement("Fix the bug")
141141
with pytest.raises(ValueError, match="At least one scorer is required"):
142142
builder2._build_params()
143143

144-
def test_build_params_with_all_options(self, builder: AsyncScenarioBuilder, mock_blueprint: AsyncBlueprint) -> None:
144+
def test_build_params_with_all_options(
145+
self, mock_builder: AsyncScenarioBuilder, mock_blueprint: AsyncBlueprint
146+
) -> None:
145147
"""Test _build_params with all optional fields set."""
146-
builder.with_problem_statement("Fix the bug")
147-
builder.with_additional_context({"hint": "line 42"})
148-
builder.add_test_command_scorer("tests", test_command="pytest")
149-
builder.from_blueprint(mock_blueprint)
150-
builder.with_working_directory("/app")
151-
builder.with_metadata({"team": "infra"})
152-
builder.with_reference_output("diff content")
153-
builder.with_required_env_vars(["API_KEY"])
154-
builder.with_required_secrets(["db_pass"])
155-
builder.with_validation_type("FORWARD")
156-
157-
params = builder._build_params()
148+
mock_builder.with_problem_statement("Fix the bug")
149+
mock_builder.with_additional_context({"hint": "line 42"})
150+
mock_builder.add_test_command_scorer("tests", test_command="pytest")
151+
mock_builder.from_blueprint(mock_blueprint)
152+
mock_builder.with_working_directory("/app")
153+
mock_builder.with_metadata({"team": "infra"})
154+
mock_builder.with_reference_output("diff content")
155+
mock_builder.with_required_env_vars(["API_KEY"])
156+
mock_builder.with_required_secrets(["db_pass"])
157+
mock_builder.with_validation_type("FORWARD")
158+
159+
params = mock_builder._build_params()
158160

159161
assert params["name"] == "test-scenario"
160162
assert params["input_context"]["problem_statement"] == "Fix the bug"
@@ -169,14 +171,14 @@ def test_build_params_with_all_options(self, builder: AsyncScenarioBuilder, mock
169171
assert params.get("required_secret_names") == ["db_pass"]
170172
assert params.get("validation_type") == "FORWARD"
171173

172-
def test_build_params_normalizes_weights(self, builder: AsyncScenarioBuilder) -> None:
174+
def test_build_params_normalizes_weights(self, mock_builder: AsyncScenarioBuilder) -> None:
173175
"""Test that _build_params normalizes scorer weights to sum to 1.0."""
174-
builder.with_problem_statement("Fix the bug")
175-
builder.add_bash_script_scorer("scorer1", bash_script="echo 1", weight=1.0)
176-
builder.add_bash_script_scorer("scorer2", bash_script="echo 2", weight=2.0)
177-
builder.add_bash_script_scorer("scorer3", bash_script="echo 3", weight=3.0)
176+
mock_builder.with_problem_statement("Fix the bug")
177+
mock_builder.add_bash_script_scorer("scorer1", bash_script="echo 1", weight=1.0)
178+
mock_builder.add_bash_script_scorer("scorer2", bash_script="echo 2", weight=2.0)
179+
mock_builder.add_bash_script_scorer("scorer3", bash_script="echo 3", weight=3.0)
178180

179-
params = builder._build_params()
181+
params = mock_builder._build_params()
180182
scorers = list(params["scoring_contract"]["scoring_function_parameters"])
181183

182184
# Weights 1, 2, 3 should normalize to 1/6, 2/6, 3/6
@@ -191,15 +193,15 @@ def test_build_params_normalizes_weights(self, builder: AsyncScenarioBuilder) ->
191193

192194
@pytest.mark.asyncio
193195
async def test_push_calls_api_and_returns_scenario(
194-
self, builder: AsyncScenarioBuilder, mock_async_client: MagicMock
196+
self, mock_builder: AsyncScenarioBuilder, mock_async_client: MagicMock
195197
) -> None:
196198
"""Test push() calls API with correct params and returns AsyncScenario."""
197199
mock_async_client.scenarios.create.return_value.id = "scn-new-123"
198200

199-
builder.with_problem_statement("Fix the bug")
200-
builder.add_test_command_scorer("tests", test_command="pytest")
201+
mock_builder.with_problem_statement("Fix the bug")
202+
mock_builder.add_test_command_scorer("tests", test_command="pytest")
201203

202-
scenario = await builder.push()
204+
scenario = await mock_builder.push()
203205

204206
mock_async_client.scenarios.create.assert_called_once()
205207
call_kwargs = mock_async_client.scenarios.create.call_args.kwargs
@@ -208,10 +210,10 @@ async def test_push_calls_api_and_returns_scenario(
208210

209211
assert scenario.id == "scn-new-123"
210212

211-
def test_fluent_chaining(self, builder: AsyncScenarioBuilder, mock_blueprint: AsyncBlueprint) -> None:
213+
def test_fluent_chaining(self, mock_builder: AsyncScenarioBuilder, mock_blueprint: AsyncBlueprint) -> None:
212214
"""Test that all builder methods can be chained fluently."""
213215
result = (
214-
builder.from_blueprint(mock_blueprint)
216+
mock_builder.from_blueprint(mock_blueprint)
215217
.with_working_directory("/app")
216218
.with_problem_statement("Fix the bug")
217219
.with_additional_context({"hint": "check main.py"})
@@ -223,8 +225,8 @@ def test_fluent_chaining(self, builder: AsyncScenarioBuilder, mock_blueprint: As
223225
.with_validation_type("FORWARD")
224226
)
225227

226-
assert result is builder
227-
assert builder._blueprint is mock_blueprint
228-
assert builder._working_directory == "/app"
229-
assert builder._problem_statement == "Fix the bug"
230-
assert len(builder._scorers) == 1
228+
assert result is mock_builder
229+
assert mock_builder._blueprint is mock_blueprint
230+
assert mock_builder._working_directory == "/app"
231+
assert mock_builder._problem_statement == "Fix the bug"
232+
assert len(mock_builder._scorers) == 1

tests/sdk/test_execution.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
TASK_COMPLETION_SHORT,
1313
MockExecutionView,
1414
)
15-
from runloop_api_client.sdk.execution import Execution, _StreamingGroup
15+
from runloop_api_client.sdk import Execution
16+
from runloop_api_client.sdk.execution import _StreamingGroup
1617

1718
# Legacy aliases for backward compatibility during transition
1819
SHORT_SLEEP = THREAD_STARTUP_DELAY

tests/sdk/test_execution_result.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from unittest.mock import Mock
77

88
from tests.sdk.conftest import MockExecutionView
9-
from runloop_api_client.sdk.execution_result import ExecutionResult
9+
from runloop_api_client.sdk import ExecutionResult
1010

1111

1212
class TestExecutionResult:

tests/sdk/test_ops.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,21 @@
2020
MockBlueprintView,
2121
create_mock_httpx_response,
2222
)
23-
from runloop_api_client.sdk import Agent, Devbox, Scorer, Scenario, Snapshot, Blueprint, StorageObject
24-
from runloop_api_client.sdk.sync import (
23+
from runloop_api_client.sdk import (
24+
Agent,
25+
Devbox,
26+
Scorer,
2527
AgentOps,
28+
Scenario,
29+
Snapshot,
30+
Blueprint,
2631
DevboxOps,
2732
ScorerOps,
2833
RunloopSDK,
2934
ScenarioOps,
3035
SnapshotOps,
3136
BlueprintOps,
37+
StorageObject,
3238
StorageObjectOps,
3339
)
3440
from runloop_api_client.lib.polling import PollingConfig

0 commit comments

Comments
 (0)