test(coverage): add Behave BDD tests for 8 under-covered modules
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 19s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 38s
CI / integration_tests (pull_request) Successful in 2m44s
CI / lint (push) Successful in 12s
CI / quality (push) Successful in 16s
CI / build (push) Successful in 23s
CI / typecheck (push) Successful in 31s
CI / security (push) Successful in 32s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 3m25s
CI / unit_tests (pull_request) Successful in 13m3s
CI / docker (pull_request) Successful in 1m2s
CI / benchmark-publish (push) Successful in 16m35s
CI / benchmark-regression (pull_request) Successful in 22m21s
CI / unit_tests (push) Successful in 24m45s
CI / docker (push) Successful in 8s
CI / coverage (pull_request) Successful in 1h1m47s
CI / coverage (push) Successful in 1h11m37s
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 15s
CI / build (pull_request) Successful in 16s
CI / quality (pull_request) Successful in 19s
CI / security (pull_request) Successful in 36s
CI / typecheck (pull_request) Successful in 38s
CI / integration_tests (pull_request) Successful in 2m44s
CI / lint (push) Successful in 12s
CI / quality (push) Successful in 16s
CI / build (push) Successful in 23s
CI / typecheck (push) Successful in 31s
CI / security (push) Successful in 32s
CI / benchmark-regression (push) Has been skipped
CI / integration_tests (push) Successful in 3m25s
CI / unit_tests (pull_request) Successful in 13m3s
CI / docker (pull_request) Successful in 1m2s
CI / benchmark-publish (push) Successful in 16m35s
CI / benchmark-regression (pull_request) Successful in 22m21s
CI / unit_tests (push) Successful in 24m45s
CI / docker (push) Successful in 8s
CI / coverage (pull_request) Successful in 1h1m47s
CI / coverage (push) Successful in 1h11m37s
Added targeted Behave BDD feature files and step definitions to improve unit test coverage for: - decision_service.py: Full coverage of all 7 service methods (18 scenarios) - plan_apply_service.py: Branch coverage for handle_merge_failure (2 scenarios) - plan_executor.py: Edge cases for rollback, checkpoint, and parse_steps (15 scenarios) - cli/commands/plan.py: Uncovered region lines 1950-2273 (23 scenarios) - repositories.py: Remaining missed branches and lines (14 scenarios) - sandbox/checkpoint.py: Full coverage of CheckpointManager (26 scenarios) - langgraph/bridge.py: Remaining uncovered lines and branches (10 scenarios) - cli/commands/config.py: Safety net to maintain 100% coverage (42 scenarios) Total: 150 new scenarios, 596 steps, all passing. Also fixed a step definition collision in plan_lifecycle_coverage by renaming "the delete result should be false" to "the plan delete result should be false". ISSUES CLOSED: #475
This commit is contained in:
@@ -0,0 +1,156 @@
|
||||
Feature: DecisionService application-layer coverage
|
||||
As a developer
|
||||
I want thorough unit tests for every DecisionService method
|
||||
So that decision_service.py achieves full line and branch coverage
|
||||
|
||||
# All steps use the "dsvc-" prefix to avoid collisions with other step files.
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# Constructor
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Scenario: dsvc- constructor stores settings and unit_of_work
|
||||
Given dsvc- a mock settings object and a mock UnitOfWork
|
||||
When dsvc- I construct a DecisionService with those dependencies
|
||||
Then dsvc- the service should store the settings
|
||||
And dsvc- the service should store the unit_of_work
|
||||
And dsvc- the service should have a bound structlog logger
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# record_decision
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Scenario: dsvc- record_decision persists and returns the decision
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- a sample root Decision object
|
||||
When dsvc- I call record_decision with the sample decision
|
||||
Then dsvc- the UoW transaction should have been entered
|
||||
And dsvc- ctx.decisions.create should have been called with the decision
|
||||
And dsvc- the returned decision should be the same object
|
||||
|
||||
Scenario: dsvc- record_decision logs info and debug messages
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork and captured logger
|
||||
And dsvc- a sample root Decision object
|
||||
When dsvc- I call record_decision with the sample decision
|
||||
Then dsvc- the logger should have recorded an info call with "recording_decision"
|
||||
And dsvc- the logger should have recorded a debug call with "decision_recorded"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# get_decision
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Scenario: dsvc- get_decision retrieves a decision by ID
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo get method returns a Decision
|
||||
When dsvc- I call get_decision with a known ID
|
||||
Then dsvc- ctx.decisions.get should have been called with the ID
|
||||
And dsvc- the returned value should be the expected Decision
|
||||
|
||||
Scenario: dsvc- get_decision returns None when not found
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo get method returns None
|
||||
When dsvc- I call get_decision with an unknown ID
|
||||
Then dsvc- the returned value should be None
|
||||
|
||||
Scenario: dsvc- get_decision logs a debug message
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork and captured logger
|
||||
And dsvc- the mock repo get method returns a Decision
|
||||
When dsvc- I call get_decision with a known ID
|
||||
Then dsvc- the logger should have recorded a debug call with "getting_decision"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# get_decisions_for_plan
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Scenario: dsvc- get_decisions_for_plan returns list of decisions
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo get_by_plan method returns 3 decisions
|
||||
When dsvc- I call get_decisions_for_plan with a plan ID
|
||||
Then dsvc- ctx.decisions.get_by_plan should have been called with the plan ID
|
||||
And dsvc- the returned list should have 3 decisions
|
||||
|
||||
Scenario: dsvc- get_decisions_for_plan returns empty list when none exist
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo get_by_plan method returns 0 decisions
|
||||
When dsvc- I call get_decisions_for_plan with a plan ID
|
||||
Then dsvc- the returned list should have 0 decisions
|
||||
|
||||
Scenario: dsvc- get_decisions_for_plan logs a debug message
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork and captured logger
|
||||
And dsvc- the mock repo get_by_plan method returns 3 decisions
|
||||
When dsvc- I call get_decisions_for_plan with a plan ID
|
||||
Then dsvc- the logger should have recorded a debug call with "getting_decisions_for_plan"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# get_decision_tree
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Scenario: dsvc- get_decision_tree returns BFS-ordered list
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo get_tree method returns 4 decisions
|
||||
When dsvc- I call get_decision_tree with a root ID
|
||||
Then dsvc- ctx.decisions.get_tree should have been called with the root ID
|
||||
And dsvc- the returned tree list should have 4 decisions
|
||||
|
||||
Scenario: dsvc- get_decision_tree logs a debug message
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork and captured logger
|
||||
And dsvc- the mock repo get_tree method returns 4 decisions
|
||||
When dsvc- I call get_decision_tree with a root ID
|
||||
Then dsvc- the logger should have recorded a debug call with "getting_decision_tree"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# get_path_to_root
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Scenario: dsvc- get_path_to_root returns leaf-to-root path
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo get_path_to_root method returns 3 decisions
|
||||
When dsvc- I call get_path_to_root with a leaf ID
|
||||
Then dsvc- ctx.decisions.get_path_to_root should have been called with the leaf ID
|
||||
And dsvc- the returned path list should have 3 decisions
|
||||
|
||||
Scenario: dsvc- get_path_to_root logs a debug message
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork and captured logger
|
||||
And dsvc- the mock repo get_path_to_root method returns 3 decisions
|
||||
When dsvc- I call get_path_to_root with a leaf ID
|
||||
Then dsvc- the logger should have recorded a debug call with "getting_path_to_root"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# mark_superseded
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Scenario: dsvc- mark_superseded updates and returns the decision
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo update_superseded_by method returns a superseded Decision
|
||||
When dsvc- I call mark_superseded with old and new IDs
|
||||
Then dsvc- ctx.decisions.update_superseded_by should have been called with both IDs
|
||||
And dsvc- the returned decision should be the superseded one
|
||||
|
||||
Scenario: dsvc- mark_superseded logs info with both IDs
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork and captured logger
|
||||
And dsvc- the mock repo update_superseded_by method returns a superseded Decision
|
||||
When dsvc- I call mark_superseded with old and new IDs
|
||||
Then dsvc- the logger should have recorded an info call with "marking_superseded"
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# list_by_type
|
||||
# ------------------------------------------------------------------
|
||||
|
||||
Scenario: dsvc- list_by_type returns filtered decisions
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo list_by_type method returns 2 decisions
|
||||
When dsvc- I call list_by_type with plan ID and type "strategy_choice"
|
||||
Then dsvc- ctx.decisions.list_by_type should have been called with plan ID and type
|
||||
And dsvc- the returned type list should have 2 decisions
|
||||
|
||||
Scenario: dsvc- list_by_type returns empty when no matches
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork
|
||||
And dsvc- the mock repo list_by_type method returns 0 decisions
|
||||
When dsvc- I call list_by_type with plan ID and type "error_recovery"
|
||||
Then dsvc- the returned type list should have 0 decisions
|
||||
|
||||
Scenario: dsvc- list_by_type logs a debug message
|
||||
Given dsvc- a DecisionService with a mocked UnitOfWork and captured logger
|
||||
And dsvc- the mock repo list_by_type method returns 2 decisions
|
||||
When dsvc- I call list_by_type with plan ID and type "strategy_choice"
|
||||
Then dsvc- the logger should have recorded a debug call with "listing_by_type"
|
||||
Reference in New Issue
Block a user