fix(validation): normalise --key value option keys and reject consecutive flags
Restore the two behaviours that master had but were lost in this branch: 1. Convert hyphens to underscores in named-option keys before forwarding to the service layer (--coverage-threshold → coverage_threshold). This matches Typer/Click's universal convention and the documented attach_validation(args=...) contract, where keys must be Python- identifier-style strings. 2. Detect a missing value when the next token starts with -- (e.g. "--threshold --strict true" no longer silently sets threshold to the literal string "--strict"; it errors with a clear "Missing value for option" message). Update the Behave step definitions and the Robot helper to assert the underscore-normalised key (coverage_threshold), matching the restored behaviour. The CLI-level option name (--coverage-threshold) is unchanged in the feature file and Robot test — only the dict key the service receives is normalised.
This commit is contained in:
@@ -115,15 +115,19 @@ def step_named_opts_attach_succeeds(context: Context) -> None:
|
||||
def step_named_opts_received_coverage_threshold(
|
||||
context: Context, expected_val: str
|
||||
) -> None:
|
||||
"""Verify the service received coverage-threshold in the args dict."""
|
||||
"""Verify the service received coverage_threshold in the args dict.
|
||||
|
||||
The CLI converts hyphens in option names to underscores when forwarding
|
||||
to the service layer (``--coverage-threshold`` → ``coverage_threshold``).
|
||||
"""
|
||||
call_kwargs = context.named_opts_mock_service.attach_validation.call_args
|
||||
assert call_kwargs is not None, "attach_validation was not called"
|
||||
args_dict = call_kwargs.kwargs.get("args") or {}
|
||||
assert "coverage-threshold" in args_dict, (
|
||||
f"Expected 'coverage-threshold' in args, got: {args_dict}"
|
||||
assert "coverage_threshold" in args_dict, (
|
||||
f"Expected 'coverage_threshold' in args, got: {args_dict}"
|
||||
)
|
||||
assert args_dict["coverage-threshold"] == expected_val, (
|
||||
f"Expected coverage-threshold={expected_val!r}, got {args_dict['coverage-threshold']!r}"
|
||||
assert args_dict["coverage_threshold"] == expected_val, (
|
||||
f"Expected coverage_threshold={expected_val!r}, got {args_dict['coverage_threshold']!r}"
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -82,8 +82,8 @@ def attach_with_coverage_threshold() -> None:
|
||||
sys.exit(1)
|
||||
|
||||
args_dict = call_kwargs.kwargs.get("args") or {}
|
||||
if args_dict.get("coverage-threshold") != "90":
|
||||
print(f"FAIL: expected args={{'coverage-threshold': '90'}}, got {args_dict!r}")
|
||||
if args_dict.get("coverage_threshold") != "90":
|
||||
print(f"FAIL: expected args={{'coverage_threshold': '90'}}, got {args_dict!r}")
|
||||
sys.exit(1)
|
||||
|
||||
print("validation-attach-named-option-coverage-threshold-ok")
|
||||
|
||||
@@ -291,18 +291,19 @@ def attach(
|
||||
) -> None:
|
||||
"""Attach a validation to a resource.
|
||||
|
||||
Extra validation arguments are passed as named options using ``--key value``
|
||||
format (e.g. ``--coverage-threshold 90``). The ``--`` prefix is stripped
|
||||
and the remaining key/value pairs are forwarded to the validation service.
|
||||
|
||||
The validation mode (required or informational) is determined by the
|
||||
validation's registered definition, not overridden at attach time.
|
||||
|
||||
Validation-specific arguments are passed as named options using the
|
||||
``--key value`` format (e.g. ``--coverage-threshold 90``). Hyphens in
|
||||
option names are converted to underscores in the stored argument dict
|
||||
(e.g. ``--coverage-threshold`` becomes ``coverage_threshold``).
|
||||
|
||||
Examples:
|
||||
agents validation attach git-checkout/my-repo local/coverage-check
|
||||
agents validation attach --project myproj git-checkout/my-repo local/lint
|
||||
agents validation attach git-checkout/my-repo local/coverage-check \
|
||||
--coverage-threshold 90
|
||||
agents validation attach --project local/api-service \\
|
||||
local/api-repo local/run-tests --coverage-threshold 90
|
||||
"""
|
||||
try:
|
||||
# Parse extra named options from ctx.args (--key value pairs).
|
||||
@@ -315,15 +316,20 @@ def attach(
|
||||
while i < len(raw_extra):
|
||||
token = raw_extra[i]
|
||||
if token.startswith("--"):
|
||||
key = token[2:] # strip leading "--"
|
||||
key = token[2:].replace(
|
||||
"-", "_"
|
||||
) # --coverage-threshold → coverage_threshold
|
||||
if not key:
|
||||
console.print(
|
||||
"[red]Invalid option:[/red] bare '--' is not allowed"
|
||||
)
|
||||
raise typer.Abort()
|
||||
# Next token is the value
|
||||
if i + 1 >= len(raw_extra):
|
||||
console.print(f"[red]Missing value for option:[/red] {token}")
|
||||
# Next token is the value (must not be another --flag)
|
||||
if i + 1 >= len(raw_extra) or raw_extra[i + 1].startswith("--"):
|
||||
console.print(
|
||||
f"[red]Missing value for option:[/red] {token} "
|
||||
"(expected --key value, e.g. --coverage-threshold 90)"
|
||||
)
|
||||
raise typer.Abort()
|
||||
val = raw_extra[i + 1]
|
||||
extra_args[key] = val
|
||||
|
||||
Reference in New Issue
Block a user