build(features/steps): fix 14 files to match new ruff format
CI / lint (pull_request) Failing after 16s
CI / typecheck (pull_request) Successful in 27s
CI / coverage (pull_request) Has been skipped
CI / security (pull_request) Successful in 19s
CI / quality (pull_request) Failing after 15s
CI / behave (3.11) (pull_request) Failing after 11s
CI / behave (3.12) (pull_request) Failing after 10s
CI / behave (3.13) (pull_request) Failing after 14s
CI / docker (pull_request) Has been skipped
CI / helm (pull_request) Has been skipped
CI / build (pull_request) Failing after 13s
CI / lint (pull_request) Failing after 16s
CI / typecheck (pull_request) Successful in 27s
CI / coverage (pull_request) Has been skipped
CI / security (pull_request) Successful in 19s
CI / quality (pull_request) Failing after 15s
CI / behave (3.11) (pull_request) Failing after 11s
CI / behave (3.12) (pull_request) Failing after 10s
CI / behave (3.13) (pull_request) Failing after 14s
CI / docker (pull_request) Has been skipped
CI / helm (pull_request) Has been skipped
CI / build (pull_request) Failing after 13s
This commit is contained in:
@@ -26,9 +26,9 @@ def step_check_adr_files(context):
|
||||
@then("I should find at least {count:d} ADR files")
|
||||
def step_verify_adr_count(context, count):
|
||||
"""Verify minimum number of ADR files."""
|
||||
assert (
|
||||
len(context.adr_files) >= count
|
||||
), f"Expected at least {count} ADR files, found {len(context.adr_files)}"
|
||||
assert len(context.adr_files) >= count, (
|
||||
f"Expected at least {count} ADR files, found {len(context.adr_files)}"
|
||||
)
|
||||
|
||||
|
||||
@then("each ADR should have a valid format with status")
|
||||
@@ -40,9 +40,9 @@ def step_verify_adr_format(context):
|
||||
assert "## Status" in content, f"{adr_file.name} missing Status section"
|
||||
assert "## Context" in content, f"{adr_file.name} missing Context section"
|
||||
assert "## Decision" in content, f"{adr_file.name} missing Decision section"
|
||||
assert (
|
||||
"## Consequences" in content
|
||||
), f"{adr_file.name} missing Consequences section"
|
||||
assert "## Consequences" in content, (
|
||||
f"{adr_file.name} missing Consequences section"
|
||||
)
|
||||
|
||||
|
||||
@given('the source directory exists at "{path}"')
|
||||
|
||||
@@ -202,9 +202,9 @@ def step_assert_attempts(context, count):
|
||||
@then('a CleverAgentsError should be raised with message "{message}"')
|
||||
def step_assert_cleveragents_error(context, message):
|
||||
exception = _get_recorded_exception(context)
|
||||
assert (
|
||||
exception is not None
|
||||
), "Expected CleverAgentsError but no exception was recorded"
|
||||
assert exception is not None, (
|
||||
"Expected CleverAgentsError but no exception was recorded"
|
||||
)
|
||||
assert isinstance(exception, CleverAgentsError)
|
||||
assert message in str(exception)
|
||||
|
||||
@@ -307,9 +307,9 @@ def step_assert_exit_code(context, code):
|
||||
actual = _capture_exit_code(context)
|
||||
if actual != code:
|
||||
output = _capture_output(context)
|
||||
assert (
|
||||
actual == code
|
||||
), f"Expected exit code {code}, got {actual}. CLI output:\n{output}"
|
||||
assert actual == code, (
|
||||
f"Expected exit code {code}, got {actual}. CLI output:\n{output}"
|
||||
)
|
||||
|
||||
|
||||
@then("the command should be aborted")
|
||||
|
||||
@@ -375,9 +375,9 @@ def step_file_in_context(context):
|
||||
@then('I should see "{text}" in the output')
|
||||
def step_see_text_in_output(context, text):
|
||||
"""Verify text appears in command output."""
|
||||
assert (
|
||||
text in context.command_output
|
||||
), f"Expected '{text}' in output: {context.command_output}"
|
||||
assert text in context.command_output, (
|
||||
f"Expected '{text}' in output: {context.command_output}"
|
||||
)
|
||||
|
||||
|
||||
@then("a new plan should be created")
|
||||
@@ -517,9 +517,9 @@ def step_error_mentions(context, text):
|
||||
"""Verify error message contains text."""
|
||||
# Check both stdout and stderr for error messages
|
||||
combined_output = (context.command_output or "") + (context.command_error or "")
|
||||
assert (
|
||||
text.lower() in combined_output.lower()
|
||||
), f"Expected '{text}' not found in: {combined_output}"
|
||||
assert text.lower() in combined_output.lower(), (
|
||||
f"Expected '{text}' not found in: {combined_output}"
|
||||
)
|
||||
|
||||
|
||||
@then('the output should mention "{text}"')
|
||||
|
||||
@@ -418,9 +418,9 @@ def step_check_final_content(context: Context) -> None:
|
||||
|
||||
@then("{count:d} changes should be marked as applied")
|
||||
def step_check_applied_count(context: Context, count: int) -> None:
|
||||
assert (
|
||||
context.edge_applied_count == count
|
||||
), f"Expected {count} applied, got {context.edge_applied_count}"
|
||||
assert context.edge_applied_count == count, (
|
||||
f"Expected {count} applied, got {context.edge_applied_count}"
|
||||
)
|
||||
|
||||
|
||||
@given("the plan has a CREATE change with None content")
|
||||
@@ -626,13 +626,13 @@ def step_add_move_traversal_new_path(context: Context) -> None:
|
||||
@then("a PlanError should be raised mentioning path traversal")
|
||||
def step_check_path_traversal_error(context: Context) -> None:
|
||||
assert context.edge_exception is not None, "Expected a PlanError"
|
||||
assert isinstance(
|
||||
context.edge_exception, PlanError
|
||||
), f"Expected PlanError, got {type(context.edge_exception).__name__}"
|
||||
assert isinstance(context.edge_exception, PlanError), (
|
||||
f"Expected PlanError, got {type(context.edge_exception).__name__}"
|
||||
)
|
||||
msg = str(context.edge_exception.message).lower()
|
||||
assert (
|
||||
"path traversal" in msg
|
||||
), f"Expected 'path traversal' in error message, got: {context.edge_exception.message}"
|
||||
assert "path traversal" in msg, (
|
||||
f"Expected 'path traversal' in error message, got: {context.edge_exception.message}"
|
||||
)
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────
|
||||
@@ -850,9 +850,9 @@ def step_check_second_file_unchanged(context: Context) -> None:
|
||||
with contextlib.suppress(Exception):
|
||||
os.chmod(readonly, 0o644)
|
||||
content = readonly.read_text()
|
||||
assert (
|
||||
content == "# original fail target content"
|
||||
), f"Expected original content, got: {content}"
|
||||
assert content == "# original fail target content", (
|
||||
f"Expected original content, got: {content}"
|
||||
)
|
||||
|
||||
|
||||
@then("the plan cannot be restarted from errored state")
|
||||
@@ -868,9 +868,9 @@ def step_plan_cannot_restart_errored(context: Context) -> None:
|
||||
|
||||
@then('the plan phase should remain "{expected_phase}"')
|
||||
def step_plan_phase_remains(context: Context, expected_phase: str) -> None:
|
||||
assert (
|
||||
context.plan.phase.value == expected_phase
|
||||
), f"Expected phase '{expected_phase}', got '{context.plan.phase.value}'"
|
||||
assert context.plan.phase.value == expected_phase, (
|
||||
f"Expected phase '{expected_phase}', got '{context.plan.phase.value}'"
|
||||
)
|
||||
|
||||
|
||||
@when("I try to execute the errored plan")
|
||||
@@ -884,9 +884,9 @@ def step_try_execute_errored(context: Context) -> None:
|
||||
|
||||
@then("a plan not ready error should be raised for the errored plan")
|
||||
def step_check_errored_exec_error(context: Context) -> None:
|
||||
assert (
|
||||
context.errored_exec_error is not None
|
||||
), "Expected PlanNotReadyError for errored plan"
|
||||
assert context.errored_exec_error is not None, (
|
||||
"Expected PlanNotReadyError for errored plan"
|
||||
)
|
||||
assert isinstance(context.errored_exec_error, PlanNotReadyError)
|
||||
|
||||
|
||||
@@ -901,7 +901,7 @@ def step_try_complete_strategize_after_failure(context: Context) -> None:
|
||||
|
||||
@then("a plan error should be raised because plan is not processing")
|
||||
def step_check_post_fail_complete_error(context: Context) -> None:
|
||||
assert (
|
||||
context.post_fail_complete_error is not None
|
||||
), "Expected PlanError when completing after failure"
|
||||
assert context.post_fail_complete_error is not None, (
|
||||
"Expected PlanError when completing after failure"
|
||||
)
|
||||
assert isinstance(context.post_fail_complete_error, PlanError)
|
||||
|
||||
@@ -180,9 +180,9 @@ def step_convert_model_provider_to_string(context, member):
|
||||
@then("it should be a string type")
|
||||
def step_verify_string_type(context):
|
||||
"""Verify the value is a string."""
|
||||
assert isinstance(
|
||||
context.string_value, str
|
||||
), f"Not a string: {type(context.string_value)}"
|
||||
assert isinstance(context.string_value, str), (
|
||||
f"Not a string: {type(context.string_value)}"
|
||||
)
|
||||
|
||||
|
||||
@then('it should equal "{expected}"')
|
||||
@@ -240,18 +240,18 @@ def step_iterate_enum_values(context, enum_name):
|
||||
@then("I should get {count:d} enum members")
|
||||
def step_verify_enum_count(context, count):
|
||||
"""Verify the number of enum members."""
|
||||
assert (
|
||||
context.member_count == count
|
||||
), f"Expected {count} members, got {context.member_count}"
|
||||
assert context.member_count == count, (
|
||||
f"Expected {count} members, got {context.member_count}"
|
||||
)
|
||||
|
||||
|
||||
@then("each member should be a {enum_name} instance")
|
||||
def step_verify_enum_instances(context, enum_name):
|
||||
"""Verify each member is an instance of the enum."""
|
||||
for member in context.enum_members:
|
||||
assert isinstance(
|
||||
member, context.current_enum
|
||||
), f"{member} is not a {enum_name} instance"
|
||||
assert isinstance(member, context.current_enum), (
|
||||
f"{member} is not a {enum_name} instance"
|
||||
)
|
||||
|
||||
|
||||
# Enum comparison tests
|
||||
@@ -296,9 +296,9 @@ def step_access_enum_name(context, member):
|
||||
@then('the name should be "{expected}"')
|
||||
def step_verify_enum_name(context, expected):
|
||||
"""Verify the enum name attribute."""
|
||||
assert (
|
||||
context.enum_name == expected
|
||||
), f"Expected name {expected}, got {context.enum_name}"
|
||||
assert context.enum_name == expected, (
|
||||
f"Expected name {expected}, got {context.enum_name}"
|
||||
)
|
||||
|
||||
|
||||
@when("I access the value attribute of ModelPublisher.{member}")
|
||||
@@ -311,9 +311,9 @@ def step_access_enum_value(context, member):
|
||||
@then('the value should be "{expected}"')
|
||||
def step_verify_enum_value_attr(context, expected):
|
||||
"""Verify the enum value attribute."""
|
||||
assert (
|
||||
context.enum_value_attr == expected
|
||||
), f"Expected value {expected}, got {context.enum_value_attr}"
|
||||
assert context.enum_value_attr == expected, (
|
||||
f"Expected value {expected}, got {context.enum_value_attr}"
|
||||
)
|
||||
|
||||
|
||||
# Creating enum from value
|
||||
@@ -334,9 +334,9 @@ def step_verify_created_enum(context, member):
|
||||
"""Verify the created enum member."""
|
||||
expected = getattr(ModelProvider, member)
|
||||
assert context.creation_successful, "Enum creation failed"
|
||||
assert (
|
||||
context.created_enum == expected
|
||||
), f"Expected {expected}, got {context.created_enum}"
|
||||
assert context.created_enum == expected, (
|
||||
f"Expected {expected}, got {context.created_enum}"
|
||||
)
|
||||
assert context.created_enum is expected, "Enum instances should be identical"
|
||||
|
||||
|
||||
@@ -395,15 +395,15 @@ def step_get_string_representation(context):
|
||||
@then('repr should contain "{expected}"')
|
||||
def step_verify_repr_contains(context, expected):
|
||||
"""Verify repr contains expected string."""
|
||||
assert (
|
||||
expected in context.repr_value
|
||||
), f"'{expected}' not in repr: {context.repr_value}"
|
||||
assert expected in context.repr_value, (
|
||||
f"'{expected}' not in repr: {context.repr_value}"
|
||||
)
|
||||
|
||||
|
||||
@then('str should equal "{expected}"')
|
||||
def step_verify_str_equals(context, expected):
|
||||
"""Verify str equals expected."""
|
||||
# For str, Enum classes, str() returns the value, not the name
|
||||
assert (
|
||||
context.enum_value.value == expected
|
||||
), f"Expected str '{expected}', got '{context.enum_value.value}'"
|
||||
assert context.enum_value.value == expected, (
|
||||
f"Expected str '{expected}', got '{context.enum_value.value}'"
|
||||
)
|
||||
|
||||
@@ -160,9 +160,9 @@ def step_assert_google_kwargs(context, kwargs_string):
|
||||
expected = _parse_kwargs_string(kwargs_string)
|
||||
for key, value in expected.items():
|
||||
assert key in call_kwargs, f"Expected {key} in ChatGoogleGenerativeAI kwargs"
|
||||
assert (
|
||||
call_kwargs[key] == value
|
||||
), f"Expected ChatGoogleGenerativeAI kwargs[{key!r}] to equal {value!r}"
|
||||
assert call_kwargs[key] == value, (
|
||||
f"Expected ChatGoogleGenerativeAI kwargs[{key!r}] to equal {value!r}"
|
||||
)
|
||||
|
||||
|
||||
@then(
|
||||
@@ -184,9 +184,9 @@ def step_assert_google_metadata(context, expected_name, expected_model):
|
||||
def step_assert_google_generated_change(context, count, file_path):
|
||||
response = getattr(context, "response", None)
|
||||
assert response is not None, "Provider response should exist"
|
||||
assert (
|
||||
len(response.changes) == count
|
||||
), f"Expected {count} changes, got {len(response.changes)}"
|
||||
assert len(response.changes) == count, (
|
||||
f"Expected {count} changes, got {len(response.changes)}"
|
||||
)
|
||||
assert any(
|
||||
getattr(change, "file_path", None) == file_path for change in response.changes
|
||||
), f"Expected change for {file_path}"
|
||||
|
||||
@@ -643,9 +643,9 @@ def step_assert_response_contains_validation_error(context):
|
||||
assert context.response is not None
|
||||
assert context.response.changes, "Expected generated changes to be returned"
|
||||
assert context.response.changes[0].file_path == context.expected_change.file_path
|
||||
assert (
|
||||
context.response.error_message == context.expected_validation_message
|
||||
), "Validation failure message should be surfaced"
|
||||
assert context.response.error_message == context.expected_validation_message, (
|
||||
"Validation failure message should be surfaced"
|
||||
)
|
||||
assert context.response.model_used == "test-model"
|
||||
assert context.response.token_count == 0
|
||||
assert context.requested_models == ["test-model"]
|
||||
@@ -804,23 +804,23 @@ def step_assert_graph_failure_response(context):
|
||||
@then("the provider response should capture the nested retry failure")
|
||||
def step_assert_nested_retry_failure(context):
|
||||
assert context.response is not None, "Expected provider response to be populated"
|
||||
assert (
|
||||
context.response.changes == []
|
||||
), f"Expected no changes for nested failure, got {context.response.changes!r}"
|
||||
assert (
|
||||
context.response.error_message == context.failure_message
|
||||
), f"Expected error message {context.failure_message!r}, got {context.response.error_message!r}"
|
||||
assert context.response.changes == [], (
|
||||
f"Expected no changes for nested failure, got {context.response.changes!r}"
|
||||
)
|
||||
assert context.response.error_message == context.failure_message, (
|
||||
f"Expected error message {context.failure_message!r}, got {context.response.error_message!r}"
|
||||
)
|
||||
|
||||
|
||||
@then("the progress callback should end at 100 percent even on failure")
|
||||
def step_assert_failure_progress_updates(context):
|
||||
assert context.progress_updates, "Expected progress updates to be recorded"
|
||||
assert (
|
||||
context.progress_updates[0] == 5
|
||||
), f"Expected first progress update to be 5, got {context.progress_updates[0]!r}"
|
||||
assert (
|
||||
context.progress_updates[-1] == 100
|
||||
), f"Expected final progress update to be 100, got {context.progress_updates[-1]!r}"
|
||||
assert context.progress_updates[0] == 5, (
|
||||
f"Expected first progress update to be 5, got {context.progress_updates[0]!r}"
|
||||
)
|
||||
assert context.progress_updates[-1] == 100, (
|
||||
f"Expected final progress update to be 100, got {context.progress_updates[-1]!r}"
|
||||
)
|
||||
|
||||
|
||||
@then("the stream API should preserve non-dict payloads in events")
|
||||
|
||||
@@ -234,9 +234,9 @@ def step_assert_chat_openai_constructor(context, api_key, model_id):
|
||||
assert call_kwargs["api_key"] == api_key
|
||||
assert call_kwargs["model"] == model_id
|
||||
|
||||
assert (
|
||||
context.plan_generation_graph_call is not None
|
||||
), "PlanGenerationGraph should receive the ChatOpenAI instance"
|
||||
assert context.plan_generation_graph_call is not None, (
|
||||
"PlanGenerationGraph should receive the ChatOpenAI instance"
|
||||
)
|
||||
_, graph_kwargs = context.plan_generation_graph_call
|
||||
assert graph_kwargs["llm"] is context.chat_openai_instance
|
||||
|
||||
@@ -262,9 +262,9 @@ def step_assert_chat_openai_kwargs(context, kwargs_string):
|
||||
expected_kwargs = _parse_kwargs_string(kwargs_string)
|
||||
for key, value in expected_kwargs.items():
|
||||
assert key in call_kwargs, f"Expected {key} in ChatOpenAI kwargs"
|
||||
assert (
|
||||
call_kwargs[key] == value
|
||||
), f"Expected ChatOpenAI kwargs[{key!r}] to equal {value!r}"
|
||||
assert call_kwargs[key] == value, (
|
||||
f"Expected ChatOpenAI kwargs[{key!r}] to equal {value!r}"
|
||||
)
|
||||
|
||||
|
||||
@then(
|
||||
@@ -298,9 +298,9 @@ def step_assert_no_changes(context):
|
||||
)
|
||||
def step_assert_generated_change(context, count, file_path):
|
||||
assert context.response is not None, "Provider response should exist"
|
||||
assert (
|
||||
len(context.response.changes) == count
|
||||
), f"Expected {count} changes, got {len(context.response.changes)}"
|
||||
assert len(context.response.changes) == count, (
|
||||
f"Expected {count} changes, got {len(context.response.changes)}"
|
||||
)
|
||||
assert any(
|
||||
getattr(change, "file_path", None) == file_path
|
||||
for change in context.response.changes
|
||||
|
||||
@@ -210,9 +210,9 @@ def step_assert_openrouter_kwargs(context, kwargs_string):
|
||||
expected = _parse_kwargs_string(kwargs_string)
|
||||
for key, value in expected.items():
|
||||
assert key in call_kwargs, f"Expected {key} in ChatOpenAI kwargs"
|
||||
assert (
|
||||
call_kwargs[key] == value
|
||||
), f"Expected ChatOpenAI kwargs[{key!r}] to equal {value!r}"
|
||||
assert call_kwargs[key] == value, (
|
||||
f"Expected ChatOpenAI kwargs[{key!r}] to equal {value!r}"
|
||||
)
|
||||
|
||||
|
||||
@then(
|
||||
@@ -224,9 +224,9 @@ def step_assert_openrouter_headers(context, headers_string):
|
||||
_, call_kwargs = call
|
||||
expected = _parse_headers_string(headers_string)
|
||||
actual_headers = call_kwargs.get("default_headers")
|
||||
assert (
|
||||
actual_headers == expected
|
||||
), f"Expected default_headers {expected!r}, got {actual_headers!r}"
|
||||
assert actual_headers == expected, (
|
||||
f"Expected default_headers {expected!r}, got {actual_headers!r}"
|
||||
)
|
||||
|
||||
|
||||
@then(
|
||||
@@ -235,9 +235,9 @@ def step_assert_openrouter_headers(context, headers_string):
|
||||
def step_assert_openrouter_generated_change(context, count, file_path):
|
||||
response = getattr(context, "response", None)
|
||||
assert response is not None, "Provider response should exist"
|
||||
assert (
|
||||
len(response.changes) == count
|
||||
), f"Expected {count} changes, got {len(response.changes)}"
|
||||
assert len(response.changes) == count, (
|
||||
f"Expected {count} changes, got {len(response.changes)}"
|
||||
)
|
||||
assert any(
|
||||
getattr(change, "file_path", None) == file_path for change in response.changes
|
||||
), f"Expected change for {file_path}"
|
||||
|
||||
@@ -1128,9 +1128,9 @@ def step_assert_typer_abort(context):
|
||||
@then("the plan tell should execute successfully")
|
||||
def step_plan_tell_success(context):
|
||||
"""Assert plan tell executed successfully."""
|
||||
assert (
|
||||
context.result.exit_code == 0
|
||||
), f"Exit code was {context.result.exit_code}, output: {context.result.output}"
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Exit code was {context.result.exit_code}, output: {context.result.output}"
|
||||
)
|
||||
assert "Plan created" in context.result.output or "✓" in context.result.output
|
||||
|
||||
|
||||
@@ -1176,12 +1176,12 @@ def step_plan_tell_general_abort(context):
|
||||
@then("the plan tell streaming path should execute successfully")
|
||||
def step_plan_tell_streaming_success(context):
|
||||
"""Assert plan tell streaming path executed via asyncio."""
|
||||
assert (
|
||||
context.result.exit_code == 0
|
||||
), f"Exit code was {context.result.exit_code}, output: {context.result.output}"
|
||||
assert (
|
||||
getattr(context, "stream_call_count", 0) == 1
|
||||
), "Streaming coroutine was not awaited"
|
||||
assert context.result.exit_code == 0, (
|
||||
f"Exit code was {context.result.exit_code}, output: {context.result.output}"
|
||||
)
|
||||
assert getattr(context, "stream_call_count", 0) == 1, (
|
||||
"Streaming coroutine was not awaited"
|
||||
)
|
||||
assert context.stream_call_args, "No streaming call arguments recorded"
|
||||
first_call = context.stream_call_args[0]
|
||||
assert first_call.args[1] == context.stream_prompt
|
||||
|
||||
@@ -1046,9 +1046,9 @@ def step_assert_lazy_registry_actor(
|
||||
registry = getattr(context, "lazy_registry", None)
|
||||
assert registry is not None, "Lazy registry stub was not configured"
|
||||
expected = {"provider": provider.lower(), "model": model.lower()}
|
||||
assert registry.requests == [
|
||||
expected
|
||||
], "Lazy registry did not capture the actor request"
|
||||
assert registry.requests == [expected], (
|
||||
"Lazy registry did not capture the actor request"
|
||||
)
|
||||
|
||||
resolution = getattr(context, "provider_resolution", None)
|
||||
assert resolution is not None, "Provider resolution result missing"
|
||||
@@ -1314,9 +1314,9 @@ def step_assert_stream_token_count(context: Context, expected: int) -> None:
|
||||
with context.unit_of_work.transaction() as ctx:
|
||||
plan = ctx.plans.get_current_for_project(context.test_project.id)
|
||||
assert plan is not None, "Plan was not persisted"
|
||||
assert (
|
||||
plan.token_count == expected
|
||||
), f"Expected token count {expected}, got {plan.token_count}"
|
||||
assert plan.token_count == expected, (
|
||||
f"Expected token count {expected}, got {plan.token_count}"
|
||||
)
|
||||
|
||||
|
||||
@then('the streaming failure events should include an error with message "{message}"')
|
||||
@@ -1333,9 +1333,9 @@ def step_assert_stream_failure_event(context: Context, message: str) -> None:
|
||||
failure_payload = payload
|
||||
break
|
||||
assert failure_payload is not None, "Expected a failure event in the stream"
|
||||
assert (
|
||||
failure_payload.get("error") == message
|
||||
), f"Expected failure message '{message}', got '{failure_payload.get('error')}'"
|
||||
assert failure_payload.get("error") == message, (
|
||||
f"Expected failure message '{message}', got '{failure_payload.get('error')}'"
|
||||
)
|
||||
|
||||
|
||||
@then(
|
||||
@@ -1347,9 +1347,9 @@ def step_assert_non_python_stream(context: Context) -> None:
|
||||
events = getattr(context, "stream_events", [])
|
||||
assert events, "Streaming events were not captured"
|
||||
end_event = events[-1]
|
||||
assert (
|
||||
isinstance(end_event, dict) and "__end__" in end_event
|
||||
), "Final streaming event did not include end payload"
|
||||
assert isinstance(end_event, dict) and "__end__" in end_event, (
|
||||
"Final streaming event did not include end payload"
|
||||
)
|
||||
payload = end_event["__end__"]
|
||||
changes = payload.get("changes") or []
|
||||
assert len(changes) == 1, "Expected exactly one streamed change"
|
||||
@@ -1395,12 +1395,12 @@ def step_assert_stub_provider_requests(
|
||||
requests = getattr(context, "stub_provider_requests", None)
|
||||
assert requests, "No provider override requests were recorded"
|
||||
last_request = requests[-1]
|
||||
assert (
|
||||
last_request["provider"] == provider
|
||||
), f"Expected provider '{provider}', got '{last_request['provider']}'"
|
||||
assert (
|
||||
last_request["model"] == model
|
||||
), f"Expected model '{model}', got '{last_request['model']}'"
|
||||
assert last_request["provider"] == provider, (
|
||||
f"Expected provider '{provider}', got '{last_request['provider']}'"
|
||||
)
|
||||
assert last_request["model"] == model, (
|
||||
f"Expected model '{model}', got '{last_request['model']}'"
|
||||
)
|
||||
|
||||
|
||||
@given("I have a plan service without providers configured")
|
||||
@@ -1546,9 +1546,9 @@ def step_assert_validation_hint(context: Context) -> None:
|
||||
@given('the project has plans named "{first}" and "{second}"')
|
||||
def step_project_with_named_plans(context: Context, first: str, second: str) -> None:
|
||||
"""Ensure the project has specific plan names configured."""
|
||||
assert (
|
||||
context.project.id is not None
|
||||
), "Project must be persisted before adding plans"
|
||||
assert context.project.id is not None, (
|
||||
"Project must be persisted before adding plans"
|
||||
)
|
||||
with context.unit_of_work.transaction() as ctx:
|
||||
current_plan = ctx.plans.get_current_for_project(context.project.id)
|
||||
assert current_plan is not None, "Current plan is required for this step"
|
||||
@@ -1718,9 +1718,9 @@ def step_try_build_plan_no_current(context: Context) -> None:
|
||||
def step_check_plan_error(context: Context, message: str) -> None:
|
||||
"""Check that a PlanError was raised with specific message."""
|
||||
assert context.exception is not None, "No exception captured"
|
||||
assert isinstance(
|
||||
context.exception, PlanError
|
||||
), f"Unexpected exception type {type(context.exception).__name__}"
|
||||
assert isinstance(context.exception, PlanError), (
|
||||
f"Unexpected exception type {type(context.exception).__name__}"
|
||||
)
|
||||
actual_message = str(getattr(context.exception, "message", "")) or str(
|
||||
context.exception
|
||||
)
|
||||
@@ -1739,9 +1739,9 @@ def step_check_plan_error_contains(context: Context, message: str) -> None:
|
||||
def step_plan_error_details_include_provider_diagnostics(context: Context) -> None:
|
||||
"""Ensure provider diagnostics are present in the captured PlanError details."""
|
||||
assert context.exception is not None, "No exception captured"
|
||||
assert isinstance(
|
||||
context.exception, PlanError
|
||||
), f"Expected PlanError but got {type(context.exception).__name__}"
|
||||
assert isinstance(context.exception, PlanError), (
|
||||
f"Expected PlanError but got {type(context.exception).__name__}"
|
||||
)
|
||||
details = getattr(context.exception, "details", {}) or {}
|
||||
required_keys = {
|
||||
"configured_providers",
|
||||
@@ -1756,9 +1756,9 @@ def step_plan_error_details_include_provider_diagnostics(context: Context) -> No
|
||||
@given("I configured an auto-debug plan with a disappearing plan ID")
|
||||
def step_configure_auto_debug_disappearing_plan(context: Context) -> None:
|
||||
"""Set up a plan service whose current plan loses its ID mid-run."""
|
||||
assert hasattr(
|
||||
context, "temp_dir"
|
||||
), "Temporary directory setup is required before configuring auto-debug"
|
||||
assert hasattr(context, "temp_dir"), (
|
||||
"Temporary directory setup is required before configuring auto-debug"
|
||||
)
|
||||
|
||||
class FlickeringPlan:
|
||||
def __init__(self, plan_id: int) -> None:
|
||||
@@ -2306,18 +2306,18 @@ def step_check_nested_move(context: Context) -> None:
|
||||
old_path, new_path = context.nested_move_paths
|
||||
assert not old_path.exists(), "Original nested file still exists"
|
||||
assert new_path.exists(), "Nested destination file was not created"
|
||||
assert (
|
||||
new_path.read_text() == "# Nested file to move"
|
||||
), "Nested destination content mismatch"
|
||||
assert new_path.read_text() == "# Nested file to move", (
|
||||
"Nested destination content mismatch"
|
||||
)
|
||||
|
||||
|
||||
@given("the plan has a pending MOVE change to an absolute path")
|
||||
def step_add_absolute_move_change(context: Context) -> None:
|
||||
"""Add a MOVE change whose destination is an absolute path."""
|
||||
assert getattr(context, "project", None) is not None, "Project is required"
|
||||
assert (
|
||||
getattr(context, "current_plan", None) is not None
|
||||
), "Current plan is required"
|
||||
assert getattr(context, "current_plan", None) is not None, (
|
||||
"Current plan is required"
|
||||
)
|
||||
|
||||
source_path = context.project.path / "absolute_source.py"
|
||||
source_path.write_text("# Absolute move source")
|
||||
@@ -2440,12 +2440,12 @@ def step_check_plan_error_with_details(context: Context) -> None:
|
||||
with contextlib.suppress(builtins.BaseException):
|
||||
os.chmod(context.readonly_file, 0o644)
|
||||
|
||||
assert (
|
||||
context.exception is not None
|
||||
), f"Expected an exception but got result: {getattr(context, 'apply_result', 'none')}"
|
||||
assert isinstance(
|
||||
context.exception, PlanError
|
||||
), f"Expected PlanError but got {type(context.exception).__name__}: {context.exception}"
|
||||
assert context.exception is not None, (
|
||||
f"Expected an exception but got result: {getattr(context, 'apply_result', 'none')}"
|
||||
)
|
||||
assert isinstance(context.exception, PlanError), (
|
||||
f"Expected PlanError but got {type(context.exception).__name__}: {context.exception}"
|
||||
)
|
||||
assert "Failed to apply change" in str(context.exception.message)
|
||||
|
||||
|
||||
@@ -3295,9 +3295,9 @@ def step_assert_langsmith_builder_base_metadata(
|
||||
assert metadata.get("project_name") == project_name
|
||||
assert metadata.get("project_id") is None
|
||||
extra_metadata_keys = set(metadata.keys()) - {"project_id", "project_name"}
|
||||
assert (
|
||||
not extra_metadata_keys
|
||||
), f"Unexpected metadata keys present: {sorted(extra_metadata_keys)}"
|
||||
assert not extra_metadata_keys, (
|
||||
f"Unexpected metadata keys present: {sorted(extra_metadata_keys)}"
|
||||
)
|
||||
assert tags == ["service:plan"], f"Expected only base service tag, got {tags}"
|
||||
|
||||
|
||||
@@ -3477,9 +3477,9 @@ def step_assert_auto_debug_retry_success(context: Context) -> None:
|
||||
|
||||
attempts = getattr(context, "latest_debug_attempts", []) or []
|
||||
assert attempts, "No debug attempts were recorded"
|
||||
assert any(
|
||||
attempt.success for attempt in attempts
|
||||
), "Debug attempt was not marked successful"
|
||||
assert any(attempt.success for attempt in attempts), (
|
||||
"Debug attempt was not marked successful"
|
||||
)
|
||||
|
||||
|
||||
@when("I try to stream plan generation without an AI provider")
|
||||
|
||||
@@ -673,6 +673,6 @@ def step_project_command_should_succeed(context):
|
||||
@then("the project command should exit with code {code:d}")
|
||||
def step_project_command_exit_code(context, code):
|
||||
"""Assert the project command exited with the expected code."""
|
||||
assert (
|
||||
context.result.exit_code == code
|
||||
), f"Expected exit code {code}, got {context.result.exit_code}. Output: {context.result.output}"
|
||||
assert context.result.exit_code == code, (
|
||||
f"Expected exit code {code}, got {context.result.exit_code}. Output: {context.result.output}"
|
||||
)
|
||||
|
||||
@@ -121,27 +121,27 @@ def step_apply_async_exponential_retry(context, max_attempts):
|
||||
@then("the function should eventually succeed")
|
||||
def step_verify_function_succeeded(context):
|
||||
"""Verify function succeeded after retries."""
|
||||
assert (
|
||||
context.retry_succeeded
|
||||
), f"Function failed: {getattr(context, 'retry_error', 'Unknown error')}"
|
||||
assert context.retry_succeeded, (
|
||||
f"Function failed: {getattr(context, 'retry_error', 'Unknown error')}"
|
||||
)
|
||||
assert context.result == "success"
|
||||
|
||||
|
||||
@then("the async function should eventually succeed")
|
||||
def step_verify_async_function_succeeded(context):
|
||||
"""Verify async function succeeded after retries."""
|
||||
assert (
|
||||
context.retry_succeeded
|
||||
), f"Async function failed: {getattr(context, 'retry_error', 'Unknown error')}"
|
||||
assert context.retry_succeeded, (
|
||||
f"Async function failed: {getattr(context, 'retry_error', 'Unknown error')}"
|
||||
)
|
||||
assert context.result == "async success"
|
||||
|
||||
|
||||
@then("the function should be called {expected_calls:d} times")
|
||||
def step_verify_call_count(context, expected_calls):
|
||||
"""Verify function was called expected number of times."""
|
||||
assert (
|
||||
context.call_count == expected_calls
|
||||
), f"Expected {expected_calls} calls, got {context.call_count}"
|
||||
assert context.call_count == expected_calls, (
|
||||
f"Expected {expected_calls} calls, got {context.call_count}"
|
||||
)
|
||||
|
||||
|
||||
# Network retry pattern tests
|
||||
@@ -182,9 +182,9 @@ def step_verify_network_retry(context):
|
||||
@then("the function should be called {max_calls:d} times maximum")
|
||||
def step_verify_max_calls(context, max_calls):
|
||||
"""Verify function wasn't called more than max times."""
|
||||
assert (
|
||||
context.call_count <= max_calls
|
||||
), f"Function called {context.call_count} times, max is {max_calls}"
|
||||
assert context.call_count <= max_calls, (
|
||||
f"Function called {context.call_count} times, max is {max_calls}"
|
||||
)
|
||||
|
||||
|
||||
# Provider retry pattern tests
|
||||
@@ -261,18 +261,18 @@ def step_apply_file_retry(context):
|
||||
@then("the file operation should eventually succeed")
|
||||
def step_verify_file_retry_success(context):
|
||||
"""Verify the file operation eventually succeeds."""
|
||||
assert (
|
||||
context.file_retry_succeeded
|
||||
), f"File retry failed: {getattr(context, 'file_retry_error', 'Unknown error')}"
|
||||
assert context.file_retry_succeeded, (
|
||||
f"File retry failed: {getattr(context, 'file_retry_error', 'Unknown error')}"
|
||||
)
|
||||
assert context.file_result == "file success"
|
||||
|
||||
|
||||
@then("the file operation should be invoked {expected:d} times")
|
||||
def step_verify_file_retry_invocations(context, expected):
|
||||
"""Verify the file operation was invoked the expected number of times."""
|
||||
assert (
|
||||
context.file_call_count == expected
|
||||
), f"Expected {expected} calls, got {context.file_call_count}"
|
||||
assert context.file_call_count == expected, (
|
||||
f"Expected {expected} calls, got {context.file_call_count}"
|
||||
)
|
||||
|
||||
|
||||
# Circuit breaker tests
|
||||
@@ -496,9 +496,9 @@ def step_async_retry_context_manager_failure(context):
|
||||
@then("the async retry context manager should capture the exception")
|
||||
def step_verify_async_retry_context_manager_capture(context):
|
||||
"""Ensure the async retry context manager captured the exception."""
|
||||
assert (
|
||||
context.async_retry_context_manager_error
|
||||
), "Expected async runtime error to propagate"
|
||||
assert context.async_retry_context_manager_error, (
|
||||
"Expected async runtime error to propagate"
|
||||
)
|
||||
assert context.retry_context_errors_added_async >= 1
|
||||
assert isinstance(context.retry_context.errors[-1], Exception)
|
||||
|
||||
@@ -690,9 +690,9 @@ def step_apply_retry_on_result(context, max_attempts):
|
||||
@then("the decorated function should return the successful payload")
|
||||
def step_verify_retry_on_result_payload(context):
|
||||
"""Ensure retry_on_result eventually returns the non-retry payload."""
|
||||
assert (
|
||||
context.retry_on_result_succeeded
|
||||
), f"Retry on result failed: {context.retry_on_result_output}"
|
||||
assert context.retry_on_result_succeeded, (
|
||||
f"Retry on result failed: {context.retry_on_result_output}"
|
||||
)
|
||||
assert isinstance(context.retry_on_result_output, dict)
|
||||
assert context.retry_on_result_output.get("retry") is False
|
||||
|
||||
|
||||
@@ -49,9 +49,9 @@ def step_validate_code_ast(context: Context, code: str) -> None:
|
||||
def step_check_stream_routing_error(context: Context, text: str) -> None:
|
||||
assert context.ast_error is not None, "Expected StreamRoutingError"
|
||||
assert isinstance(context.ast_error, StreamRoutingError)
|
||||
assert text in str(
|
||||
context.ast_error
|
||||
), f"Expected '{text}' in error: {context.ast_error}"
|
||||
assert text in str(context.ast_error), (
|
||||
f"Expected '{text}' in error: {context.ast_error}"
|
||||
)
|
||||
|
||||
|
||||
@then("no error should be raised from ast validation")
|
||||
@@ -159,16 +159,16 @@ def step_sanitize_fenced_content(context: Context, inner: str) -> None:
|
||||
|
||||
@then('the sanitized content should be "{expected}"')
|
||||
def step_check_sanitized_content(context: Context, expected: str) -> None:
|
||||
assert (
|
||||
context.sanitized_content == expected
|
||||
), f"Expected '{expected}', got '{context.sanitized_content}'"
|
||||
assert context.sanitized_content == expected, (
|
||||
f"Expected '{expected}', got '{context.sanitized_content}'"
|
||||
)
|
||||
|
||||
|
||||
@then("the sanitization error should be None")
|
||||
def step_check_sanitized_error_none(context: Context) -> None:
|
||||
assert (
|
||||
context.sanitized_error is None
|
||||
), f"Expected no error, got: {context.sanitized_error}"
|
||||
assert context.sanitized_error is None, (
|
||||
f"Expected no error, got: {context.sanitized_error}"
|
||||
)
|
||||
|
||||
|
||||
@then("the sanitization error should not be None")
|
||||
@@ -178,16 +178,16 @@ def step_check_sanitized_error_not_none(context: Context) -> None:
|
||||
|
||||
@then("the sanitization reason should be None")
|
||||
def step_check_sanitized_reason_none(context: Context) -> None:
|
||||
assert (
|
||||
context.sanitized_reason is None
|
||||
), f"Expected no reason, got: {context.sanitized_reason}"
|
||||
assert context.sanitized_reason is None, (
|
||||
f"Expected no reason, got: {context.sanitized_reason}"
|
||||
)
|
||||
|
||||
|
||||
@then('the sanitization reason should be "{expected}"')
|
||||
def step_check_sanitized_reason(context: Context, expected: str) -> None:
|
||||
assert (
|
||||
context.sanitized_reason == expected
|
||||
), f"Expected reason '{expected}', got '{context.sanitized_reason}'"
|
||||
assert context.sanitized_reason == expected, (
|
||||
f"Expected reason '{expected}', got '{context.sanitized_reason}'"
|
||||
)
|
||||
|
||||
|
||||
# ──────────────────────────────────────────────────
|
||||
@@ -216,9 +216,9 @@ def step_try_create_project_invalid_name(context: Context, name: str) -> None:
|
||||
@then('a project validation error should be raised mentioning "{text}"')
|
||||
def step_check_project_validation_error(context: Context, text: str) -> None:
|
||||
assert context.project_error is not None, "Expected a validation error"
|
||||
assert (
|
||||
text.lower() in str(context.project_error).lower()
|
||||
), f"Expected '{text}' in error: {context.project_error}"
|
||||
assert text.lower() in str(context.project_error).lower(), (
|
||||
f"Expected '{text}' in error: {context.project_error}"
|
||||
)
|
||||
|
||||
|
||||
@when('I create a project fixture with name "{name}"')
|
||||
@@ -255,9 +255,9 @@ def step_create_project_relative_path(context: Context, rel_path: str) -> None:
|
||||
|
||||
@then("the project fixture path should be absolute")
|
||||
def step_check_project_path_absolute(context: Context) -> None:
|
||||
assert (
|
||||
context.created_project.path.is_absolute()
|
||||
), f"Expected absolute path, got: {context.created_project.path}"
|
||||
assert context.created_project.path.is_absolute(), (
|
||||
f"Expected absolute path, got: {context.created_project.path}"
|
||||
)
|
||||
|
||||
|
||||
@when("I try to create a project with empty name")
|
||||
@@ -308,9 +308,9 @@ def step_coerce_empty_list(context: Context) -> None:
|
||||
|
||||
@then("the coerced result should be an empty list")
|
||||
def step_check_coerced_empty(context: Context) -> None:
|
||||
assert (
|
||||
context.coerced_result == []
|
||||
), f"Expected empty list, got: {context.coerced_result}"
|
||||
assert context.coerced_result == [], (
|
||||
f"Expected empty list, got: {context.coerced_result}"
|
||||
)
|
||||
|
||||
|
||||
@when("I coerce a list with one dict entry and one Change entry")
|
||||
@@ -348,9 +348,9 @@ def step_coerce_mixed_list(context: Context) -> None:
|
||||
|
||||
@then("the coerced result should have {count:d} changes")
|
||||
def step_check_coerced_count(context: Context, count: int) -> None:
|
||||
assert (
|
||||
len(context.coerced_result) == count
|
||||
), f"Expected {count} changes, got {len(context.coerced_result)}"
|
||||
assert len(context.coerced_result) == count, (
|
||||
f"Expected {count} changes, got {len(context.coerced_result)}"
|
||||
)
|
||||
|
||||
|
||||
@when("I try to coerce a non-list input")
|
||||
@@ -370,9 +370,9 @@ def step_coerce_non_list(context: Context) -> None:
|
||||
def step_check_coerce_plan_error(context: Context, text: str) -> None:
|
||||
assert context.coerce_error is not None, "Expected PlanError"
|
||||
assert isinstance(context.coerce_error, PlanError)
|
||||
assert text in str(
|
||||
context.coerce_error.message
|
||||
), f"Expected '{text}' in error: {context.coerce_error.message}"
|
||||
assert text in str(context.coerce_error.message), (
|
||||
f"Expected '{text}' in error: {context.coerce_error.message}"
|
||||
)
|
||||
|
||||
|
||||
@when("I try to coerce a list containing an integer")
|
||||
@@ -425,7 +425,7 @@ def step_try_create_arg_with_name(context: Context, name: str) -> None:
|
||||
@then("the argument name should be accepted as valid identifier")
|
||||
def step_check_arg_name_accepted(context: Context) -> None:
|
||||
# "class" is a valid Python identifier (keyword but still passes str.isidentifier())
|
||||
assert (
|
||||
context.created_arg is not None
|
||||
), f"Expected argument creation to succeed, got error: {context.arg_create_error}"
|
||||
assert context.created_arg is not None, (
|
||||
f"Expected argument creation to succeed, got error: {context.arg_create_error}"
|
||||
)
|
||||
assert context.created_arg.name == "class"
|
||||
|
||||
Reference in New Issue
Block a user