Public Access
fix: skip spec-driven workflow tests when sibling repos not checked out
Add _skip_if_repo_missing guard to skill tests that reference sibling repos (infra, grm, sso-bridge). In CI, only devx is checked out, so these tests skip instead of failing with FileNotFoundError. Generated with [Devin](https://devin.ai) Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
This commit is contained in:
co-authored by
Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>
parent
aefc22de57
commit
83ad963193
@@ -36,6 +36,22 @@ def _load_workflow(repo_path: Path, filename: str) -> dict:
|
||||
return yaml.safe_load(f)
|
||||
|
||||
|
||||
def _skip_if_repo_missing(repo_name: str) -> None:
|
||||
"""Skip test if the sibling repo directory doesn't exist (CI only checks out one repo)."""
|
||||
repo_path = _OBLACHNO_ROOT / repo_name
|
||||
if not repo_path.is_dir():
|
||||
pytest.skip(f"Repo {repo_name} not found at {repo_path} (CI only checks out devx)")
|
||||
|
||||
|
||||
def _read_skill(repo_name: str, skill_name: str) -> str:
|
||||
"""Read a skill file from a repo, skipping if the repo or file doesn't exist."""
|
||||
_skip_if_repo_missing(repo_name)
|
||||
skill_path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / skill_name / "SKILL.md"
|
||||
if not skill_path.exists():
|
||||
pytest.skip(f"SKILL.md not found in {repo_name}/{skill_name}")
|
||||
return skill_path.read_text(encoding="utf-8")
|
||||
|
||||
|
||||
def _get_step_names(job: dict) -> list[str]:
|
||||
"""Extract step names from a job dict."""
|
||||
names = []
|
||||
@@ -426,11 +442,13 @@ class TestSpecDrivenDevelopmentSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", ["infra", "grm", "sso-bridge", "devx"])
|
||||
def test_skill_exists_in_repo(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
skill_path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "spec-driven-development" / "SKILL.md"
|
||||
assert skill_path.exists(), f"SKILL.md not found in {repo_name}"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", ["infra", "grm", "sso-bridge", "devx"])
|
||||
def test_skill_has_required_sections(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
skill_path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "spec-driven-development" / "SKILL.md"
|
||||
content = skill_path.read_text(encoding="utf-8")
|
||||
for section in self.REQUIRED_SECTIONS:
|
||||
@@ -438,18 +456,21 @@ class TestSpecDrivenDevelopmentSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", ["infra", "grm", "sso-bridge", "devx"])
|
||||
def test_skill_mentions_req_ids(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
skill_path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "spec-driven-development" / "SKILL.md"
|
||||
content = skill_path.read_text(encoding="utf-8")
|
||||
assert "REQ-" in content, f"SKILL.md in {repo_name} must mention REQ-ID format"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", ["infra", "grm", "sso-bridge", "devx"])
|
||||
def test_skill_mentions_pr_size_limit(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
skill_path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "spec-driven-development" / "SKILL.md"
|
||||
content = skill_path.read_text(encoding="utf-8")
|
||||
assert "500" in content, f"SKILL.md in {repo_name} must mention 500 line PR size limit"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", ["infra", "grm", "sso-bridge", "devx"])
|
||||
def test_skill_mentions_nightly_gate(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
skill_path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "spec-driven-development" / "SKILL.md"
|
||||
content = skill_path.read_text(encoding="utf-8")
|
||||
assert "nightly" in content.lower(), f"SKILL.md in {repo_name} must mention nightly gate"
|
||||
@@ -470,23 +491,27 @@ class TestDevxWorkflowSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_skill_exists(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "devx-workflow" / "SKILL.md"
|
||||
assert path.exists(), f"devx-workflow SKILL.md not found in {repo_name}"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_spec_validation(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "devx-workflow" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "validate_spec" in content, f"devx-workflow skill in {repo_name} must mention validate_spec"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_pr_size_check(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "devx-workflow" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "check_pr_size" in content, f"devx-workflow skill in {repo_name} must mention check_pr_size"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_pr_workflow_commands(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "devx-workflow" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "make create-pr" in content or "make push-with-pr" in content, (
|
||||
@@ -495,6 +520,7 @@ class TestDevxWorkflowSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_auto_merge(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "devx-workflow" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "auto-merge" in content.lower() or "ready-to-merge" in content, (
|
||||
@@ -503,6 +529,7 @@ class TestDevxWorkflowSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_has_correct_task_prefix(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
"""Each repo's devx-workflow skill must mention its correct task prefix."""
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "devx-workflow" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
@@ -559,11 +586,13 @@ class TestTestingAndDebuggingSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", ALL_REPOS)
|
||||
def test_skill_exists(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "testing-and-debugging" / "SKILL.md"
|
||||
assert path.exists(), f"testing-and-debugging SKILL.md not found in {repo_name}"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", ALL_REPOS)
|
||||
def test_has_required_sections(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "testing-and-debugging" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
# All testing-and-debugging skills should have a CI failure investigation section
|
||||
@@ -574,6 +603,7 @@ class TestTestingAndDebuggingSkill:
|
||||
# Repos with PR workflow should mention spec-driven workflow
|
||||
@pytest.mark.parametrize("repo_name", ["infra", "grm", "sso-bridge", "devx"])
|
||||
def test_mentions_spec_driven_workflow(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "testing-and-debugging" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "spec" in content.lower(), (
|
||||
@@ -612,11 +642,13 @@ class TestPrReviewSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_skill_exists(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "pr-review" / "SKILL.md"
|
||||
assert path.exists(), f"pr-review SKILL.md not found in {repo_name}"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_all_review_categories(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "pr-review" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
required_categories = [
|
||||
@@ -634,6 +666,7 @@ class TestPrReviewSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_auto_fix(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "pr-review" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "auto-fix" in content.lower() or "auto fix" in content.lower(), (
|
||||
@@ -642,30 +675,35 @@ class TestPrReviewSkill:
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_gitea_mcp(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "pr-review" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "mcp" in content.lower(), f"pr-review skill in {repo_name} must mention Gitea MCP"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_inline_comments(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "pr-review" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "inline" in content.lower(), f"pr-review skill in {repo_name} must mention inline comments"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_ready_to_merge(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "pr-review" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "ready-to-merge" in content, f"pr-review skill in {repo_name} must mention ready-to-merge label"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_resolve_discussion(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "pr-review" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "resolve" in content.lower(), f"pr-review skill in {repo_name} must mention resolving discussions"
|
||||
|
||||
@pytest.mark.parametrize("repo_name", REPOS_WITH_PR_WORKFLOW)
|
||||
def test_mentions_summary(self, repo_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / "pr-review" / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
assert "summary" in content.lower(), f"pr-review skill in {repo_name} must mention posting a summary"
|
||||
@@ -726,6 +764,7 @@ class TestSkillConsistency:
|
||||
|
||||
@pytest.mark.parametrize("repo_name, skill_name", ALL_SKILLS)
|
||||
def test_skill_has_title(self, repo_name: str, skill_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / skill_name / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
first_line = content.strip().split("\n")[0]
|
||||
@@ -733,12 +772,14 @@ class TestSkillConsistency:
|
||||
|
||||
@pytest.mark.parametrize("repo_name, skill_name", ALL_SKILLS)
|
||||
def test_skill_not_empty(self, repo_name: str, skill_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / skill_name / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8").strip()
|
||||
assert len(content) > 100, f"{repo_name}/{skill_name}: SKILL.md is too short ({len(content)} chars)"
|
||||
|
||||
@pytest.mark.parametrize("repo_name, skill_name", ALL_SKILLS)
|
||||
def test_skill_has_sections(self, repo_name: str, skill_name: str) -> None:
|
||||
_skip_if_repo_missing(repo_name)
|
||||
path = _OBLACHNO_ROOT / repo_name / ".devin" / "skills" / skill_name / "SKILL.md"
|
||||
content = path.read_text(encoding="utf-8")
|
||||
# Must have at least 2 ## sections
|
||||
|
||||
Reference in New Issue
Block a user