Files
cleveragents-core/features/invariant_cli_new_coverage.feature
HAL9000 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
fix(cli): preserve no-flag list_invariants "all scopes" semantics
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
2026-06-18 02:28:23 -04:00

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"