Files
cleveragents-core/features/tdd_expected_fail_infrastructure.feature
hurui200320 f67e8a2e07
CI / lint (pull_request) Successful in 18s
CI / build (pull_request) Successful in 17s
CI / helm (pull_request) Successful in 18s
CI / quality (pull_request) Successful in 53s
CI / typecheck (pull_request) Successful in 56s
CI / security (pull_request) Successful in 57s
CI / push-validation (pull_request) Successful in 40s
CI / unit_tests (pull_request) Successful in 3m13s
CI / integration_tests (pull_request) Successful in 4m23s
CI / e2e_tests (pull_request) Successful in 4m37s
CI / docker (pull_request) Successful in 1m34s
CI / coverage (pull_request) Successful in 10m46s
CI / status-check (pull_request) Successful in 1s
CI / lint (push) Successful in 17s
CI / quality (push) Successful in 17s
CI / build (push) Successful in 24s
CI / helm (push) Successful in 24s
CI / push-validation (push) Successful in 37s
CI / typecheck (push) Successful in 52s
CI / security (push) Successful in 52s
CI / e2e_tests (push) Successful in 3m13s
CI / unit_tests (push) Successful in 6m37s
CI / integration_tests (push) Successful in 6m39s
CI / docker (push) Successful in 1m35s
CI / coverage (push) Successful in 11m13s
CI / status-check (push) Successful in 1s
fix(testing): document and harden non-AssertionError guard in apply_tdd_inversion to reduce flaky CI
Surface the non-AssertionError guard warning in standard Behave output by emitting to stderr in addition to the structured logger, and add infrastructure coverage that asserts this guard path is visible during test runs. Document the @tdd_expected_fail expectation that bug-signaling failures must use AssertionError so infrastructure exceptions are not accidentally treated as expected bug failures.

ISSUES CLOSED: #8294
2026-04-15 13:37:16 +08:00

184 lines
10 KiB
Gherkin

@infrastructure
Feature: TDD expected-fail handler infrastructure
Verify that ``apply_tdd_inversion`` (the production code path called by the
``Scenario.run()`` monkey-patch) and ``handle_tdd_expected_fail`` (the
standalone entry point) correctly invert scenario AND step-level status for
TDD issue-capture tests.
# --- apply_tdd_inversion (production path) ---
Scenario: apply_tdd_inversion inverts a failed scenario with failed and skipped steps to passed
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has a step "broken step" with status "failed"
And the mock scenario has a step "skipped step" with status "skipped"
When apply_tdd_inversion processes the scenario with failed True
Then the scenario status should be "passed"
And the step "broken step" should have status "passed"
And the step "skipped step" should have status "passed"
And the step "broken step" should have error_message cleared
And the step "skipped step" should have error_message cleared
Scenario: apply_tdd_inversion fails a passed scenario that still carries @tdd_expected_fail
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "passed"
And the mock scenario has a step "last step" with status "passed"
When apply_tdd_inversion processes the scenario with failed False
Then the scenario status should be "failed"
And the step "last step" should have a synthetic error_message
Scenario: apply_tdd_inversion does not invert when hook_failed is set
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed" and hook_failed
And the mock scenario has a step "broken step" with status "failed"
When apply_tdd_inversion processes the scenario with failed True
Then the scenario status should be "failed"
And the step "broken step" should have status "failed"
Scenario: apply_tdd_inversion does not invert during dry-run mode
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "passed" and was_dry_run
When apply_tdd_inversion processes the scenario with failed False
Then the apply_tdd_inversion result should be False
Scenario: apply_tdd_inversion does not invert non-AssertionError exceptions
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has an infrastructure-error step "infra step" with exception "RuntimeError"
When apply_tdd_inversion processes the scenario with failed True
Then the scenario status should be "failed"
And the step "infra step" should have status "failed"
Scenario: apply_tdd_inversion surfaces non-AssertionError guard warning to stderr
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has an infrastructure-error step "infra step" with exception "RuntimeError"
When apply_tdd_inversion processes the scenario with failed True while capturing stderr
Then the scenario status should be "failed"
And the step "infra step" should have status "failed"
And the apply_tdd_inversion result should be True
And stderr output should contain "Non-assertion exception in expected-fail scenario"
Scenario: apply_tdd_inversion does not emit guard warning for AssertionError exceptions
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has a step "bug step" with status "failed"
When apply_tdd_inversion processes the scenario with failed True while capturing stderr
Then the scenario status should be "passed"
And the step "bug step" should have status "passed"
And the apply_tdd_inversion result should be False
And stderr output should not contain "Non-assertion exception"
Scenario: apply_tdd_inversion ignores scenarios without @tdd_expected_fail
Given a mock scenario tagged "@tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has a step "broken step" with status "failed"
When apply_tdd_inversion processes the scenario with failed True
Then the scenario status should be "failed"
And the step "broken step" should have status "failed"
# --- handle_tdd_expected_fail (standalone entry point) ---
Scenario: Handler inverts a failed scenario with failed and skipped steps to passed
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has a step "broken step" with status "failed"
And the mock scenario has a step "skipped step" with status "skipped"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "passed"
And the step "broken step" should have status "passed"
And the step "skipped step" should have status "passed"
And the step "broken step" should have error_message cleared
And the step "skipped step" should have error_message cleared
Scenario: Handler fails a passed scenario that still carries @tdd_expected_fail
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "passed"
And the mock scenario has a step "last step" with status "passed"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "failed"
And the step "last step" should have a synthetic error_message
Scenario: Handler rejects @tdd_expected_fail without @tdd_issue
Given a mock scenario tagged "@tdd_expected_fail" with status "failed"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "failed"
Scenario: Handler rejects @tdd_expected_fail without @tdd_issue_N
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue" with status "failed"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "failed"
Scenario: Handler rejects @tdd_issue_N without @tdd_issue unconditionally
Given a mock scenario tagged "@tdd_issue_999" with status "passed"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "failed"
Scenario: Handler ignores scenarios without @tdd_expected_fail
Given a mock scenario tagged "@tdd_issue @tdd_issue_999" with status "failed"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "failed"
Scenario: Handler does not invert non-AssertionError exceptions
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has an infrastructure-error step "infra step" with exception "RuntimeError"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "failed"
And the step "infra step" should have status "failed"
# --- apply_tdd_inversion: mixed exception types (S3) ---
Scenario: apply_tdd_inversion does not invert when mixed exceptions present
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has a step "bug step" with status "failed"
And the mock scenario has an infrastructure-error step "infra step" with exception "RuntimeError"
When apply_tdd_inversion processes the scenario with failed True
Then the scenario status should be "failed"
And the step "bug step" should have status "failed"
And the step "infra step" should have status "failed"
Scenario: Handler does not invert when mixed exceptions present
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has a step "bug step" with status "failed"
And the mock scenario has an infrastructure-error step "infra step" with exception "RuntimeError"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "failed"
And the step "bug step" should have status "failed"
And the step "infra step" should have status "failed"
# --- selective step reset preserves already-passed steps (TC-3) ---
Scenario: apply_tdd_inversion preserves already-passed steps during inversion
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has a step "ok step" with status "passed"
And the mock scenario has a step "broken step" with status "failed"
And the mock scenario has a step "skipped step" with status "skipped"
When apply_tdd_inversion processes the scenario with failed True
Then the scenario status should be "passed"
And the step "ok step" should have status "passed"
And the step "broken step" should have status "passed"
And the step "skipped step" should have status "passed"
And the step "broken step" should have error_message cleared
And the step "skipped step" should have error_message cleared
Scenario: Handler preserves already-passed steps during inversion
Given a mock scenario tagged "@tdd_expected_fail @tdd_issue @tdd_issue_999" with status "failed"
And the mock scenario has a step "ok step" with status "passed"
And the mock scenario has a step "broken step" with status "failed"
And the mock scenario has a step "skipped step" with status "skipped"
When the TDD expected-fail handler processes the scenario
Then the scenario status should be "passed"
And the step "ok step" should have status "passed"
And the step "broken step" should have status "passed"
And the step "skipped step" should have status "passed"
And the step "broken step" should have error_message cleared
And the step "skipped step" should have error_message cleared
# --- before_scenario hook_failed regression test (TC-2) ---
Scenario: before_scenario sets hook_failed on invalid TDD tags
Given a mock scenario tagged "@tdd_expected_fail" with status "untested"
When before_scenario is called with the mock scenario
Then the scenario hook_failed flag should be True
And the scenario status should be "failed"
# --- _install_tdd_expected_fail_patch (S2) ---
Scenario: _install_tdd_expected_fail_patch sets the patched flag
When the TDD expected-fail patch installation status is checked
Then the Scenario class should have _tdd_run_patched set to True
Scenario: _install_tdd_expected_fail_patch is idempotent
When _install_tdd_expected_fail_patch is called twice
Then the Scenario.run method should not be double-wrapped