"""Step definitions for Plan Preflight Guardrail coverage-boost scenarios. Targets the uncovered lines identified in coverage.xml: - Lines 52-53: PreflightCheckResult.__repr__ - Lines 86-90: PreflightReport.format_report - Lines 166-169: check_action_schema with action_name=None - Line 221: check_skill_tool_existence with missing skills """ from __future__ import annotations from behave import given, then, when from behave.runner import Context from cleveragents.application.services.plan_preflight_guardrail import ( PlanPreflightGuardrail, PreflightCheckName, PreflightCheckResult, PreflightRejection, PreflightReport, ) # --------------------------------------------------------------------------- # Givens # --------------------------------------------------------------------------- @given("a passing PreflightCheckResult for action_schema_validation") def step_given_passing_result(context: Context) -> None: context.cb_check_result = PreflightCheckResult( PreflightCheckName.ACTION_SCHEMA, True, "Action 'deploy' found and valid", ) @given('a failing PreflightCheckResult for actor_availability with message "{msg}"') def step_given_failing_result(context: Context, msg: str) -> None: context.cb_check_result = PreflightCheckResult( PreflightCheckName.ACTOR_AVAILABILITY, False, msg, ) @given("a PreflightReport with one passing and one failing check result") def step_given_mixed_report(context: Context) -> None: report = PreflightReport() report.add( PreflightCheckResult( PreflightCheckName.ACTION_SCHEMA, True, "Action 'deploy' found and valid", ) ) report.add( PreflightCheckResult( PreflightCheckName.ACTOR_AVAILABILITY, False, "Missing actor roles: estimation", ) ) context.cb_report = report @given("a PreflightReport with only passing check results") def step_given_all_pass_report(context: Context) -> None: report = PreflightReport() report.add( PreflightCheckResult( PreflightCheckName.ACTION_SCHEMA, True, "Action 'deploy' found and valid", ) ) report.add( PreflightCheckResult( PreflightCheckName.AUTOMATION_POLICY, True, "Automation profile permits execution", ) ) context.cb_report = report @given("a fresh plan preflight guardrail instance") def step_given_fresh_guardrail(context: Context) -> None: context.cb_guardrail = PlanPreflightGuardrail() context.cb_check_result = None context.cb_rejection = None context.cb_skill_registry: dict[str, object] = {} @given('a skill registry containing only "{name}"') def step_given_skill_registry_one(context: Context, name: str) -> None: context.cb_skill_registry = {name: {"name": name}} @given( "a fresh plan preflight guardrail instance with all registries populated but no action name" ) def step_given_guardrail_no_action_name(context: Context) -> None: context.cb_guardrail = PlanPreflightGuardrail() context.cb_actor_registry = { role: {"role": role} for role in PlanPreflightGuardrail.ACTOR_ROLES } context.cb_automation_profile = {"name": "auto"} context.cb_rejection = None # --------------------------------------------------------------------------- # Whens # --------------------------------------------------------------------------- @when("I get the repr of the check result") def step_when_get_repr(context: Context) -> None: context.cb_repr_str = repr(context.cb_check_result) @when("I format the report") def step_when_format_report(context: Context) -> None: context.cb_formatted = context.cb_report.format_report() @when("I check action schema with no action name") def step_when_check_action_none(context: Context) -> None: context.cb_check_result = context.cb_guardrail.check_action_schema( None, {"some-action": {"name": "some-action"}} ) @when('I check skill/tool existence for skills "{skills_csv}"') def step_when_check_skills(context: Context, skills_csv: str) -> None: skill_names = tuple(s.strip() for s in skills_csv.split(",")) context.cb_check_result = context.cb_guardrail.check_skill_tool_existence( tool_names=(), tool_registry={}, skill_names=skill_names, skill_registry=context.cb_skill_registry, ) @when( 'I check skill/tool existence for tools "{tools}" and skills "{skills}" with empty registries' ) def step_when_check_both_missing(context: Context, tools: str, skills: str) -> None: tool_names = tuple(t.strip() for t in tools.split(",")) skill_names = tuple(s.strip() for s in skills.split(",")) context.cb_check_result = context.cb_guardrail.check_skill_tool_existence( tool_names=tool_names, tool_registry={}, skill_names=skill_names, skill_registry={}, ) @when("I run all preflight checks with action_name as None") def step_when_run_all_none_action(context: Context) -> None: try: context.cb_guardrail.run_all_checks( action_name=None, action_registry={}, actor_registry=context.cb_actor_registry, automation_profile=context.cb_automation_profile, ) context.cb_rejection = None except PreflightRejection as exc: context.cb_rejection = exc # --------------------------------------------------------------------------- # Thens # --------------------------------------------------------------------------- @then('the repr should contain "{text}"') def step_then_repr_contains(context: Context, text: str) -> None: assert text in context.cb_repr_str, ( f"Expected '{text}' in repr, got: {context.cb_repr_str}" ) @then('the formatted report should start with "{prefix}"') def step_then_report_starts_with(context: Context, prefix: str) -> None: assert context.cb_formatted.startswith(prefix), ( f"Expected report to start with '{prefix}', got: {context.cb_formatted[:80]}" ) @then("the formatted report should contain a PASS line") def step_then_report_has_pass(context: Context) -> None: assert "[PASS]" in context.cb_formatted, ( f"Expected [PASS] in report, got:\n{context.cb_formatted}" ) @then("the formatted report should contain a FAIL line") def step_then_report_has_fail(context: Context) -> None: assert "[FAIL]" in context.cb_formatted, ( f"Expected [FAIL] in report, got:\n{context.cb_formatted}" ) @then('the formatted report should not contain "{text}"') def step_then_report_not_contains(context: Context, text: str) -> None: assert text not in context.cb_formatted, ( f"Did not expect '{text}' in report, but found it:\n{context.cb_formatted}" ) @then("the action schema check should fail") def step_then_action_check_fails(context: Context) -> None: result = context.cb_check_result assert result is not None, "No check result recorded" assert result.passed is False, f"Expected FAIL but got PASS: {result.message}" assert result.check == PreflightCheckName.ACTION_SCHEMA @then('the action schema check message should contain "{text}"') def step_then_action_msg_contains(context: Context, text: str) -> None: assert text in context.cb_check_result.message, ( f"Expected '{text}' in message, got: {context.cb_check_result.message}" ) @then("the skill tool check should fail") def step_then_skill_check_fails(context: Context) -> None: result = context.cb_check_result assert result is not None, "No check result recorded" assert result.passed is False, f"Expected FAIL but got PASS: {result.message}" assert result.check == PreflightCheckName.SKILL_TOOL_EXISTENCE @then('the skill tool check message should contain "{text}"') def step_then_skill_msg_contains(context: Context, text: str) -> None: assert text in context.cb_check_result.message, ( f"Expected '{text}' in message, got: {context.cb_check_result.message}" ) @then('a preflight rejection should be raised for "{check_name}"') def step_then_rejection_for_check(context: Context, check_name: str) -> None: assert context.cb_rejection is not None, ( "Expected PreflightRejection but none was raised" ) assert context.cb_rejection.check.value == check_name, ( f"Expected check '{check_name}', got '{context.cb_rejection.check.value}'" ) @then('the preflight rejection message should contain "{text}"') def step_then_rejection_msg_contains(context: Context, text: str) -> None: assert context.cb_rejection is not None, "No rejection recorded" assert text in str(context.cb_rejection), ( f"Expected '{text}' in rejection message, got: {context.cb_rejection}" )