diff --git a/docs/decisions/ADR-001-auto-detect-test-command.md b/docs/decisions/ADR-001-auto-detect-test-command.md deleted file mode 100644 index 81a558a..0000000 --- a/docs/decisions/ADR-001-auto-detect-test-command.md +++ /dev/null @@ -1,71 +0,0 @@ -# ADR-001: Auto-detect test command - -## Status - -**Accepted** (2026-04-11) - -## Context - -The primary friction point in the multi-agent workflow template setup was requiring -users to manually edit `workflow.conf` before git hooks would work. Users would -clone a repository, attempt to push to an issue branch, and encounter cryptic -errors when the pre-push hook tried to run an empty `TEST_CMD`. - -This manual configuration step blocked adoption and created poor first-run experience. - -## Options Considered - -**Option A: Smarter defaults** -Set `TEST_CMD="echo 'No tests configured'"` in template to avoid empty command errors. - -**Option B: Auto-detect from project markers** -Scan for `package.json`, `pyproject.toml`, `go.mod`, etc. at hook runtime and -infer the appropriate test command. - -**Option C: Interactive setup script** -Provide a `setup.sh` script that prompts users for test command and writes -`workflow.conf`. - -**Option D: Agent-driven config** -Have orchestrator/issue agents automatically detect and configure test commands -during repository initialization. - -## Decision - -Implemented **Options A + B combined** with **Option D** as an enhancement: - -1. **Empty default signals auto-detect**: `TEST_CMD=""` in template triggers detection -2. **Runtime detection with write-back**: `.githooks/lib/detect.sh` scans project markers -3. **Agent integration**: Orchestrator Phase A also runs detection on repository init -4. **Option C rejected**: Interactive scripts add complexity and still require manual steps - -### Key Design Choices - -**Write-back vs runtime-only** -Chose write-back to `workflow.conf` for transparency and debuggability. Users can -see what was detected and manually override if needed. - -**Monorepo multiple-marker behavior** -When multiple markers found (e.g., `package.json` + `pyproject.toml`), warn and -skip rather than choosing first. Safety over convenience. - -**Shared library approach** -Created `.githooks/lib/detect.sh` sourced by both local hooks and CI to avoid -code duplication and ensure consistent behavior. - -## Consequences - -**Positive:** -- Fresh clones with standard project structures work immediately -- No manual configuration required for Node.js, Python, Go, Rust, Java projects -- Clear error messages guide users toward resolution -- Transparent auto-detection with manual override capability - -**Negative:** -- Monorepos and unusual project structures still require one manual configuration step -- Additional complexity in hook logic -- Edge cases (multiple test commands, custom runners) need documentation - -**Trade-offs:** -- Prioritized common case convenience over edge case flexibility -- Added detection logic increases maintenance surface but dramatically improves UX \ No newline at end of file diff --git a/docs/orchestrator_init_detection.md b/docs/orchestrator_init_detection.md deleted file mode 100644 index b609ce9..0000000 --- a/docs/orchestrator_init_detection.md +++ /dev/null @@ -1,70 +0,0 @@ -# Orchestrate Phase A Detection - Verification Checklist - -This checklist verifies that the Orchestrate agent correctly detects project types and writes `workflow.conf` during Phase A initialization. - -## Test Scenarios - -### ✓ Scenario 1: Fresh clone with package.json -- **Setup:** Clone with `package.json` and `workflow.conf` containing `TEST_CMD=""` -- **Expected:** Orchestrate Phase A detects Node.js project, sets `TEST_CMD="npm test"` in workflow.conf, commits change -- **Verification:** - - [ ] `workflow.conf` updated with `TEST_CMD="npm test"` - - [ ] Git commit made with message `chore(config): auto-detect test runner as npm test` - - [ ] Orchestrate agent reports what was detected to user -- **Status:** ✅ READY FOR TESTING - Feature implemented in Phase A step 4 - -### ✓ Scenario 2: Fresh clone with pyproject.toml -- **Setup:** Clone with `pyproject.toml` and `workflow.conf` containing `TEST_CMD=""` -- **Expected:** Orchestrate Phase A detects Python project, sets `TEST_CMD="pytest"` in workflow.conf -- **Verification:** - - [ ] `workflow.conf` updated with `TEST_CMD="pytest"` - - [ ] Git commit made with message `chore(config): auto-detect test runner as pytest` - - [ ] Orchestrate agent reports detection to user -- **Status:** ✅ READY FOR TESTING - Feature implemented in Phase A step 4 - -### ✓ Scenario 3: Fresh clone with multiple markers -- **Setup:** Clone with both `package.json` and `pyproject.toml`, `workflow.conf` has `TEST_CMD=""` -- **Expected:** Orchestrate agent warns user about multiple markers, does not write workflow.conf -- **Verification:** - - [ ] Warning message displayed about multiple project markers - - [ ] User asked to set `TEST_CMD` manually in workflow.conf - - [ ] No changes made to workflow.conf - - [ ] No git commits for config changes -- **Status:** ✅ READY FOR TESTING - Feature implemented in Phase A step 4 - -### ✓ Scenario 4: Fresh clone with no markers -- **Setup:** Clone with no project marker files, `workflow.conf` has `TEST_CMD=""` -- **Expected:** Orchestrate agent continues without error, notes config will be set later during scaffolding -- **Verification:** - - [ ] No error thrown - - [ ] Message noting workflow.conf will be configured during scaffolding - - [ ] workflow.conf remains unchanged - - [ ] Phase A continues to next step -- **Status:** ✅ READY FOR TESTING - Feature implemented in Phase A step 4 - -### ✓ Scenario 5: Clone where TEST_CMD already set -- **Setup:** Clone with `workflow.conf` containing `TEST_CMD="make test"` -- **Expected:** Orchestrate agent skips detection entirely, does not override manual configuration -- **Verification:** - - [ ] Detection step skipped - - [ ] workflow.conf remains unchanged - - [ ] No git commits for config changes - - [ ] Phase A continues normally -- **Status:** ✅ READY FOR TESTING - Feature implemented in Phase A step 4 - -## Implementation Requirements - -The Orchestrate agent must add this step to Phase A after branch creation: - -1. Check if `workflow.conf` has `TEST_CMD=""` (empty) -2. If empty, scan for project markers using same priority as `.githooks/lib/detect.sh` -3. Handle single marker: update workflow.conf and commit -4. Handle multiple markers: warn user, ask for manual config -5. Handle no markers: continue with note about later scaffolding -6. Handle existing config: skip detection entirely - -## Notes - -- This is a documentation/instruction change to `.github/agents/orchestrate.agent.md` -- Not executable code that can be unit tested -- Verification requires manual QA following this checklist \ No newline at end of file diff --git a/docs/readme_quickstart.md b/docs/readme_quickstart.md deleted file mode 100644 index 5ff0833..0000000 --- a/docs/readme_quickstart.md +++ /dev/null @@ -1,22 +0,0 @@ -# README Quick Start Verification Checklist - -Manual verification checklist for README.md Quick Start simplification (Issue #6). - -## Acceptance Criteria - -- [x] Quick Start has exactly 2 steps -- [x] Step 2 mentions `@orchestrate` -- [x] No mention of "Edit workflow.conf" in Quick Start or For a new project sections -- [x] `git config core.hooksPath .githooks` appears in developer setup note, not Quick Start -- [x] Configuration subsection explains auto-detection, not manual editing -- [x] No broken links - -## Current Status: PASSING ✅ - -The README.md now meets all acceptance criteria: -- ✅ Quick Start reduced from 4 steps to exactly 2 steps -- ✅ Step 2 mentions `@orchestrate` -- ✅ Removed "Edit `workflow.conf`" from guided path -- ✅ Moved `git config core.hooksPath .githooks` to developer setup note -- ✅ Configuration subsection explains auto-detection instead of manual editing -- ✅ All links verified and working \ No newline at end of file diff --git a/tests/agent_flow.bats b/tests/agent_flow.bats deleted file mode 100644 index d738e15..0000000 --- a/tests/agent_flow.bats +++ /dev/null @@ -1,34 +0,0 @@ -#!/usr/bin/env bats -# Content verification tests for docs/auto/agent-flow.md (issue #48) - -AGENT_FLOW="docs/auto/agent-flow.md" - -@test "flowchart does not contain '+ branch' in the GitHub Issue node" { - run grep -n "create GitHub Issue + branch" "$AGENT_FLOW" - [ "$status" -ne 0 ] -} - -@test "Phase Coordination table Init row does not contain '+ branch'" { - run grep -n "Creates GitHub Issue + branch" "$AGENT_FLOW" - [ "$status" -ne 0 ] -} - -@test "Phase Coordination table Implement row contains 'Creates feature branch'" { - run grep -n "Creates feature branch" "$AGENT_FLOW" - [ "$status" -eq 0 ] -} - -@test "CI failure row contains 'prior retrospective'" { - run grep -n "prior retrospective" "$AGENT_FLOW" - [ "$status" -eq 0 ] -} - -@test "Transition Rules in-progress to review contains 'CI checks are green on the PR'" { - run grep -n "CI checks are green on the PR" "$AGENT_FLOW" - [ "$status" -eq 0 ] -} - -@test "Orchestrate agent description does not mention feature branch in status/draft context" { - run grep -n "status/draft.*feature branch\|feature branch.*status/draft\|and feature branch" "$AGENT_FLOW" - [ "$status" -ne 0 ] -} diff --git a/tests/agent_instructions.bats b/tests/agent_instructions.bats deleted file mode 100644 index 1ca0510..0000000 --- a/tests/agent_instructions.bats +++ /dev/null @@ -1,202 +0,0 @@ -#!/usr/bin/env bats -# Content verification tests for agent instruction files (issue #48) - -REPO_ROOT="/Users/matt/Documents/PounceTek/Developer/GitHub/mpfk/auto" -ORCHESTRATE="$REPO_ROOT/.github/agents/orchestrate.agent.md" -COPILOT_INSTRUCTIONS="$REPO_ROOT/.github/copilot-instructions.md" -DEVELOP="$REPO_ROOT/.github/agents/develop.agent.md" - -# Test 1: orchestrate.agent.md does NOT contain branch creation in Phase A -@test "orchestrate.agent.md: Phase A does not create a feature branch" { - # Phase A ends before Phase B; check that branch creation is not a numbered step in Phase A - # Extract Phase A content (between "## Phase A" and "## Phase B") - phase_a=$(awk '/^## Phase A/,/^## Phase B/' "$ORCHESTRATE") - run echo "$phase_a" - [[ "$output" != *"Create a feature branch"* ]] -} - -# Test 2: orchestrate.agent.md DOES contain Phase C implementation section -@test "orchestrate.agent.md: Phase C implementation section exists" { - grep -q "Phase C: Implementation Handoff" "$ORCHESTRATE" -} - -@test "orchestrate.agent.md: asks whether to assign Copilot develop agent" { - grep -q "Would you like me to assign the Copilot 'develop' Agent to begin work" "$ORCHESTRATE" -} - -# Test 3: copilot-instructions.md does NOT have "Create a feature branch" as step 3 -@test "copilot-instructions.md: branch creation is not step 3 in step-by-step" { - # Step 3 should NOT be "Create a feature branch" - run grep -n "^3\. \*\*Create a feature branch" "$COPILOT_INSTRUCTIONS" - [ "$status" -ne 0 ] -} - -# Test 4: copilot-instructions.md DOES contain retrospective reference for CI failure -@test "copilot-instructions.md: CI failure re-invocation mentions retrospective" { - grep -q "retrospective from the last develop agent run" "$COPILOT_INSTRUCTIONS" -} - -# Test 5: develop.agent.md DOES contain Retrospective Logging section -@test "develop.agent.md: Retrospective Logging section exists" { - grep -q "Retrospective Logging" "$DEVELOP" -} - -# Test 6: develop.agent.md DOES contain add_issue_comment instruction -@test "develop.agent.md: add_issue_comment is referenced in retrospective instructions" { - grep -q "add_issue_comment" "$DEVELOP" -} - -# Test 7: develop.agent.md DOES contain list_issue_comments instruction -@test "develop.agent.md: list_issue_comments is referenced in retrospective instructions" { - grep -q "list_issue_comments" "$DEVELOP" -} - -# Test 8: copilot-instructions.md Phase 1 does NOT mention branch creation -@test "copilot-instructions.md: Phase 1 Init does not mention feature branch" { - phase1=$(awk '/^### Phase 1: Init/,/^### Phase 2:/' "$COPILOT_INSTRUCTIONS") - run echo "$phase1" - [[ "$output" != *"feature branch"* ]] -} - -# Test 9: copilot-instructions.md GitHub-Native Triggers item 6 includes retrospective -@test "copilot-instructions.md: GitHub-Native Triggers CI failure item mentions prior retrospective" { - grep -q "prior retrospective as context" "$COPILOT_INSTRUCTIONS" -} - -# --- Issue #52: MCP tool name and fallback tests --- - -ISSUE_AGENT="$REPO_ROOT/.github/agents/issue.agent.md" -STATE_GUARD="$REPO_ROOT/.github/workflows/issue-state-guard.yml" -ISSUE_NATIVE="$REPO_ROOT/.github/workflows/issue-native-automation.yml" -PR_SYNC="$REPO_ROOT/.github/workflows/pr-issue-sync.yml" -CI_GATE="$REPO_ROOT/.github/workflows/ci-issue-gate.yml" - -# Test: issue.agent.md uses correct MCP tool names (no old names) -@test "issue.agent.md: no references to deprecated create_issue tool name" { - run grep -w "create_issue" "$ISSUE_AGENT" - [ "$status" -ne 0 ] -} - -@test "issue.agent.md: no references to deprecated update_issue tool name" { - run grep -w "update_issue" "$ISSUE_AGENT" - [ "$status" -ne 0 ] -} - -@test "issue.agent.md: no references to deprecated get_issue tool name" { - run grep -w "get_issue" "$ISSUE_AGENT" - [ "$status" -ne 0 ] -} - -@test "issue.agent.md: references issue_write tool" { - grep -q "issue_write" "$ISSUE_AGENT" -} - -@test "issue.agent.md: references issue_read tool" { - grep -q "issue_read" "$ISSUE_AGENT" -} - -# Test: issue.agent.md sets intermediate labels -@test "issue.agent.md: sets status/researching label" { - grep -q "status/researching" "$ISSUE_AGENT" -} - -@test "issue.agent.md: sets status/planning label" { - grep -q "status/planning" "$ISSUE_AGENT" -} - -# Test: issue.agent.md has fallback section -@test "issue.agent.md: has fallback section for update failure" { - grep -q "Fallback" "$ISSUE_AGENT" -} - -@test "issue.agent.md: ready handoff references Copilot develop agent" { - grep -q "Assign to Copilot 'develop' Agent to begin work" "$ISSUE_AGENT" -} - -# Test: orchestrate.agent.md uses correct MCP tool names (no old names) -@test "orchestrate.agent.md: no references to deprecated create_issue tool name" { - run grep -w "create_issue" "$ORCHESTRATE" - [ "$status" -ne 0 ] -} - -@test "orchestrate.agent.md: no references to deprecated update_issue tool name" { - run grep -w "update_issue" "$ORCHESTRATE" - [ "$status" -ne 0 ] -} - -@test "orchestrate.agent.md: no references to deprecated get_issue tool name" { - run grep -w "get_issue" "$ORCHESTRATE" - [ "$status" -ne 0 ] -} - -@test "orchestrate.agent.md: references issue_write tool" { - grep -q "issue_write" "$ORCHESTRATE" -} - -@test "orchestrate.agent.md: includes explicit Gate 1 decision wording" { - grep -q "Gate 1: Approve this plan to move the issue to status/ready" "$ORCHESTRATE" -} - -# Test: issue-state-guard.yml no longer auto-advances draft -> ready (3A) -@test "issue-state-guard.yml: does not auto-advance to status/ready" { - run grep -q "Auto-advanced to status/ready" "$STATE_GUARD" - [ "$status" -ne 0 ] -} - -# Test: assignment path no longer auto-promotes to status/ready -@test "issue-native-automation.yml: copilot assignment does not auto-promote status/ready" { - run grep -q "Plan is present but label wasn't updated — auto-promote" "$ISSUE_NATIVE" - [ "$status" -ne 0 ] -} - -# Test: /auto plan-approved is restricted to maintainers -@test "issue-native-automation.yml: plan approval checks maintainer association" { - grep -q "author_association" "$ISSUE_NATIVE" - grep -q "OWNER" "$ISSUE_NATIVE" - grep -q "MEMBER" "$ISSUE_NATIVE" -} - -# Test: /auto plan-approved validates source status -@test "issue-native-automation.yml: plan approval requires planning or researching status" { - grep -q "status/planning" "$ISSUE_NATIVE" - grep -q "status/researching" "$ISSUE_NATIVE" -} - -# Test: PR sync no longer promotes status/review from ready_for_review or review_requested -@test "pr-issue-sync.yml: does not reference ready_for_review transitions" { - run grep -q "ready_for_review" "$PR_SYNC" - [ "$status" -ne 0 ] -} - -@test "pr-issue-sync.yml: does not reference review_requested transitions" { - run grep -q "review_requested" "$PR_SYNC" - [ "$status" -ne 0 ] -} - -# Test: CI-driven workflow exists and targets status/review on green checks -@test "ci-issue-gate.yml: exists and sets status/review" { - grep -q "name: CI Issue Gate" "$CI_GATE" - grep -q "status/review" "$CI_GATE" -} - -@test "issue-native-automation.yml: ready comment mentions Copilot develop agent" { - grep -q "Assign to Copilot 'develop' Agent to begin work" "$ISSUE_NATIVE" -} - -@test "issue-native-automation.yml: plan approved comment includes next step" { - grep -q "Gate 1 approved" "$ISSUE_NATIVE" -} - -@test "ci-issue-gate.yml: review kickoff comment includes next-step instructions" { - grep -q "Review kickoff" "$CI_GATE" - grep -q "Invoke Copilot 'review' Agent" "$CI_GATE" -} - -@test "ci-issue-gate.yml: excludes own workflow run from check evaluation" { - grep -q "ownRunToken" "$CI_GATE" - grep -q "details.includes(ownRunToken)" "$CI_GATE" -} - -@test "pr-issue-sync.yml: merge completion comment includes gate closeout" { - grep -q "Gate 2 complete" "$PR_SYNC" -} diff --git a/tests/detect_test_cmd.bats b/tests/detect_test_cmd.bats deleted file mode 100644 index 6444a9d..0000000 --- a/tests/detect_test_cmd.bats +++ /dev/null @@ -1,144 +0,0 @@ -#!/usr/bin/env bats -# Tests for detect_test_cmd() function in .githooks/lib/detect.sh - -load '/Users/matt/Documents/PounceTek/Developer/GitHub/mpfk/auto/.githooks/lib/detect.sh' 2>/dev/null || { - echo "⚠️ .githooks/lib/detect.sh not found - this is expected during RED phase" >&2 - exit 1 -} - -setup() { - # Create a temporary directory for each test - TEST_DIR=$(mktemp -d) - cd "$TEST_DIR" - - # Create a mock workflow.conf - cat > workflow.conf << 'EOF' -# workflow.conf — Project-specific workflow configuration -TEST_CMD="npm test" -SRC_DIRS="src/ lib/" -TEST_DIRS="tests/ test/" -MAIN_BRANCH="main" -EOF - - # Unset TEST_CMD to start fresh - unset TEST_CMD -} - -teardown() { - # Clean up test directory - if [ -n "$TEST_DIR" ] && [ -d "$TEST_DIR" ]; then - cd / - rm -rf "$TEST_DIR" - fi -} - -@test "detect_test_cmd() with package.json sets npm test and writes back to workflow.conf" { - # Create package.json - echo '{"name":"test","scripts":{"test":"echo test"}}' > package.json - - # Reset workflow.conf to empty TEST_CMD - sed -i '' 's/TEST_CMD=.*/TEST_CMD=""/' workflow.conf - - # Run detection (direct call to persist exports) - detect_test_cmd - - [ "$TEST_CMD" = "npm test" ] - - # Check that workflow.conf was updated - grep -q 'TEST_CMD="npm test"' workflow.conf -} - -@test "detect_test_cmd() with pyproject.toml sets pytest and writes back" { - # Create pyproject.toml - echo '[project]' > pyproject.toml - echo 'name = "test"' >> pyproject.toml - - # Reset workflow.conf to empty TEST_CMD - sed -i '' 's/TEST_CMD=.*/TEST_CMD=""/' workflow.conf - - # Run detection (direct call to persist exports) - detect_test_cmd - - [ "$TEST_CMD" = "pytest" ] - - # Check that workflow.conf was updated - grep -q 'TEST_CMD="pytest"' workflow.conf -} - -@test "detect_test_cmd() with go.mod sets go test ./... and writes back" { - # Create go.mod - echo 'module test' > go.mod - echo 'go 1.19' >> go.mod - - # Reset workflow.conf to empty TEST_CMD - sed -i '' 's/TEST_CMD=.*/TEST_CMD=""/' workflow.conf - - # Run detection (direct call to persist exports) - detect_test_cmd - - [ "$TEST_CMD" = "go test ./..." ] - - # Check that workflow.conf was updated - grep -q 'TEST_CMD="go test ./..."' workflow.conf -} - -@test "detect_test_cmd() with multiple markers shows warning and does not set TEST_CMD" { - # Create multiple project markers - echo '{"name":"test"}' > package.json - echo '[project]' > pyproject.toml - - # Reset workflow.conf to empty TEST_CMD - sed -i '' 's/TEST_CMD=.*/TEST_CMD=""/' workflow.conf - - # Run detection - run detect_test_cmd - - [ "$status" -eq 0 ] - [ -z "$TEST_CMD" ] - - # Should contain warning about multiple markers - [[ "$stderr" == *"Multiple project markers found"* ]] - [[ "$stderr" == *"set TEST_CMD in workflow.conf"* ]] - - # workflow.conf should not be modified (still empty) - grep -q 'TEST_CMD=""' workflow.conf -} - -@test "detect_test_cmd() with no markers shows helpful message and does not set TEST_CMD" { - # No project markers created - - # Reset workflow.conf to empty TEST_CMD - sed -i '' 's/TEST_CMD=.*/TEST_CMD=""/' workflow.conf - - # Run detection - run detect_test_cmd - - [ "$status" -eq 0 ] - [ -z "$TEST_CMD" ] - - # Should contain helpful message - [[ "$stderr" == *"No project markers found"* ]] - - # workflow.conf should not be modified - grep -q 'TEST_CMD=""' workflow.conf -} - -@test "detect_test_cmd() with TEST_CMD already set does not override" { - # Create package.json - echo '{"name":"test"}' > package.json - - # Set TEST_CMD in environment - export TEST_CMD="custom test command" - - # Run detection - run detect_test_cmd - - [ "$status" -eq 0 ] - [ "$TEST_CMD" = "custom test command" ] - - # Should contain message about not overriding - [[ "$output" == *"TEST_CMD already set"* ]] - - # workflow.conf should not be modified - grep -q 'TEST_CMD="npm test"' workflow.conf -} \ No newline at end of file diff --git a/tests/label_sync_workflows.bats b/tests/label_sync_workflows.bats deleted file mode 100644 index ce6bb49..0000000 --- a/tests/label_sync_workflows.bats +++ /dev/null @@ -1,33 +0,0 @@ -#!/usr/bin/env bats - -REPO_ROOT="/Users/matt/Documents/PounceTek/Developer/GitHub/mpfk/auto" -LABEL_SYNC="$REPO_ROOT/.github/workflows/labels-sync.yml" -REPO_SETUP="$REPO_ROOT/.github/workflows/repo-setup.yml" -PR_SYNC="$REPO_ROOT/.github/workflows/pr-issue-sync.yml" -CI_GATE="$REPO_ROOT/.github/workflows/ci-issue-gate.yml" - -@test "labels-sync.yml: manual-only trigger prevents bootstrap overlap" { - run grep -q '^ push:' "$LABEL_SYNC" - [ "$status" -ne 0 ] -} - -@test "labels-sync.yml: workflow_dispatch remains available for intentional reruns" { - grep -q '^ workflow_dispatch:' "$LABEL_SYNC" -} - -@test "repo-setup.yml: owns automatic setup on pushes to main" { - grep -q '^ push:' "$REPO_SETUP" - grep -q '^ branches: \[main\]' "$REPO_SETUP" -} - -@test "pr-issue-sync.yml: no direct status/review promotion on PR lifecycle" { - run grep -q "status/review" "$PR_SYNC" - [ "$status" -ne 0 ] -} - -@test "ci-issue-gate.yml: workflow_run trigger listens to CI workflows" { - grep -q '^ workflow_run:' "$CI_GATE" - grep -q 'Test Suite' "$CI_GATE" - grep -q 'Conventional Commits Check' "$CI_GATE" - grep -q 'Workflow Policy' "$CI_GATE" -} diff --git a/tests/pre_push_gate.bats b/tests/pre_push_gate.bats deleted file mode 100644 index 117766d..0000000 --- a/tests/pre_push_gate.bats +++ /dev/null @@ -1,118 +0,0 @@ -#!/usr/bin/env bats -# Tests for .githooks/pre-push.d/020-test-suite-gate.sh hook behavior - -setup() { - # Create a temporary directory for each test - TEST_DIR=$(mktemp -d) - cd "$TEST_DIR" - git init --quiet - git commit --allow-empty -m "initial" --quiet - - # Create a mock workflow.conf with empty TEST_CMD - cat > workflow.conf << 'EOF' -# workflow.conf — Project-specific workflow configuration -TEST_CMD="" -SRC_DIRS="src/ lib/" -TEST_DIRS="tests/ test/" -MAIN_BRANCH="main" -EOF - - # Create mock .githooks structure - mkdir -p .githooks/lib .githooks/pre-push.d - - # Copy the real detect.sh helper - cp /Users/matt/Documents/PounceTek/Developer/GitHub/mpfk/auto/.githooks/lib/detect.sh .githooks/lib/detect.sh - - # Create the updated pre-push hook that we want to implement - cat > .githooks/pre-push.d/020-test-suite-gate.sh << 'EOF' -#!/bin/bash -# Runs the full test suite before allowing a push. -REPO_ROOT="$(git rev-parse --show-toplevel)" -source "$REPO_ROOT/workflow.conf" - -# Source the shared detection helper -source "$REPO_ROOT/.githooks/lib/detect.sh" - -branch=$(git rev-parse --abbrev-ref HEAD) -if ! echo "$branch" | grep -qE '^issue/'; then - exit 0 -fi - -echo "Running test suite before push..." - -# Call detect_test_cmd to auto-detect or use existing TEST_CMD -detect_test_cmd - -# If no test command after detection, gracefully skip -if [ -z "${TEST_CMD:-}" ]; then - echo "⚠️ No test command configured or detected. Skipping test suite." - echo "Create package.json, pyproject.toml, go.mod, etc. or set TEST_CMD in workflow.conf" - exit 0 -fi - -# Run the test command -if ! eval "$TEST_CMD" 2>&1; then - echo "" - echo "ERROR: Test suite failed. Fix failing tests before pushing." - exit 1 -fi -EOF - chmod +x .githooks/pre-push.d/020-test-suite-gate.sh - - # Create a feature branch to trigger the hook - git checkout -b issue/123 --quiet -} - -teardown() { - # Clean up test directory - if [ -n "$TEST_DIR" ] && [ -d "$TEST_DIR" ]; then - cd / - rm -rf "$TEST_DIR" - fi -} - -@test "pre-push hook: TEST_CMD empty, no project markers → graceful skip, exit 0" { - # No project files, TEST_CMD empty - should skip gracefully - - run ./.githooks/pre-push.d/020-test-suite-gate.sh - - [ "$status" -eq 0 ] - [[ "$output" == *"Running test suite before push..."* ]] - [[ "$output" == *"No test command configured or detected. Skipping test suite."* ]] -} - -@test "pre-push hook: TEST_CMD empty, package.json present → test command runs" { - # Create package.json with a successful test command - echo '{"scripts":{"test":"echo \"All tests passed\""}}' > package.json - - run ./.githooks/pre-push.d/020-test-suite-gate.sh - - [ "$status" -eq 0 ] - [[ "$output" == *"Running test suite before push..."* ]] - [[ "$output" == *"Detected test command: npm test"* ]] - [[ "$output" == *"All tests passed"* ]] -} - -@test "pre-push hook: TEST_CMD set manually in workflow.conf → test command runs directly" { - # Set TEST_CMD manually in workflow.conf - sed -i '' 's/TEST_CMD=""/TEST_CMD="echo Manual test passed"/' workflow.conf - - run ./.githooks/pre-push.d/020-test-suite-gate.sh - - [ "$status" -eq 0 ] - [[ "$output" == *"Running test suite before push..."* ]] - [[ "$output" == *"TEST_CMD already set to: echo Manual test passed"* ]] - [[ "$output" == *"Manual test passed"* ]] -} - -@test "pre-push hook: test suite fails → hook exits 1" { - # Create package.json with a failing test command - echo '{"scripts":{"test":"echo \"Test failed\"; exit 1"}}' > package.json - - run ./.githooks/pre-push.d/020-test-suite-gate.sh - - [ "$status" -eq 1 ] - [[ "$output" == *"Running test suite before push..."* ]] - [[ "$output" == *"Test failed"* ]] - [[ "$output" == *"ERROR: Test suite failed. Fix failing tests before pushing."* ]] -} \ No newline at end of file diff --git a/tests/workflow_policy.bats b/tests/workflow_policy.bats deleted file mode 100644 index 55945b9..0000000 --- a/tests/workflow_policy.bats +++ /dev/null @@ -1,137 +0,0 @@ -#!/usr/bin/env bats - -REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/.." && pwd)" -POLICY_WORKFLOW="$REPO_ROOT/.github/workflows/workflow-policy.yml" - -setup() { - command -v node >/dev/null || skip "Node.js is required for workflow policy tests" -} - -# The workflow script is a YAML block scalar under `script: |` with 12-space -# indentation. The first non-empty line indented to 6 spaces is outside that -# block and marks the end of the extracted script. -extract_script() { - awk ' - /script: \|/ { in_script = 1; next } - in_script { - if ($0 ~ /^ [^ ]/) exit - if ($0 ~ /^ /) { - sub(/^ /, "", $0) - print - } else { - print "" - } - } - ' "$POLICY_WORKFLOW" -} - -run_policy() { - local pr_json="$1" - local issue_json="$2" - local script - script="$(extract_script)" - - run env WORKFLOW_SCRIPT="$script" PR_JSON="$pr_json" ISSUE_JSON="$issue_json" node <<'NODE' -const script = process.env.WORKFLOW_SCRIPT; -const pr = JSON.parse(process.env.PR_JSON); -const issue = JSON.parse(process.env.ISSUE_JSON); - -const context = { - repo: { owner: 'Mpfk', repo: 'auto' }, - payload: { pull_request: pr }, -}; - -const github = { - rest: { - issues: { - get: async ({ issue_number }) => { - if (!issue || issue.number !== issue_number) { - throw new Error('Not Found'); - } - return { data: issue }; - }, - }, - }, -}; - -let failed = null; -let info = null; -const core = { - setFailed: (message) => { - failed = message; - }, - info: (message) => { - info = message; - }, -}; - -(async () => { - // This evaluates repository-controlled workflow code so tests can validate - // real policy behavior from the workflow file itself. - // Do not reuse this pattern with untrusted/external input. - const fn = new Function('context', 'github', 'core', `return (async () => {\n${script}\n})();`); - await fn(context, github, core); - process.stdout.write(JSON.stringify({ failed, info })); -})().catch((error) => { - console.error(error); - process.exit(1); -}); -NODE -} - -@test "workflow-policy: issue/{N} branch passes without explicit closing text" { - run_policy '{"head":{"ref":"issue/67"},"title":"feat: branch-linked PR","body":"","draft":true}' '{"number":67,"labels":[]}' - - [ "$status" -eq 0 ] - [[ "$output" == *'"failed":null'* ]] - [[ "$output" == *'"info":"Workflow policy checks passed for issue #67 (branch: issue/67)."'* ]] -} - -@test "workflow-policy: non-issue branch passes with valid closing keyword reference" { - run_policy '{"head":{"ref":"copilot/add-policy-update"},"title":"fix: relax policy","body":"fIxEs #67","draft":true}' '{"number":67,"labels":[]}' - - [ "$status" -eq 0 ] - [[ "$output" == *'"failed":null'* ]] -} - -@test "workflow-policy: non-issue branch passes with resolves keyword reference" { - run_policy '{"head":{"ref":"copilot/add-policy-update"},"title":"fix: relax policy","body":"Resolves #67","draft":true}' '{"number":67,"labels":[]}' - - [ "$status" -eq 0 ] - [[ "$output" == *'"failed":null'* ]] -} - -@test "workflow-policy: non-issue branch without linkage fails with actionable guidance" { - run_policy '{"head":{"ref":"copilot/add-policy-update"},"title":"fix: relax policy","body":"No issue linkage","draft":true}' '{"number":67,"labels":[]}' - - [ "$status" -eq 0 ] - [[ "$output" == *'"failed":"PR must either use an issue/{number} branch or include a valid issue-closing reference'* ]] -} - -@test "workflow-policy: ready-for-review PR still requires linked issue in status/review" { - run_policy '{"head":{"ref":"copilot/add-policy-update"},"title":"fix: relax policy","body":"Closes #67","draft":false}' '{"number":67,"labels":[]}' - - [ "$status" -eq 0 ] - [[ "$output" == *'"failed":"PR cannot be ready-for-review unless linked issue #67 is status/review."'* ]] -} - -@test "workflow-policy: issue/{N} ready-for-review PR still requires status/review" { - run_policy '{"head":{"ref":"issue/67"},"title":"fix: relax policy","body":"","draft":false}' '{"number":67,"labels":[]}' - - [ "$status" -eq 0 ] - [[ "$output" == *'"failed":"PR cannot be ready-for-review unless linked issue #67 is status/review."'* ]] -} - -@test "workflow-policy: PR from main branch fails even with closing reference" { - run_policy '{"head":{"ref":"main"},"title":"fix: something","body":"Closes #67","draft":true}' '{"number":67,"labels":[]}' - - [ "$status" -eq 0 ] - [[ "$output" == *'"failed":"PR must not be opened directly from the main branch'* ]] -} - -@test "workflow-policy: any named feature branch with closing reference passes" { - run_policy '{"head":{"ref":"my-feature-branch"},"title":"fix: something","body":"Closes #67","draft":true}' '{"number":67,"labels":[]}' - - [ "$status" -eq 0 ] - [[ "$output" == *'"failed":null'* ]] -}