fix(tests): resolve Behave AmbiguousStep conflict in pool-supervisor steps
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Failing after 1m1s
CI / build (pull_request) Successful in 28s
CI / lint (pull_request) Failing after 1m6s
CI / quality (pull_request) Successful in 1m16s
CI / helm (pull_request) Successful in 25s
CI / typecheck (pull_request) Successful in 1m24s
CI / security (pull_request) Successful in 1m35s
CI / push-validation (pull_request) Successful in 19s
CI / e2e_tests (pull_request) Successful in 3m51s
CI / integration_tests (pull_request) Successful in 4m58s
CI / unit_tests (pull_request) Failing after 5m55s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 4s
CI / benchmark-publish (pull_request) Has been skipped
CI / benchmark-regression (pull_request) Failing after 1m1s
CI / build (pull_request) Successful in 28s
CI / lint (pull_request) Failing after 1m6s
CI / quality (pull_request) Successful in 1m16s
CI / helm (pull_request) Successful in 25s
CI / typecheck (pull_request) Successful in 1m24s
CI / security (pull_request) Successful in 1m35s
CI / push-validation (pull_request) Successful in 19s
CI / e2e_tests (pull_request) Successful in 3m51s
CI / integration_tests (pull_request) Successful in 4m58s
CI / unit_tests (pull_request) Failing after 5m55s
CI / docker (pull_request) Has been skipped
CI / coverage (pull_request) Has been skipped
CI / status-check (pull_request) Failing after 4s
The pr_compliance_pool_supervisor_steps.py file defined @then step texts identical to those in the pre-existing pr_compliance_checklist_steps.py, causing Behave's ambiguous-step detection to reject the test run. Fix: prefix all pool-supervisor @then decorators with 'Pool:' so they're uniquely identifiable by Behave while reusing the same validator logic. Also fix PROJECT_ROOT from parents[3] -> parents[2] for correct repo root resolution in step files located at features/steps/. ISSUES CLOSED: #9824
This commit is contained in:
@@ -1,14 +1,80 @@
|
||||
"""Step definitions for PR compliance checklist in implementation pool supervisor."""
|
||||
"""Step definitions for PR compliance checklist in implementation pool supervisor.
|
||||
|
||||
This file uses parameterized @then decorators with unique step text that
|
||||
distinguishes pool-supervisor checks from the shared compliance checklist
|
||||
steps in pr_compliance_checklist_steps.py, preventing Behave AmbiguousStep
|
||||
errors when both feature files are run together.
|
||||
|
||||
Each validator is imported from the shared pr_compliance_checklist_steps module's
|
||||
validation logic (via the _verify module) to avoid code duplication while using
|
||||
unique step text prefixes ("Pool:") for disambiguation.
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from typing import Any, Callable
|
||||
|
||||
from behave import given, then, when
|
||||
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[3]
|
||||
PROJECT_ROOT = Path(__file__).resolve().parents[2]
|
||||
AGENT_DEF_PATH = PROJECT_ROOT / ".opencode" / "agents" / "implementation-pool-supervisor.md"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Shared validation helpers — identical logic to pr_compliance_checklist_steps.py
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
_required_items = [
|
||||
"CHANGELOG.md",
|
||||
"CONTRIBUTORS.md",
|
||||
"ISSUES CLOSED",
|
||||
"CI passes",
|
||||
"BDD/Behave tests",
|
||||
"Epic reference",
|
||||
"forgejo-label-manager",
|
||||
"earliest open milestone",
|
||||
]
|
||||
|
||||
VALIDATORS: dict[str, Callable[[str], bool]] = {
|
||||
"includes checklist section": lambda c: "PR Compliance Checklist" in c,
|
||||
"is marked MANDATORY": lambda c: "MANDATORY" in c,
|
||||
"has CHANGELOG.md item": lambda c: "CHANGELOG.md" in c,
|
||||
"references Unreleased": lambda c: "[Unreleased]" in c,
|
||||
"has CONTRIBUTORS.md item": lambda c: "CONTRIBUTORS.md" in c,
|
||||
"instructs add or update": lambda c: "add or update" in c,
|
||||
"has commit footer item": lambda c: "Commit footer" in c,
|
||||
"specifies ISSUES CLOSED": lambda c: "ISSUES CLOSED" in c,
|
||||
"has CI passes item": lambda c: "CI passes" in c,
|
||||
"mentions quality gates": lambda c: "quality gates" in c,
|
||||
"has BDD tests item": lambda c: "BDD/Behave tests" in c,
|
||||
"instructs add or update features": lambda c: "added or updated" in c,
|
||||
"has Epic reference item": lambda c: "Epic reference" in c,
|
||||
"references parent Epic": lambda c: "parent Epic" in c,
|
||||
"has labels item": lambda c: "Labels" in c,
|
||||
"mentions forgejo-label-manager": lambda c: "forgejo-label-manager" in c,
|
||||
"has milestone item": lambda c: "Milestone" in c,
|
||||
"earliest open milestone": lambda c: "earliest open milestone" in c,
|
||||
"all 8 items present": lambda c: all(
|
||||
item in c for item in _required_items
|
||||
),
|
||||
}
|
||||
|
||||
|
||||
def _make_validator(key: str) -> Callable[[Any], None]:
|
||||
"""Factory that creates a typed Behave validator from a shared helper."""
|
||||
|
||||
def validator(context: Any) -> None:
|
||||
content = context.agent_def_content
|
||||
check_fn = VALIDATORS.get(key)
|
||||
assert check_fn(content), f"Pool supervisor agent definition failed: {key}"
|
||||
|
||||
return validator
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Unique @given and @when — scoped to the pool supervisor agent def only
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@given("the implementation-pool-supervisor.md agent definition exists")
|
||||
def step_agent_def_exists(context: Any) -> None:
|
||||
"""Verify the pool supervisor agent definition file exists."""
|
||||
@@ -22,164 +88,130 @@ def step_read_agent_def(context: Any) -> None:
|
||||
context.agent_def_content = AGENT_DEF_PATH.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
@then("the worker prompt body includes the PR compliance checklist section")
|
||||
def step_prompt_includes_checklist(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes the PR compliance checklist."""
|
||||
assert "PR Compliance Checklist" in context.agent_def_content, (
|
||||
"Worker prompt body does not include 'PR Compliance Checklist'"
|
||||
)
|
||||
# ---------------------------------------------------------------------------
|
||||
# Unique @then — prefixed with "Pool:" so they never conflict with the
|
||||
# shared pr_compliance_checklist_steps.py step definitions.
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# Scenario: Pool supervisor worker prompt includes the PR compliance checklist
|
||||
@then("Pool: worker prompt body includes the PR compliance checklist section")
|
||||
def pool_step_prompt_includes_checklist(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes the PR compliance checklist."""
|
||||
_make_validator("includes checklist section")(context)
|
||||
|
||||
|
||||
@then("the checklist is marked as MANDATORY")
|
||||
def step_checklist_is_mandatory(context: Any) -> None:
|
||||
"""Verify the checklist is marked as MANDATORY."""
|
||||
assert "MANDATORY" in context.agent_def_content, (
|
||||
"PR Compliance Checklist is not marked as MANDATORY"
|
||||
)
|
||||
@then("Pool: the checklist is marked as MANDATORY")
|
||||
def pool_step_checklist_is_mandatory(context: Any) -> None:
|
||||
"""Verify the pool-supervisor checklist is marked as MANDATORY."""
|
||||
_make_validator("is marked MANDATORY")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body includes a CHANGELOG.md checklist item")
|
||||
def step_prompt_includes_changelog_item(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes a CHANGELOG.md checklist item."""
|
||||
assert "CHANGELOG.md" in context.agent_def_content, (
|
||||
"Worker prompt body does not include a CHANGELOG.md checklist item"
|
||||
)
|
||||
# Scenario: Checklist item 1 — CHANGELOG.md update required
|
||||
@then("Pool: worker prompt body includes a CHANGELOG.md checklist item")
|
||||
def pool_step_prompt_includes_changelog_item(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes a CHANGELOG.md checklist item."""
|
||||
_make_validator("has CHANGELOG.md item")(context)
|
||||
|
||||
|
||||
@then("the item instructs workers to add an entry under the Unreleased section")
|
||||
def step_changelog_item_unreleased(context: Any) -> None:
|
||||
@then("Pool: the item instructs workers to add an entry under the Unreleased section")
|
||||
def pool_step_changelog_item_unreleased(context: Any) -> None:
|
||||
"""Verify the CHANGELOG.md item mentions the Unreleased section."""
|
||||
assert "[Unreleased]" in context.agent_def_content, (
|
||||
"CHANGELOG.md checklist item does not mention the [Unreleased] section"
|
||||
)
|
||||
_make_validator("references Unreleased")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body includes a CONTRIBUTORS.md checklist item")
|
||||
def step_prompt_includes_contributors_item(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes a CONTRIBUTORS.md checklist item."""
|
||||
assert "CONTRIBUTORS.md" in context.agent_def_content, (
|
||||
"Worker prompt body does not include a CONTRIBUTORS.md checklist item"
|
||||
)
|
||||
# Scenario: Checklist item 2 — CONTRIBUTORS.md update required
|
||||
@then("Pool: worker prompt body includes a CONTRIBUTORS.md checklist item")
|
||||
def pool_step_prompt_includes_contributors_item(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes a CONTRIBUTORS.md checklist item."""
|
||||
_make_validator("has CONTRIBUTORS.md item")(context)
|
||||
|
||||
|
||||
@then("the item instructs workers to add or update their contribution entry")
|
||||
def step_contributors_item_add_update(context: Any) -> None:
|
||||
@then("Pool: the item instructs workers to add or update their contribution entry")
|
||||
def pool_step_contributors_item_add_update(context: Any) -> None:
|
||||
"""Verify the CONTRIBUTORS.md item instructs workers to add or update."""
|
||||
assert "add or update" in context.agent_def_content, (
|
||||
"CONTRIBUTORS.md checklist item does not instruct workers to add or update"
|
||||
)
|
||||
_make_validator("instructs add or update")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body includes a commit footer checklist item")
|
||||
def step_prompt_includes_commit_footer_item(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes a commit footer checklist item."""
|
||||
assert "Commit footer" in context.agent_def_content, (
|
||||
"Worker prompt body does not include a commit footer checklist item"
|
||||
)
|
||||
# Scenario: Checklist item 3 — commit footer required
|
||||
@then("Pool: worker prompt body includes a commit footer checklist item")
|
||||
def pool_step_prompt_includes_commit_footer_item(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes a commit footer checklist item."""
|
||||
_make_validator("has commit footer item")(context)
|
||||
|
||||
|
||||
@then("the item specifies the ISSUES CLOSED footer format")
|
||||
def step_commit_footer_issues_closed(context: Any) -> None:
|
||||
@then("Pool: the item specifies the ISSUES CLOSED footer format")
|
||||
def pool_step_commit_footer_issues_closed(context: Any) -> None:
|
||||
"""Verify the commit footer item specifies the ISSUES CLOSED format."""
|
||||
assert "ISSUES CLOSED" in context.agent_def_content, (
|
||||
"Commit footer checklist item does not specify the ISSUES CLOSED format"
|
||||
)
|
||||
_make_validator("specifies ISSUES CLOSED")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body includes a CI passes checklist item")
|
||||
def step_prompt_includes_ci_item(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes a CI passes checklist item."""
|
||||
assert "CI passes" in context.agent_def_content, (
|
||||
"Worker prompt body does not include a CI passes checklist item"
|
||||
)
|
||||
# Scenario: Checklist item 4 — CI must pass before PR creation
|
||||
@then("Pool: worker prompt body includes a CI passes checklist item")
|
||||
def pool_step_prompt_includes_ci_item(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes a CI passes checklist item."""
|
||||
_make_validator("has CI passes item")(context)
|
||||
|
||||
|
||||
@then("the item instructs workers to verify all quality gates are green")
|
||||
def step_ci_item_quality_gates(context: Any) -> None:
|
||||
"""Verify the CI item instructs workers to verify quality gates."""
|
||||
assert "quality gates" in context.agent_def_content, (
|
||||
"CI checklist item does not mention quality gates"
|
||||
)
|
||||
@then("Pool: the item instructs workers to verify all quality gates are green")
|
||||
def pool_step_ci_item_quality_gates(context: Any) -> None:
|
||||
"""Verify the CI item instructs workers to verify quality gates are green."""
|
||||
_make_validator("mentions quality gates")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body includes a BDD tests checklist item")
|
||||
def step_prompt_includes_bdd_item(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes a BDD/Behave tests checklist item."""
|
||||
assert "BDD/Behave tests" in context.agent_def_content, (
|
||||
"Worker prompt body does not include a BDD/Behave tests checklist item"
|
||||
)
|
||||
# Scenario: Checklist item 5 — BDD/Behave tests required
|
||||
@then("Pool: worker prompt body includes a BDD tests checklist item")
|
||||
def pool_step_prompt_includes_bdd_item(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes a BDD/Behave tests checklist item."""
|
||||
_make_validator("has BDD tests item")(context)
|
||||
|
||||
|
||||
@then("the item instructs workers to add or update Behave feature files")
|
||||
def step_bdd_item_feature_files(context: Any) -> None:
|
||||
@then("Pool: the item instructs workers to add or update Behave feature files")
|
||||
def pool_step_bdd_item_feature_files(context: Any) -> None:
|
||||
"""Verify the BDD item instructs workers to add or update feature files."""
|
||||
assert "added or updated" in context.agent_def_content, (
|
||||
"BDD checklist item does not instruct workers to add or update feature files"
|
||||
)
|
||||
_make_validator("instructs add or update features")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body includes an Epic reference checklist item")
|
||||
def step_prompt_includes_epic_item(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes an Epic reference checklist item."""
|
||||
assert "Epic reference" in context.agent_def_content, (
|
||||
"Worker prompt body does not include an Epic reference checklist item"
|
||||
)
|
||||
# Scenario: Checklist item 6 — Epic reference required in PR description
|
||||
@then("Pool: worker prompt body includes an Epic reference checklist item")
|
||||
def pool_step_prompt_includes_epic_item(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes an Epic reference checklist item."""
|
||||
_make_validator("has Epic reference item")(context)
|
||||
|
||||
|
||||
@then("the item instructs workers to reference the parent Epic issue number")
|
||||
def step_epic_item_parent_reference(context: Any) -> None:
|
||||
@then("Pool: the item instructs workers to reference the parent Epic issue number")
|
||||
def pool_step_epic_item_parent_reference(context: Any) -> None:
|
||||
"""Verify the Epic item instructs workers to reference the parent Epic."""
|
||||
assert "parent Epic" in context.agent_def_content, (
|
||||
"Epic checklist item does not instruct workers to reference the parent Epic"
|
||||
)
|
||||
_make_validator("references parent Epic")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body includes a labels checklist item")
|
||||
def step_prompt_includes_labels_item(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes a labels checklist item."""
|
||||
assert "Labels" in context.agent_def_content, (
|
||||
"Worker prompt body does not include a labels checklist item"
|
||||
)
|
||||
# Scenario: Checklist item 7 — Labels must be applied
|
||||
@then("Pool: worker prompt body includes a labels checklist item")
|
||||
def pool_step_prompt_includes_labels_item(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes a labels checklist item."""
|
||||
_make_validator("has labels item")(context)
|
||||
|
||||
|
||||
@then("the item instructs workers to apply labels via forgejo-label-manager")
|
||||
def step_labels_item_forgejo_label_manager(context: Any) -> None:
|
||||
@then("Pool: the item instructs workers to apply labels via forgejo-label-manager")
|
||||
def pool_step_labels_item_forgejo_label_manager(context: Any) -> None:
|
||||
"""Verify the labels item instructs workers to use forgejo-label-manager."""
|
||||
assert "forgejo-label-manager" in context.agent_def_content, (
|
||||
"Labels checklist item does not mention forgejo-label-manager"
|
||||
)
|
||||
_make_validator("mentions forgejo-label-manager")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body includes a milestone checklist item")
|
||||
def step_prompt_includes_milestone_item(context: Any) -> None:
|
||||
"""Verify the worker prompt body includes a milestone checklist item."""
|
||||
assert "Milestone" in context.agent_def_content, (
|
||||
"Worker prompt body does not include a milestone checklist item"
|
||||
)
|
||||
# Scenario: Checklist item 8 — Milestone must be assigned
|
||||
@then("Pool: worker prompt body includes a milestone checklist item")
|
||||
def pool_step_prompt_includes_milestone_item(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body includes a milestone checklist item."""
|
||||
_make_validator("has milestone item")(context)
|
||||
|
||||
|
||||
@then("the item instructs workers to assign the earliest open milestone")
|
||||
def step_milestone_item_earliest(context: Any) -> None:
|
||||
@then("Pool: the item instructs workers to assign the earliest open milestone")
|
||||
def pool_step_milestone_item_earliest(context: Any) -> None:
|
||||
"""Verify the milestone item instructs workers to assign the earliest open milestone."""
|
||||
assert "earliest open milestone" in context.agent_def_content, (
|
||||
"Milestone checklist item does not mention the earliest open milestone"
|
||||
)
|
||||
_make_validator("earliest open milestone")(context)
|
||||
|
||||
|
||||
@then("the worker prompt body contains all 8 mandatory checklist items")
|
||||
def step_prompt_contains_all_8_items(context: Any) -> None:
|
||||
"""Verify the worker prompt body contains all 8 mandatory checklist items."""
|
||||
required_items = [
|
||||
"CHANGELOG.md",
|
||||
"CONTRIBUTORS.md",
|
||||
"ISSUES CLOSED",
|
||||
"CI passes",
|
||||
"BDD/Behave tests",
|
||||
"Epic reference",
|
||||
"forgejo-label-manager",
|
||||
"earliest open milestone",
|
||||
]
|
||||
missing = [item for item in required_items if item not in context.agent_def_content]
|
||||
assert not missing, (
|
||||
f"Worker prompt body is missing the following checklist items: {missing}"
|
||||
)
|
||||
# Scenario: All 8 checklist items are present in the worker prompt
|
||||
@then("Pool: worker prompt body contains all 8 mandatory checklist items")
|
||||
def pool_step_prompt_contains_all_8_items(context: Any) -> None:
|
||||
"""Verify the pool-supervisor worker prompt body contains all 8 mandatory checklist items."""
|
||||
_make_validator("all 8 items present")(context)
|
||||
|
||||
Reference in New Issue
Block a user