93567d1453
CI / lint (pull_request) Successful in 35s
CI / build (pull_request) Successful in 35s
CI / helm (pull_request) Successful in 43s
CI / quality (pull_request) Successful in 51s
CI / push-validation (pull_request) Successful in 35s
CI / typecheck (pull_request) Successful in 57s
CI / security (pull_request) Successful in 1m22s
CI / unit_tests (pull_request) Successful in 4m17s
CI / docker (pull_request) Successful in 1m30s
CI / integration_tests (pull_request) Successful in 9m26s
CI / coverage (pull_request) Successful in 9m6s
CI / status-check (pull_request) Successful in 3s
Fixes the four root causes behind PR #8733's red unit_tests + Robot integration_tests gates after the post-rebase landing of the v3.3.0 spec + invariant enforcement work. 1. Lazy-import module path used a slash instead of a dot — the `__getattr__` lookup in `application/services/__init__.py` could never resolve `SubplanSpawnError` because `importlib.import_module` only accepts dotted module paths. This broke both the `svcov3 lazy-load SubplanSpawnError` Behave scenario AND the `Test Services Package Exports` Robot scenario (the `from cleveragents.application.services import *` star-import walks `__all__` and trips on the bad entry). 2. `subplan_service_coverage_boost.feature:11` contained the literal placeholder text `{1:d}` instead of the literal value `1`. Gherkin does not interpolate `{n:d}` in feature bodies — only in step patterns — so the existing `the SubplanSpawnError message should contain {n:d} semicolons` step could not match. 3. `step_load_invariants_plan_only` used `@when(re.compile(...))` without `use_step_matcher("re")`, so behave silently treated it as an undefined step. Switched the file's matcher to `re` for that single step (auto-anchored, no end marker — behave's `re` matcher refuses `$`) and back to `parse` for the rest, so it no longer collides with the parse-matched "with project" variant whose `{plan_id}` field is greedy. 4. `step_check_action_against_invariants` used the parse field `{action_text}` which doesn't match empty strings, so the "Empty action text" scenario reported the When step as undefined. Extracted the body into `_check_action_against_loaded` and added a literal-pattern `'I check action "" against loaded invariants'` step that delegates to it. Both step variants now also write `context.error` so the shared `the error message should contain "..."` step in `service_steps.py` (which reads `context.error`) works against either error type. 5. `step_attempt_strategy_decision` / `step_create_strategy_decision` only checked `context.strategize_invariants`, which is empty when the scenario adds a global invariant via `Given` but never runs the explicit `I start the Strategize phase` step. Added `_strategize_active_invariants` helper that falls back to all active invariants on the service — mirrors the same fallback pattern already used by `_check_action_against_loaded`. 6. `step_winning_scope` in `invariant_reconciliation_actor_steps.py` only read `context.reconciliation_result`, so reusing the `the winning invariant for "X" should be from "Y" scope` assertion in a non-reconciliation scenario errored with AttributeError. Now falls back to `context.loaded_invariants` (which `InvariantService.load_active_invariants` already merges with the plan > project > global precedence the scenario asserts). Verified locally: - `unit_tests` gate passes (16504 scenarios, 0 failed, 0 errored). - `lint` gate passes. - `Test Services Package Exports` star-import path resolves `SubplanSpawnError` cleanly via the package `__getattr__`. ISSUES CLOSED: #8725
114 lines
5.1 KiB
Gherkin
114 lines
5.1 KiB
Gherkin
@phase1 @subplan @coverage
|
|
Feature: Subplan Service Coverage Boost
|
|
Additional scenarios targeting uncovered code paths in
|
|
SubplanService, SubplanSpawnError, and related helpers.
|
|
|
|
# --- SubplanSpawnError direct instantiation (v3.3.0) ---
|
|
|
|
@error_object
|
|
Scenario: SubplanSpawnError message format includes errors
|
|
When a SubplanSpawnError is created for errors "bad resource" and "missing merge"
|
|
Then the SubplanSpawnError message should contain 1 semicolons
|
|
And the SubplanSpawnError message should contain "Spawn validation failed"
|
|
And the SubplanSpawnError message should contain "bad resource"
|
|
And the SubplanSpawnError message should contain "missing merge"
|
|
|
|
# --- decision_service property (line 167) ---
|
|
|
|
@property_access
|
|
Scenario: Accessing the decision_service property returns the injected service
|
|
Given a subplan service constructed with a mock decision service
|
|
When the decision_service property is accessed
|
|
Then the returned decision service should be the injected mock
|
|
|
|
# --- spawn() with config=None (line 207) ---
|
|
|
|
@null_guard @spawn
|
|
Scenario: Spawn raises ValueError when config is None
|
|
Given a subplan service constructed with a mock decision service
|
|
And a parent plan for coverage tests
|
|
When spawn is called with config set to None
|
|
Then a ValueError should be raised with text "config must not be None"
|
|
|
|
# --- spawn() with spawn_entries=None (line 209) ---
|
|
|
|
@null_guard @spawn
|
|
Scenario: Spawn raises ValueError when spawn_entries is None
|
|
Given a subplan service constructed with a mock decision service
|
|
And a parent plan for coverage tests
|
|
And a default subplan config for coverage tests
|
|
When spawn is called with spawn_entries set to None
|
|
Then a ValueError should be raised with text "spawn_entries must not be None"
|
|
|
|
# --- spawn() raising SubplanSpawnError (v3.3.0) ---
|
|
|
|
@spawn_validation_error
|
|
Scenario: Spawn raises SubplanSpawnError when validation fails
|
|
Given a subplan service constructed with a mock decision service
|
|
And a parent plan for coverage tests
|
|
And a subplan config with merge strategy forcibly set to None
|
|
And a spawn entry with a valid spawn decision for coverage tests
|
|
When spawn is called and validation is expected to fail
|
|
Then a SubplanSpawnError should be raised
|
|
And the SubplanSpawnError should mention "merge strategy"
|
|
|
|
# --- validate_spawn() with config=None (line 301) ---
|
|
|
|
@null_guard @validate
|
|
Scenario: Validate spawn raises ValueError when config is None
|
|
Given a subplan service constructed with a mock decision service
|
|
And a spawn entry with a valid spawn decision for coverage tests
|
|
When validate_spawn is called with config set to None
|
|
Then a ValueError should be raised with text "config must not be None"
|
|
|
|
# --- validate_spawn() with spawn_entries=None (line 303) ---
|
|
|
|
@null_guard @validate
|
|
Scenario: Validate spawn raises ValueError when spawn_entries is None
|
|
Given a subplan service constructed with a mock decision service
|
|
And a default subplan config for coverage tests
|
|
When validate_spawn is called with spawn_entries set to None
|
|
Then a ValueError should be raised with text "spawn_entries must not be None"
|
|
|
|
# --- Empty action_name validation (lines 334-336) ---
|
|
|
|
@validation @action_name
|
|
Scenario: Validate spawn detects empty action_name on spawn entry
|
|
Given a subplan service constructed with a mock decision service
|
|
And a default subplan config for coverage tests
|
|
And a spawn entry with an empty action_name
|
|
When validate_spawn is called for coverage tests
|
|
Then the validation result should not be valid
|
|
And the validation errors should mention "empty action_name"
|
|
|
|
@validation @action_name
|
|
Scenario: Validate spawn detects whitespace-only action_name on spawn entry
|
|
Given a subplan service constructed with a mock decision service
|
|
And a default subplan config for coverage tests
|
|
And a spawn entry with a whitespace-only action_name
|
|
When validate_spawn is called for coverage tests
|
|
Then the validation result should not be valid
|
|
And the validation errors should mention "empty action_name"
|
|
|
|
# --- get_spawn_decisions() with empty plan_id (line 375) ---
|
|
|
|
@null_guard @get_decisions
|
|
Scenario: Get spawn decisions raises ValueError for empty plan_id
|
|
Given a subplan service constructed with a mock decision service
|
|
When get_spawn_decisions is called with an empty plan_id
|
|
Then a ValueError should be raised with text "plan_id must not be empty"
|
|
|
|
@null_guard @get_decisions
|
|
Scenario: Get spawn decisions raises ValueError for whitespace-only plan_id
|
|
Given a subplan service constructed with a mock decision service
|
|
When get_spawn_decisions is called with a whitespace-only plan_id
|
|
Then a ValueError should be raised with text "plan_id must not be empty"
|
|
|
|
# --- build_spawn_entries() with decisions=None (line 414) ---
|
|
|
|
@null_guard @build_entries
|
|
Scenario: Build spawn entries raises ValueError when decisions is None
|
|
Given a subplan service constructed with a mock decision service
|
|
When build_spawn_entries is called with decisions set to None
|
|
Then a ValueError should be raised with text "decisions must not be None"
|