c7a20eccd0
CI / load-versions (pull_request) Successful in 31s
CI / push-validation (pull_request) Successful in 37s
CI / lint (pull_request) Successful in 44s
CI / typecheck (pull_request) Successful in 1m10s
CI / security (pull_request) Successful in 1m24s
CI / build (pull_request) Successful in 36s
CI / quality (pull_request) Successful in 1m22s
CI / helm (pull_request) Successful in 2m7s
CI / unit_tests (pull_request) Successful in 7m55s
CI / docker (pull_request) Successful in 2m54s
CI / integration_tests (pull_request) Successful in 12m23s
CI / coverage (pull_request) Successful in 14m4s
CI / status-check (pull_request) Successful in 3s
The previous refactor routed list_invariants through _resolve_scope, which silently changed the no-flags behavior: _resolve_scope returns (GLOBAL, "system") when no flag is given (the `add` default), so `agents invariant list` filtered to only global+system invariants instead of returning every active invariant. InvariantService.list_invariants documents scope=None / source_name=None as "all scopes" / "all sources"; the listing CLI must preserve that contract. Inline the mutual-exclusion check in list_invariants and restore the if/elif chain that leaves scope/source_name=None when no flag is set, while keeping `agents invariant add` defaulting to global. Tests added: - "List invariants with no flags passes no scope filter" verifies the service is called with scope=None, source_name=None. - "List invariants with conflicting scope flags rejected" covers the new BadParameter raise path. ISSUES CLOSED: #11049
179 lines
8.5 KiB
Gherkin
179 lines
8.5 KiB
Gherkin
Feature: Invariant CLI commands coverage
|
|
As a developer
|
|
I want full test coverage for the invariant CLI commands module
|
|
So that all commands and helper functions are verified
|
|
|
|
# === _resolve_scope helper ===
|
|
|
|
Scenario: Resolve scope with --global flag returns GLOBAL scope
|
|
When I resolve invariant scope with global flag set
|
|
Then the resolved invariant scope should be "global"
|
|
And the resolved invariant source name should be "system"
|
|
|
|
Scenario: Resolve scope with --project flag returns PROJECT scope
|
|
When I resolve invariant scope with project "myapp"
|
|
Then the resolved invariant scope should be "project"
|
|
And the resolved invariant source name should be "myapp"
|
|
|
|
Scenario: Resolve scope with --plan flag returns PLAN scope
|
|
When I resolve invariant scope with plan "PLAN_01HX"
|
|
Then the resolved invariant scope should be "plan"
|
|
And the resolved invariant source name should be "PLAN_01HX"
|
|
|
|
Scenario: Resolve scope with --action flag returns ACTION scope
|
|
When I resolve invariant scope with action "deploy-service"
|
|
Then the resolved invariant scope should be "action"
|
|
And the resolved invariant source name should be "deploy-service"
|
|
|
|
Scenario: Resolve scope with no flags defaults to GLOBAL scope
|
|
When I resolve invariant scope with no flags
|
|
Then the resolved invariant scope should be "global"
|
|
And the resolved invariant source name should be "system"
|
|
|
|
Scenario: Resolve scope with conflicting flags raises BadParameter
|
|
When I resolve invariant scope with global and project flags
|
|
Then a BadParameter error should be raised for invariant scope
|
|
|
|
# === _invariant_dict helper ===
|
|
|
|
Scenario: Invariant dict serializes an invariant correctly
|
|
Given a sample invariant with known fields for dict test
|
|
When I call invariant_dict on the sample invariant
|
|
Then the invariant dict should contain the correct id and text
|
|
And the invariant dict should contain scope and source_name
|
|
And the invariant dict should contain active and created_at ISO string
|
|
|
|
# === _get_service singleton ===
|
|
|
|
Scenario: Get service creates InvariantService lazily
|
|
When I call get_service with invariant module service reset to None
|
|
Then an InvariantService instance should be returned from get_service
|
|
And calling get_service again returns the same InvariantService instance
|
|
|
|
# === invariant add command ===
|
|
|
|
Scenario: Add invariant with --global flag via CLI
|
|
Given a mocked InvariantService for invariant CLI add
|
|
When I invoke invariant add with "--global" and text "Never delete prod data"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "Invariant added"
|
|
And the invariant CLI output should contain "Never delete prod data"
|
|
|
|
Scenario: Add invariant with --project flag via CLI
|
|
Given a mocked InvariantService for invariant CLI add
|
|
When I invoke invariant add with "--project myapp" and text "All APIs need tests"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "Invariant added"
|
|
|
|
Scenario: Add invariant without scope flag via CLI
|
|
Given a mocked InvariantService for invariant CLI add
|
|
When I invoke invariant add without scope flags and text "Missing scope flag"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "Invariant added"
|
|
|
|
Scenario: Add invariant with --format json via CLI
|
|
Given a mocked InvariantService for invariant CLI add
|
|
When I invoke invariant add with "--global --format json" and text "JSON constraint"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "JSON constraint"
|
|
|
|
Scenario: Add invariant handles CleverAgentsError
|
|
Given a mocked InvariantService that raises CleverAgentsError on add
|
|
When I invoke invariant add with "--global" and text "will fail"
|
|
Then the invariant CLI exit code should be non-zero
|
|
And the invariant CLI output should contain "Error"
|
|
|
|
# === invariant list command ===
|
|
|
|
Scenario: List invariants with no results
|
|
Given a mocked InvariantService that returns empty invariant list
|
|
When I invoke invariant list with no filters
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "No invariants found"
|
|
|
|
Scenario: List invariants with results in rich format
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with no filters
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "Invariants"
|
|
And the invariant CLI output should contain "Never delete prod"
|
|
|
|
Scenario: List invariants with --global filter
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with flags "--global"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant service list was called with global scope
|
|
|
|
Scenario: List invariants with --project filter
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with flags "--project myapp"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant service list was called with project scope and source "myapp"
|
|
|
|
Scenario: List invariants with --plan filter
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with flags "--plan PLAN_01HX"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant service list was called with plan scope and source "PLAN_01HX"
|
|
|
|
Scenario: List invariants with --action filter
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with flags "--action deploy"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant service list was called with action scope and source "deploy"
|
|
|
|
Scenario: List invariants with --effective flag
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with flags "--effective"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant service list was called with effective true
|
|
|
|
Scenario: List invariants with regex filter matching
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with regex "prod"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "Never delete prod"
|
|
|
|
Scenario: List invariants with invalid regex
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with regex "[invalid"
|
|
Then the invariant CLI exit code should be non-zero
|
|
And the invariant CLI output should contain "Invalid regex"
|
|
|
|
Scenario: List invariants with --format json
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with flags "--format json"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "Never delete prod"
|
|
|
|
Scenario: List invariants with no flags passes no scope filter
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with no filters
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant service list was called with no scope filter
|
|
|
|
Scenario: List invariants with conflicting scope flags rejected
|
|
Given a mocked InvariantService that returns two invariants for list
|
|
When I invoke invariant list with flags "--global --project myapp"
|
|
Then the invariant CLI exit code should be non-zero
|
|
|
|
# === invariant remove command ===
|
|
|
|
Scenario: Remove invariant with --yes flag
|
|
Given a mocked InvariantService for invariant CLI remove
|
|
When I invoke invariant remove with "--yes" for id "INV_01HX"
|
|
Then the invariant CLI exit code should be 0
|
|
And the invariant CLI output should contain "Invariant removed"
|
|
|
|
Scenario: Remove invariant without --yes cancelled by user
|
|
Given a mocked InvariantService for invariant CLI remove
|
|
When I invoke invariant remove without --yes for id "INV_01HX" and answer no
|
|
Then the invariant CLI exit code should be non-zero
|
|
And the invariant CLI output should contain "Cancelled"
|
|
|
|
Scenario: Remove invariant raises NotFoundError
|
|
Given a mocked InvariantService that raises NotFoundError on remove
|
|
When I invoke invariant remove with "--yes" for id "MISSING_ID"
|
|
Then the invariant CLI exit code should be non-zero
|
|
And the invariant CLI output should contain "not found"
|