feat(core): clone commands and actions when binding runtimes
Add clone support across Action types and Command so commands can be safely registered or runner-bound without mutating the original instances. - clone BaseAction implementations across simple, composite, IO, prompt, file, HTTP, process, and signal actions - bind cloned commands in Falyx.add_command_from_command() and CommandRunner - preserve local never_prompt settings when cloning actions - rename shared runtime state from options to options_manager for consistency - seed root and execution option namespaces consistently - apply scoped root and namespace option overrides during routing and dispatch - improve namespace completion by delegating option suggestions to FalyxParser - enrich missing-value errors and error hints
This commit is contained in:
121
tests/test_falyx/test_completion_contract.py
Normal file
121
tests/test_falyx/test_completion_contract.py
Normal file
@@ -0,0 +1,121 @@
|
||||
from prompt_toolkit.document import Document
|
||||
|
||||
from falyx import Falyx
|
||||
from falyx.completer import FalyxCompleter
|
||||
from falyx.parser import CommandArgumentParser
|
||||
|
||||
|
||||
def completion_texts(completions) -> list[str]:
|
||||
return [c.text for c in completions]
|
||||
|
||||
|
||||
def make_completion_app() -> tuple[Falyx, FalyxCompleter]:
|
||||
flx = Falyx(program="falyx")
|
||||
|
||||
flx.add_option(
|
||||
"--profile",
|
||||
suggestions=["dev", "prod", "staging"],
|
||||
help="Runtime profile",
|
||||
)
|
||||
|
||||
flx.add_option(
|
||||
"--region",
|
||||
choices=["us-east", "us-west"],
|
||||
help="Deployment region",
|
||||
)
|
||||
|
||||
parser = CommandArgumentParser()
|
||||
parser.add_argument("--name")
|
||||
parser.add_argument("--env", choices=["dev", "prod"])
|
||||
|
||||
flx.add_command(
|
||||
key="D",
|
||||
description="Deploy",
|
||||
action=lambda name, env: f"deploy {name} to {env}",
|
||||
aliases=["deploy"],
|
||||
arg_parser=parser,
|
||||
)
|
||||
|
||||
return flx, FalyxCompleter(flx)
|
||||
|
||||
|
||||
def test_completion_suggests_namespace_flags():
|
||||
_, completer = make_completion_app()
|
||||
|
||||
completions = list(
|
||||
completer.get_completions(Document(text="--pr", cursor_position=4), None)
|
||||
)
|
||||
|
||||
texts = completion_texts(completions)
|
||||
assert "--profile" in texts
|
||||
|
||||
|
||||
def test_completion_suggests_namespace_option_values():
|
||||
_, completer = make_completion_app()
|
||||
|
||||
completions = list(
|
||||
completer.get_completions(
|
||||
Document(text="--profile pr", cursor_position=len("--profile pr")),
|
||||
None,
|
||||
)
|
||||
)
|
||||
|
||||
texts = completion_texts(completions)
|
||||
assert "prod" in texts
|
||||
assert "dev" not in texts
|
||||
|
||||
|
||||
def test_completion_after_committed_namespace_option_returns_namespace_entries():
|
||||
_, completer = make_completion_app()
|
||||
|
||||
completions = list(
|
||||
completer.get_completions(
|
||||
Document(text="--profile prod de", cursor_position=len("--profile prod de")),
|
||||
None,
|
||||
)
|
||||
)
|
||||
|
||||
texts = completion_texts(completions)
|
||||
assert "deploy" in texts
|
||||
|
||||
|
||||
def test_completion_preview_mode_prefixes_namespace_entry_suggestions():
|
||||
_, completer = make_completion_app()
|
||||
|
||||
completions = list(
|
||||
completer.get_completions(Document(text="?de", cursor_position=3), None)
|
||||
)
|
||||
|
||||
texts = completion_texts(completions)
|
||||
assert "?deploy" in texts
|
||||
|
||||
|
||||
def test_resolve_completion_route_unresolved_entry_with_trailing_input_stops_namespace_entry_mode():
|
||||
flx, _ = make_completion_app()
|
||||
|
||||
route = flx.resolve_completion_route(
|
||||
["wat"],
|
||||
stub="--na",
|
||||
cursor_at_end_of_token=False,
|
||||
invocation_context=flx.get_current_invocation_context(),
|
||||
is_preview=False,
|
||||
)
|
||||
|
||||
assert route.command is None
|
||||
assert route.expecting_entry is False
|
||||
assert route.remaining_argv == ["wat", "--na"]
|
||||
assert route.stub == ""
|
||||
|
||||
|
||||
def test_completion_delegates_to_command_parser_after_leaf_command_is_resolved():
|
||||
_, completer = make_completion_app()
|
||||
|
||||
completions = list(
|
||||
completer.get_completions(
|
||||
Document(text="D --na", cursor_position=len("D --na")),
|
||||
None,
|
||||
)
|
||||
)
|
||||
|
||||
texts = completion_texts(completions)
|
||||
assert "--name" in texts
|
||||
Reference in New Issue
Block a user