# Tests for archeflow-git.sh — git branch/commit strategy for ArcheFlow runs. # # Validates: branch creation with correct naming, commit formatting, # merge strategies, input validation, and safety guards. setup() { load test_helper _common_setup } teardown() { _common_teardown } # --- Usage --- @test "git: exits 1 with usage when called with fewer than 2 args" { run "$LIB_DIR/archeflow-git.sh" [ "$status" -eq 1 ] [[ "$output" == *"Usage"* ]] } @test "git: exits 1 for unknown command" { run "$LIB_DIR/archeflow-git.sh" nonexistent test-run [ "$status" -ne 0 ] [[ "$output" == *"Unknown command"* ]] } # --- init --- @test "git init: creates branch with archeflow/ prefix" { run "$LIB_DIR/archeflow-git.sh" init test-run [ "$status" -eq 0 ] local current current=$(git branch --show-current) [ "$current" = "archeflow/test-run" ] } @test "git init: stores base branch in .archeflow/runs//base-branch" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null [ -f ".archeflow/runs/test-run/base-branch" ] local base base=$(cat ".archeflow/runs/test-run/base-branch") [ "$base" = "main" ] } @test "git init: fails if branch already exists" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null git checkout main --quiet run "$LIB_DIR/archeflow-git.sh" init test-run [ "$status" -ne 0 ] [[ "$output" == *"already exists"* ]] } # --- commit --- @test "git commit: uses conventional commit format by default" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null # Create a file to commit mkdir -p .archeflow/events echo '{"test":true}' > .archeflow/events/test-run.jsonl "$LIB_DIR/archeflow-git.sh" commit test-run plan "initial plan" 2>/dev/null local msg msg=$(git log -1 --format=%s) [[ "$msg" == "archeflow(plan): initial plan" ]] } @test "git commit: stages event file automatically" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null mkdir -p .archeflow/events echo '{"test":true}' > .archeflow/events/test-run.jsonl "$LIB_DIR/archeflow-git.sh" commit test-run plan "test commit" 2>/dev/null # Verify the event file was committed local committed_files committed_files=$(git diff-tree --no-commit-id --name-only -r HEAD) [[ "$committed_files" == *"test-run.jsonl"* ]] } @test "git commit: stages extra files passed as arguments" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null echo "extra content" > extra.txt "$LIB_DIR/archeflow-git.sh" commit test-run do "with extras" extra.txt 2>/dev/null local committed_files committed_files=$(git diff-tree --no-commit-id --name-only -r HEAD) [[ "$committed_files" == *"extra.txt"* ]] } @test "git commit: reports nothing to commit when no changes" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null # Commit the init artifacts first so there's a clean state git add -A && git commit -m "init artifacts" --quiet 2>/dev/null || true run bash -c "cd '$BATS_TEST_TMPDIR' && '$LIB_DIR/archeflow-git.sh' commit test-run plan 'empty' 2>&1" [ "$status" -eq 0 ] [[ "$output" == *"Nothing to commit"* ]] } @test "git commit: fails if not on the run branch" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null git checkout main --quiet run "$LIB_DIR/archeflow-git.sh" commit test-run plan "wrong branch" [ "$status" -ne 0 ] [[ "$output" == *"Expected to be on branch"* ]] } # --- phase-commit --- @test "git phase-commit: creates commit with phase transition message" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null mkdir -p .archeflow/events echo '{"test":true}' > .archeflow/events/test-run.jsonl "$LIB_DIR/archeflow-git.sh" phase-commit test-run plan 2>/dev/null local msg msg=$(git log -1 --format=%s) # Should contain the phase transition arrow [[ "$msg" == *"plan"* ]] [[ "$msg" == *"do"* ]] } # --- merge --- @test "git merge: squash merge is the default strategy" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null mkdir -p .archeflow/events echo '{"test":true}' > .archeflow/events/test-run.jsonl "$LIB_DIR/archeflow-git.sh" commit test-run plan "test" 2>/dev/null "$LIB_DIR/archeflow-git.sh" merge test-run 2>/dev/null local current current=$(git branch --show-current) [ "$current" = "main" ] local msg msg=$(git log -1 --format=%s) [[ "$msg" == *"archeflow run test-run"* ]] } @test "git merge: --no-ff creates a merge commit" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null mkdir -p .archeflow/events echo '{"test":true}' > .archeflow/events/test-run.jsonl "$LIB_DIR/archeflow-git.sh" commit test-run plan "test" 2>/dev/null "$LIB_DIR/archeflow-git.sh" merge test-run --no-ff 2>/dev/null local current current=$(git branch --show-current) [ "$current" = "main" ] # no-ff merge commit should have 2 parents local parent_count parent_count=$(git cat-file -p HEAD | grep -c '^parent') [ "$parent_count" -eq 2 ] } @test "git merge: rejects unknown merge strategy" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null mkdir -p .archeflow/events echo '{"test":true}' > .archeflow/events/test-run.jsonl "$LIB_DIR/archeflow-git.sh" commit test-run plan "test" 2>/dev/null run "$LIB_DIR/archeflow-git.sh" merge test-run --fast-forward [ "$status" -ne 0 ] [[ "$output" == *"Unknown merge strategy"* ]] } @test "git merge: fails with uncommitted changes" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null echo "dirty" > dirty.txt git add dirty.txt run "$LIB_DIR/archeflow-git.sh" merge test-run [ "$status" -ne 0 ] [[ "$output" == *"Uncommitted changes"* ]] } # --- format_message --- @test "git commit: simple style uses 'phase: msg' format" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null # Create config with simple style mkdir -p .archeflow echo "commit_style: simple" > .archeflow/config.yaml mkdir -p .archeflow/events echo '{"test":true}' > .archeflow/events/test-run.jsonl "$LIB_DIR/archeflow-git.sh" commit test-run plan "simple test" 2>/dev/null local msg msg=$(git log -1 --format=%s) [ "$msg" = "plan: simple test" ] } # --- status --- @test "git status: shows branch info for existing run" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null run "$LIB_DIR/archeflow-git.sh" status test-run [ "$status" -eq 0 ] [[ "$output" == *"Branch: archeflow/test-run"* ]] [[ "$output" == *"Base: main"* ]] } @test "git status: fails for nonexistent branch" { run "$LIB_DIR/archeflow-git.sh" status nonexistent [ "$status" -ne 0 ] [[ "$output" == *"does not exist"* ]] } # --- cleanup --- @test "git cleanup: fails if currently on the run branch" { "$LIB_DIR/archeflow-git.sh" init test-run 2>/dev/null run "$LIB_DIR/archeflow-git.sh" cleanup test-run [ "$status" -ne 0 ] [[ "$output" == *"Cannot delete"* ]] }