Files
freemo 051ee7c290
CI / benchmark-publish (pull_request) Has been skipped
CI / lint (pull_request) Successful in 21s
CI / quality (pull_request) Successful in 31s
CI / typecheck (pull_request) Successful in 47s
CI / security (pull_request) Successful in 52s
CI / build (pull_request) Successful in 56s
CI / e2e_tests (pull_request) Successful in 5m1s
CI / integration_tests (pull_request) Successful in 5m30s
CI / unit_tests (pull_request) Successful in 5m42s
CI / docker (pull_request) Successful in 58s
CI / coverage (pull_request) Successful in 7m35s
CI / build (push) Successful in 21s
CI / docker (push) Has been skipped
CI / benchmark-regression (pull_request) Failing after 49m24s
CI / lint (push) Successful in 22s
CI / quality (push) Successful in 39s
CI / security (push) Successful in 48s
CI / typecheck (push) Successful in 1m26s
CI / benchmark-regression (push) Has been skipped
CI / e2e_tests (push) Successful in 5m53s
CI / coverage (push) Successful in 9m4s
CI / benchmark-publish (push) Successful in 19m10s
CI / integration_tests (push) Failing after 19m18s
CI / unit_tests (push) Failing after 19m20s
test(coverage): add Behave BDD tests to improve coverage across 52 source files
Added 52 new .feature files and corresponding _steps.py files targeting
previously uncovered code paths in the following areas:

- TUI layer: app, commands, persona (state/schema/registry), widgets,
  input (shell_exec, reference_parser)
- Application services: plan lifecycle/service/executor, session,
  project, repo indexing, correction, checkpoint, actor, llm_actors,
  strategy coordinator, resource file watcher, service retry wiring
- CLI commands: session, resource, repl, plan, db, automation_profile
- Domain models: retry_policy, resource_type, cost_budget,
  docker_compose_analyzer, detail_level, _sql_string_aware,
  _postgresql_helpers
- Core: circuit_breaker, retry_service_patterns
- Infrastructure: repositories, transaction_sandbox, strategy_registry,
  plugins/loader, container
- Config: settings
- Agents: plan_generation, context_analysis, auto_debug
- A2A: facade

All new tests follow the Behave/Gherkin BDD standard. Resolved step
definition collisions with unique prefixes. Fixed Alembic fileConfig
logger disabling issue (disable_existing_loggers=False).

ISSUES CLOSED: #1068
2026-03-20 21:22:10 +00:00

310 lines
12 KiB
Gherkin

Feature: REPL module full coverage
Comprehensive unit-level tests that exercise every function and branch
inside ``src/cleveragents/cli/commands/repl.py`` to bring its coverage
from 0% to near-100%.
# -----------------------------------------------------------------
# Constants
# -----------------------------------------------------------------
Scenario: Module constants are defined correctly
Given replcov the repl module is imported
Then the constant _MULTILINE_CONTINUATION equals backslash
And the constant _LAST_COMMAND_TOKEN equals "!!"
And the _REPL_COMMANDS list is non-empty
And the _BUILTIN_COMMANDS list contains help exit and quit
# -----------------------------------------------------------------
# _get_prompt_context
# -----------------------------------------------------------------
Scenario: _get_prompt_context returns bare prompt when no env vars set
Given neither CLEVERAGENTS_PROJECT nor CLEVERAGENTS_PLAN is set
When I call _get_prompt_context
Then the prompt result is "agents> "
Scenario: _get_prompt_context returns project-only prompt
Given CLEVERAGENTS_PROJECT is set to "proj1" and CLEVERAGENTS_PLAN is unset
When I call _get_prompt_context
Then the prompt result is "agents (proj1)> "
Scenario: _get_prompt_context returns project+plan prompt
Given CLEVERAGENTS_PROJECT is set to "proj1" and CLEVERAGENTS_PLAN is set to "plan1"
When I call _get_prompt_context
Then the prompt result is "agents (proj1/plan1)> "
# -----------------------------------------------------------------
# _setup_history
# -----------------------------------------------------------------
Scenario: _setup_history loads existing history file
Given a temporary history file that exists
When I call _setup_history with that path
Then readline.read_history_file is called with the path
And readline.set_history_length is called with 10000
Scenario: _setup_history skips read when file does not exist
Given a temporary history path that does not exist
When I call _setup_history with that path
Then readline.read_history_file is not called
And readline.set_history_length is called with 10000
# -----------------------------------------------------------------
# _save_history
# -----------------------------------------------------------------
Scenario: _save_history writes the history file
Given a temporary history path for saving
When I call _save_history with that path
Then readline.write_history_file is called with the path
Scenario: _save_history suppresses OSError
Given readline.write_history_file will raise OSError
When I call _save_history with a dummy path
Then no exception is raised
# -----------------------------------------------------------------
# _setup_completer
# -----------------------------------------------------------------
Scenario: _setup_completer installs readline completer
When I call _setup_completer
Then readline.set_completer is called with a callable
And readline.set_completer_delims is called
And readline.parse_and_bind is called with tab complete
Scenario: The installed completer returns matching tokens
Given the completer is installed via _setup_completer
When I query the completer with text "ver" and state 0
Then the completer returns "version"
Scenario: The installed completer returns None for out-of-range state
Given the completer is installed via _setup_completer
When I query the completer with text "ver" and state 99
Then the completer returns None
# -----------------------------------------------------------------
# _read_multiline
# -----------------------------------------------------------------
Scenario: _read_multiline reads continuation lines ending normally
Given builtins.input will return "continued line"
When I call _read_multiline with initial line ending in backslash
Then the multiline result is "first continued line"
Scenario: _read_multiline handles chained continuations
Given builtins.input will return chained continuations
When I call _read_multiline with initial line ending in backslash
Then the multiline result is "first second third"
Scenario: _read_multiline handles EOFError in continuation
Given builtins.input will raise EOFError on continuation
When I call _read_multiline with initial line ending in backslash
Then the multiline result is "only"
Scenario: _read_multiline handles KeyboardInterrupt in continuation
Given builtins.input will raise KeyboardInterrupt on continuation
When I call _read_multiline with initial line ending in backslash
Then the multiline result is "only"
# -----------------------------------------------------------------
# _print_repl_help
# -----------------------------------------------------------------
Scenario: _print_repl_help runs without error
When I call _print_repl_help
Then no exception is raised from _print_repl_help
# -----------------------------------------------------------------
# dispatch_command
# -----------------------------------------------------------------
Scenario: dispatch_command returns 0 on success
Given the root Typer app is mocked to succeed
When I call dispatch_command with args "version"
Then the dispatch return code is 0
Scenario: dispatch_command returns code from SystemExit
Given the root Typer app raises SystemExit with code 42
When I call dispatch_command with args "bad"
Then the dispatch return code is 42
Scenario: dispatch_command returns 0 for SystemExit with None code
Given the root Typer app raises SystemExit with None code
When I call dispatch_command with args "something"
Then the dispatch return code is 0
Scenario: dispatch_command returns 130 on KeyboardInterrupt
Given the root Typer app raises KeyboardInterrupt
When I call dispatch_command with args "slow"
Then the dispatch return code is 130
Scenario: dispatch_command returns 1 and prints error on generic Exception
Given the root Typer app raises RuntimeError boom
When I call dispatch_command with args "fail"
Then the dispatch return code is 1
And the dispatch error output contains "boom"
# -----------------------------------------------------------------
# run_repl — exit commands
# -----------------------------------------------------------------
Scenario: run_repl exits on :exit command
Given the REPL input sequence is
| line |
| :exit |
When I run the REPL loop
Then the REPL loop exit code is 0
Scenario: run_repl exits on :quit command
Given the REPL input sequence is
| line |
| :quit |
When I run the REPL loop
Then the REPL loop exit code is 0
# -----------------------------------------------------------------
# run_repl — help
# -----------------------------------------------------------------
Scenario: run_repl handles :help and continues
Given the REPL input sequence is
| line |
| :help |
| :exit |
When I run the REPL loop
Then the REPL loop output contains "Built-in commands"
# -----------------------------------------------------------------
# run_repl — !! repeat
# -----------------------------------------------------------------
Scenario: run_repl repeats last command with !!
Given the REPL input sequence is
| line |
| version |
| !! |
| :exit |
And dispatch_command is mocked for run_repl
When I run the REPL loop
Then dispatch_command was called at least 2 times with "version"
Scenario: run_repl warns when !! has no previous command
Given the REPL input sequence is
| line |
| !! |
| :exit |
When I run the REPL loop
Then the REPL loop output contains "No previous command"
# -----------------------------------------------------------------
# run_repl — empty and whitespace input
# -----------------------------------------------------------------
Scenario: run_repl skips empty lines
Given the REPL input sequence is
| line |
| |
| :exit |
When I run the REPL loop
Then the REPL loop exit code is 0
# -----------------------------------------------------------------
# run_repl — multiline
# -----------------------------------------------------------------
Scenario: run_repl handles multiline input ending with backslash
Given the REPL input sequence for multiline is "version \" then "" then ":exit"
And dispatch_command is mocked for run_repl
When I run the REPL loop
Then the REPL loop exit code is 0
# -----------------------------------------------------------------
# run_repl — KeyboardInterrupt / EOFError
# -----------------------------------------------------------------
Scenario: run_repl handles KeyboardInterrupt and continues
Given the REPL input sequence for interrupt then exit
When I run the REPL loop
Then the REPL loop exit code is 0
Scenario: run_repl handles EOFError for clean exit
Given the REPL input sequence is empty
When I run the REPL loop
Then the REPL loop exit code is 0
# -----------------------------------------------------------------
# run_repl — shlex parse error
# -----------------------------------------------------------------
Scenario: run_repl handles shlex parse error gracefully
Given the REPL input sequence has an unclosed quote then exit
When I run the REPL loop
Then the REPL loop output contains "Parse error"
# -----------------------------------------------------------------
# run_repl — history on/off
# -----------------------------------------------------------------
Scenario: run_repl with history enabled calls setup and save
Given the REPL input sequence is
| line |
| :exit |
When I run the REPL loop with history enabled
Then _setup_history was called
And _save_history was called
Scenario: run_repl with no_history skips setup and save
Given the REPL input sequence is
| line |
| :exit |
When I run the REPL loop with no_history
Then _setup_history was not called
And _save_history was not called
# -----------------------------------------------------------------
# run_repl — dispatch a real-ish command
# -----------------------------------------------------------------
Scenario: run_repl dispatches tokenised command
Given the REPL input sequence is
| line |
| info |
| :exit |
And dispatch_command is mocked for run_repl
When I run the REPL loop
Then dispatch_command was called with args "info"
# -----------------------------------------------------------------
# repl_callback
# -----------------------------------------------------------------
Scenario: repl_callback starts REPL when stdout is a tty
Given sys.stdout.isatty returns True
And run_repl is mocked to return 0
When I call repl_callback
Then run_repl was invoked
And repl_callback raises SystemExit with code 0
Scenario: repl_callback starts REPL when CLEVERAGENTS_FORCE_REPL is set
Given sys.stdout.isatty returns False
And CLEVERAGENTS_FORCE_REPL is set in the environment
And run_repl is mocked to return 0
When I call repl_callback
Then run_repl was invoked
Scenario: repl_callback does nothing when not a tty and no force env
Given sys.stdout.isatty returns False
And CLEVERAGENTS_FORCE_REPL is not set
When I call repl_callback
Then run_repl was not invoked
# -----------------------------------------------------------------
# Completer edge case: empty text matches all tokens
# -----------------------------------------------------------------
Scenario: The installed completer returns first token for empty text
Given the completer is installed via _setup_completer
When I query the completer with empty text and state 0
Then the completer returns a non-None value