Skip to content

Commit f1ab531

Browse files
hyperpolymathclaude
andcommitted
test: add E2E tests and CI workflow for gitbot-fleet
Tests cover bot directory structure, fleet coordinator bash syntax, GitHub Actions workflow YAML validity, and bot Cargo manifests. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 701daf9 commit f1ab531

2 files changed

Lines changed: 245 additions & 0 deletions

File tree

.github/workflows/e2e.yml

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
name: E2E Tests
3+
on:
4+
push:
5+
branches: [main]
6+
pull_request:
7+
branches: ['**']
8+
permissions:
9+
contents: read
10+
jobs:
11+
e2e:
12+
name: E2E tests
13+
runs-on: ubuntu-latest
14+
steps:
15+
- uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4
16+
- name: Install PyYAML (for workflow YAML validation)
17+
run: pip install pyyaml --quiet
18+
- name: Run E2E tests
19+
run: bash tests/e2e.sh

tests/e2e.sh

Lines changed: 226 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,226 @@
1+
#!/usr/bin/env bash
2+
# SPDX-License-Identifier: PMPL-1.0-or-later
3+
# Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
#
5+
# gitbot-fleet — End-to-End Tests
6+
#
7+
# Validates the gitbot fleet coordinator scripts, bot directory structure,
8+
# shell script syntax, and required configuration files/directories.
9+
#
10+
# Usage:
11+
# bash tests/e2e.sh
12+
# just e2e
13+
14+
set -euo pipefail
15+
16+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
17+
PROJECT_DIR="$(cd "$SCRIPT_DIR/.." && pwd)"
18+
19+
PASS=0
20+
FAIL=0
21+
SKIP=0
22+
23+
# ─── Colour helpers ──────────────────────────────────────────────────
24+
green() { printf '\033[32m%s\033[0m\n' "$*"; }
25+
red() { printf '\033[31m%s\033[0m\n' "$*"; }
26+
yellow(){ printf '\033[33m%s\033[0m\n' "$*"; }
27+
bold() { printf '\033[1m%s\033[0m\n' "$*"; }
28+
29+
# ─── Assertion helpers ───────────────────────────────────────────────
30+
31+
# check <label> <expected-substring> <actual>
32+
check() {
33+
local name="$1" expected="$2" actual="$3"
34+
if echo "$actual" | grep -q "$expected"; then
35+
green " PASS: $name"
36+
PASS=$((PASS + 1))
37+
else
38+
red " FAIL: $name (expected '$expected', got '${actual:0:120}')"
39+
FAIL=$((FAIL + 1))
40+
fi
41+
}
42+
43+
# check_exists <label> <path>
44+
check_exists() {
45+
local name="$1" path="$2"
46+
if [ -e "$path" ]; then
47+
green " PASS: $name"
48+
PASS=$((PASS + 1))
49+
else
50+
red " FAIL: $name (path not found: $path)"
51+
FAIL=$((FAIL + 1))
52+
fi
53+
}
54+
55+
# check_dir <label> <path>
56+
check_dir() {
57+
local name="$1" path="$2"
58+
if [ -d "$path" ]; then
59+
green " PASS: $name"
60+
PASS=$((PASS + 1))
61+
else
62+
red " FAIL: $name (directory not found: $path)"
63+
FAIL=$((FAIL + 1))
64+
fi
65+
}
66+
67+
# check_bash_syntax <label> <script>
68+
check_bash_syntax() {
69+
local name="$1" script="$2"
70+
if bash -n "$script" 2>/dev/null; then
71+
green " PASS: $name"
72+
PASS=$((PASS + 1))
73+
else
74+
red " FAIL: $name (bash syntax error in $script)"
75+
bash -n "$script" 2>&1 | head -5 >&2
76+
FAIL=$((FAIL + 1))
77+
fi
78+
}
79+
80+
# skip_test <label> <reason>
81+
skip_test() {
82+
yellow " SKIP: $1 ($2)"
83+
SKIP=$((SKIP + 1))
84+
}
85+
86+
echo "═══════════════════════════════════════════════════════════════"
87+
echo " gitbot-fleet — End-to-End Tests"
88+
echo "═══════════════════════════════════════════════════════════════"
89+
echo ""
90+
91+
# ─── Section 1: Repository structure ────────────────────────────────
92+
bold "Section 1: Repository structure"
93+
94+
check_dir "bots/ directory exists" "$PROJECT_DIR/bots"
95+
check_dir "campaigns/ directory exists" "$PROJECT_DIR/campaigns"
96+
check_dir "deploy/ directory exists" "$PROJECT_DIR/deploy"
97+
check_dir "hooks/ directory exists" "$PROJECT_DIR/hooks"
98+
check_exists "AI manifest present" "$PROJECT_DIR/0-AI-MANIFEST.a2ml"
99+
check_exists "fleet-coordinator.sh present" "$PROJECT_DIR/fleet-coordinator.sh"
100+
check_exists "learning-monitor.sh present" "$PROJECT_DIR/learning-monitor.sh"
101+
check_exists "Justfile present" "$PROJECT_DIR/Justfile"
102+
check_exists "docker-compose.yml present" "$PROJECT_DIR/docker-compose.yml"
103+
104+
echo ""
105+
106+
# ─── Section 2: Bot directory structure ─────────────────────────────
107+
bold "Section 2: Bot directory structure (expected fleet members)"
108+
109+
EXPECTED_BOTS="rhodibot echidnabot sustainabot panicbot glambot seambot finishingbot accessibilitybot cipherbot"
110+
for bot in $EXPECTED_BOTS; do
111+
check_dir "bots/$bot directory exists" "$PROJECT_DIR/bots/$bot"
112+
done
113+
114+
echo ""
115+
116+
# ─── Section 3: Shell script syntax validation ───────────────────────
117+
bold "Section 3: Shell script syntax checks"
118+
119+
for script in \
120+
"$PROJECT_DIR/fleet-coordinator.sh" \
121+
"$PROJECT_DIR/learning-monitor.sh" \
122+
"$PROJECT_DIR/run-fleet.sh" \
123+
"$PROJECT_DIR/setup.sh"; do
124+
if [ -f "$script" ]; then
125+
check_bash_syntax "$(basename "$script") syntax valid" "$script"
126+
else
127+
skip_test "$(basename "$script") syntax check" "file not present"
128+
fi
129+
done
130+
131+
# Also check hooks directory for bash scripts
132+
if [ -d "$PROJECT_DIR/hooks" ]; then
133+
for script in "$PROJECT_DIR/hooks/"*.sh; do
134+
[ -f "$script" ] || continue
135+
check_bash_syntax "hooks/$(basename "$script") syntax valid" "$script"
136+
done
137+
fi
138+
139+
echo ""
140+
141+
# ─── Section 4: fleet-coordinator.sh usage output ───────────────────
142+
bold "Section 4: fleet-coordinator.sh help/usage output"
143+
144+
if [ -f "$PROJECT_DIR/fleet-coordinator.sh" ]; then
145+
OUTPUT=$(bash "$PROJECT_DIR/fleet-coordinator.sh" --help 2>&1 || bash "$PROJECT_DIR/fleet-coordinator.sh" help 2>&1 || true)
146+
if [ -z "$OUTPUT" ]; then
147+
# Try calling with no args — many scripts print usage on no args
148+
OUTPUT=$(bash "$PROJECT_DIR/fleet-coordinator.sh" 2>&1 || true)
149+
fi
150+
check "fleet-coordinator has usage/help text" "USAGE\|Usage\|usage\|COMMAND\|command\|fleet" "$OUTPUT"
151+
check "fleet-coordinator mentions bots/run-scan" "run-scan\|scan\|bot\|fleet\|deploy" "$OUTPUT"
152+
else
153+
skip_test "fleet-coordinator.sh help" "script not found"
154+
fi
155+
156+
echo ""
157+
158+
# ─── Section 5: GitHub Actions workflows ────────────────────────────
159+
bold "Section 5: GitHub Actions workflow validity (YAML syntax)"
160+
161+
WORKFLOWS_DIR="$PROJECT_DIR/.github/workflows"
162+
if [ -d "$WORKFLOWS_DIR" ]; then
163+
WORKFLOW_COUNT=0
164+
WORKFLOW_FAIL=0
165+
for wf in "$WORKFLOWS_DIR/"*.yml; do
166+
[ -f "$wf" ] || continue
167+
WORKFLOW_COUNT=$((WORKFLOW_COUNT + 1))
168+
# Validate YAML by checking with python3 (available in CI)
169+
if command -v python3 >/dev/null 2>&1; then
170+
if python3 -c "import yaml; yaml.safe_load(open('$wf'))" 2>/dev/null; then
171+
:
172+
else
173+
red " FAIL: workflow YAML parse failed: $(basename "$wf")"
174+
FAIL=$((FAIL + 1))
175+
WORKFLOW_FAIL=$((WORKFLOW_FAIL + 1))
176+
fi
177+
fi
178+
# Check SPDX header
179+
if grep -q "SPDX-License-Identifier" "$wf" 2>/dev/null; then
180+
:
181+
else
182+
yellow " WARN: missing SPDX header in $(basename "$wf")"
183+
fi
184+
done
185+
if [ "$WORKFLOW_COUNT" -gt 0 ] && [ "$WORKFLOW_FAIL" -eq 0 ]; then
186+
green " PASS: all $WORKFLOW_COUNT workflow YAML files parse successfully"
187+
PASS=$((PASS + 1))
188+
elif [ "$WORKFLOW_COUNT" -eq 0 ]; then
189+
skip_test "workflow YAML validation" "no .yml files in .github/workflows"
190+
fi
191+
check "minimum workflow count (>=5)" "[5-9]\|[0-9][0-9]" "$WORKFLOW_COUNT"
192+
else
193+
skip_test "workflow YAML validation" ".github/workflows not found"
194+
fi
195+
196+
echo ""
197+
198+
# ─── Section 6: bot Cargo.toml manifests ────────────────────────────
199+
bold "Section 6: Bot Cargo manifests"
200+
201+
for bot in rhodibot echidnabot panicbot cipherbot; do
202+
CARGO="$PROJECT_DIR/bots/$bot/Cargo.toml"
203+
if [ -f "$CARGO" ]; then
204+
OUTPUT=$(cat "$CARGO")
205+
check "$bot Cargo.toml has package name" "name.*$bot\|name.*\"$bot\"" "$OUTPUT"
206+
check "$bot Cargo.toml has PMPL license" "PMPL\|pmpl" "$OUTPUT"
207+
else
208+
skip_test "$bot Cargo.toml" "file not found"
209+
fi
210+
done
211+
212+
echo ""
213+
214+
# ═══════════════════════════════════════════════════════════════════════
215+
# Summary
216+
# ═══════════════════════════════════════════════════════════════════════
217+
echo "═══════════════════════════════════════════════════════════════"
218+
printf " Results: "
219+
green "PASS=$PASS" | tr -d '\n'
220+
echo -n " "
221+
if [ "$FAIL" -gt 0 ]; then red "FAIL=$FAIL" | tr -d '\n'; else echo -n "FAIL=0"; fi
222+
echo -n " "
223+
if [ "$SKIP" -gt 0 ]; then yellow "SKIP=$SKIP"; else echo "SKIP=0"; fi
224+
echo "═══════════════════════════════════════════════════════════════"
225+
226+
exit "$FAIL"

0 commit comments

Comments
 (0)