fix(cli): add project switch command
CI / load-versions (pull_request) Successful in 17s
CI / push-validation (pull_request) Successful in 25s
CI / quality (pull_request) Successful in 54s
CI / lint (pull_request) Successful in 1m1s
CI / security (pull_request) Successful in 1m9s
CI / typecheck (pull_request) Successful in 1m16s
CI / build (pull_request) Successful in 30s
CI / helm (pull_request) Successful in 36s
CI / unit_tests (pull_request) Successful in 6m3s
CI / docker (pull_request) Successful in 2m16s
CI / integration_tests (pull_request) Successful in 8m28s
CI / coverage (pull_request) Successful in 12m21s
CI / status-check (pull_request) Successful in 3s
CI / load-versions (pull_request) Successful in 17s
CI / push-validation (pull_request) Successful in 25s
CI / quality (pull_request) Successful in 54s
CI / lint (pull_request) Successful in 1m1s
CI / security (pull_request) Successful in 1m9s
CI / typecheck (pull_request) Successful in 1m16s
CI / build (pull_request) Successful in 30s
CI / helm (pull_request) Successful in 36s
CI / unit_tests (pull_request) Successful in 6m3s
CI / docker (pull_request) Successful in 2m16s
CI / integration_tests (pull_request) Successful in 8m28s
CI / coverage (pull_request) Successful in 12m21s
CI / status-check (pull_request) Successful in 3s
ISSUES CLOSED: #8675
This commit is contained in:
committed by
Forgejo
parent
fe3b9b22ca
commit
d0e685aed4
@@ -0,0 +1,42 @@
|
||||
Feature: Project CLI switch command
|
||||
As a developer
|
||||
I want to switch between registered projects
|
||||
So that I can manage my active project without changing directories
|
||||
|
||||
Background:
|
||||
Given a project CLI commands test database is initialized
|
||||
|
||||
# switch command
|
||||
|
||||
Scenario: switch command with rich output sets active project
|
||||
Given a project "local/switch-proj" is created in the commands DB
|
||||
When I invoke project-switch for "local/switch-proj" with yes default format
|
||||
Then the project cmd output should contain "Active Project Switched"
|
||||
And the project cmd output should contain "switch-proj"
|
||||
And the project cmd should succeed
|
||||
|
||||
Scenario: switch command with json format
|
||||
Given a project "local/switch-json" is created in the commands DB
|
||||
When I invoke project-switch for "local/switch-json" with yes format "json"
|
||||
Then the project cmd output should contain "active"
|
||||
And the project cmd JSON data should include "previous" key
|
||||
|
||||
Scenario: switch to nonexistent project fails
|
||||
When I invoke project-switch for "local/ghost-proj" with yes default format
|
||||
Then the project cmd should fail
|
||||
|
||||
Scenario: switch command with yaml format
|
||||
Given a project "local/switch-yaml" is created in the commands DB
|
||||
When I invoke project-switch for "local/switch-yaml" with yes format "yaml"
|
||||
Then the project cmd should succeed
|
||||
|
||||
Scenario: switch accepts --yes to skip confirmation
|
||||
Given a project "local/switch-yes" is created in the commands DB
|
||||
When I invoke project-switch for "local/switch-yes" with yes and short flag
|
||||
Then the project cmd output should contain "Active Project Switched"
|
||||
And the project cmd should succeed
|
||||
|
||||
Scenario: switch command can be interrupted by user confirmation denial
|
||||
Given a project "local/switch-cancel" is created in the commands DB
|
||||
When I invoke project-switch for "local/switch-cancel" without yes
|
||||
Then the project cmd should fail
|
||||
@@ -162,6 +162,25 @@ def _capture(context: Any, func: Any, *args: Any, **kwargs: Any) -> None:
|
||||
context._cmd_failed = failed
|
||||
|
||||
|
||||
def _load_cmd_json_output(context: Any) -> dict[str, Any]:
|
||||
"""Parse a JSON command envelope from captured CLI output."""
|
||||
output = context._cmd_output.strip()
|
||||
json_start = output.find("{")
|
||||
if json_start > 0:
|
||||
output = output[json_start:]
|
||||
try:
|
||||
envelope = json.loads(output)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise AssertionError(
|
||||
f"Command output is not valid JSON:\n{context._cmd_output}"
|
||||
) from exc
|
||||
assert isinstance(envelope, dict), (
|
||||
f"Expected command JSON envelope to be a dict, got "
|
||||
f"{type(envelope).__name__}: {envelope}"
|
||||
)
|
||||
return envelope
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Reusable helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -652,11 +671,76 @@ def step_invoke_delete_short_force(context: Any, name: str) -> None:
|
||||
_unpatch_project_mod()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Switch command
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@when('I invoke project-switch for "{name}" with yes default format')
|
||||
def step_invoke_switch(context: Any, name: str) -> None:
|
||||
from cleveragents.cli.commands.project import switch
|
||||
|
||||
_capture(context, switch, project=name, yes=True)
|
||||
|
||||
|
||||
@when('I invoke project-switch for "{name}" with yes format "{fmt}"')
|
||||
def step_invoke_switch_fmt(context: Any, name: str, fmt: str) -> None:
|
||||
from cleveragents.cli.commands.project import switch
|
||||
|
||||
_capture(context, switch, project=name, yes=True, output_format=fmt)
|
||||
|
||||
|
||||
@when('I invoke project-switch for "{name}" without yes')
|
||||
def step_invoke_switch_no_confirm(context: Any, name: str) -> None:
|
||||
"""Invoke switch without --yes, denying the confirmation prompt."""
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from cleveragents.cli.commands.project import app as project_app
|
||||
|
||||
_patch_project_mod(context)
|
||||
try:
|
||||
cli_runner = CliRunner()
|
||||
result = cli_runner.invoke(project_app, ["switch", name], input="n\n")
|
||||
context._cmd_output = result.output
|
||||
context._cmd_failed = result.exit_code != 0
|
||||
finally:
|
||||
_unpatch_project_mod()
|
||||
|
||||
|
||||
@when('I invoke project-switch for "{name}" with yes and short flag')
|
||||
def step_invoke_switch_short_flag(context: Any, name: str) -> None:
|
||||
"""Invoke 'project switch' via CliRunner using the ``-y`` short flag."""
|
||||
from typer.testing import CliRunner
|
||||
|
||||
from cleveragents.cli.commands.project import app as project_app
|
||||
|
||||
_patch_project_mod(context)
|
||||
try:
|
||||
cli_runner = CliRunner()
|
||||
result = cli_runner.invoke(project_app, ["switch", name, "-y"])
|
||||
context._cmd_output = result.output
|
||||
context._cmd_failed = result.exit_code != 0
|
||||
finally:
|
||||
_unpatch_project_mod()
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Then assertions
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@then('the project cmd JSON data should include "previous" key')
|
||||
def step_cmd_json_has_previous(context: Any) -> None:
|
||||
envelope = _load_cmd_json_output(context)
|
||||
data = envelope.get("data")
|
||||
assert isinstance(data, dict), (
|
||||
f"Expected envelope data to be a dict, got {type(data).__name__}: {data}"
|
||||
)
|
||||
assert "previous" in data, (
|
||||
f"Expected 'previous' in command JSON data, got keys {list(data.keys())}"
|
||||
)
|
||||
|
||||
|
||||
@then('the project cmd output should contain "{text}"')
|
||||
def step_cmd_output_contains(context: Any, text: str) -> None:
|
||||
assert text.lower() in context._cmd_output.lower(), (
|
||||
@@ -680,14 +764,7 @@ def step_cmd_fail(context: Any) -> None:
|
||||
|
||||
@then("the project cmd JSON data should include deleted_at")
|
||||
def step_cmd_json_has_deleted_at(context: Any) -> None:
|
||||
output = context._cmd_output.strip()
|
||||
try:
|
||||
envelope = json.loads(output)
|
||||
except json.JSONDecodeError as exc:
|
||||
raise AssertionError(
|
||||
f"Command output is not valid JSON:\n{context._cmd_output}"
|
||||
) from exc
|
||||
|
||||
envelope = _load_cmd_json_output(context)
|
||||
data = envelope.get("data")
|
||||
assert isinstance(data, dict), (
|
||||
f"Expected envelope data to be a dict, got {type(data).__name__}: {data}"
|
||||
|
||||
Reference in New Issue
Block a user