Skip to content

Commit 6487d66

Browse files
Copilotvgoehler
andauthored
tests: migrate to bats/cram/remake test frameworks
- tests/bats/check_changes.bats: 12 unit tests (bats) - tests/bats/detect_changes.bats: 6 unit tests (bats) - tests/bats/courses_lib.bats: 11 unit tests for courses_lib.sh (bats, new) - tests/cram/check_changes.t: CLI integration tests (cram) - tests/cram/detect_changes.t: CLI integration tests (cram) - tests/remake/Makefile.test: 8 Makefile variable/target tests (remake) - tests/run_tests.sh: unified runner for all three frameworks - Remove tests/lib/test_lib.sh and tests/test_*.sh (old custom framework) - README.md: updated Tests section with framework table and test-case tables Agent-Logs-Url: https://github.com/TUBAF-IfI-LiaScript/TUBAF-IfI-LiaScript.github.io/sessions/4f86ccae-242f-4082-a1f3-450ced03b785 Co-authored-by: vgoehler <1705385+vgoehler@users.noreply.github.com>
1 parent 2f77a2a commit 6487d66

11 files changed

Lines changed: 932 additions & 738 deletions

README.md

Lines changed: 65 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -108,19 +108,24 @@ SCORM_SCORE = 80
108108
├── digitalesysteme.yml # Kurskonfiguration
109109
├── digitalesysteme.html # Generierte Webseite
110110
├── scripts/
111-
│ ├── check_changes.sh # Intelligente Change-Detection (Makefile)
111+
│ ├── check_changes.sh # Intelligente Change-Detection (verwendet vom Makefile)
112112
│ ├── detect_changes.sh # Änderungserkennung (GitHub Action)
113113
│ ├── courses.conf # Kurs → Upstream-Repo Mapping
114114
│ ├── courses_lib.sh # Shared library für courses.conf lookups
115115
│ ├── generate_courses.sh # GitHub-Action: Kurs-Generierung
116116
│ ├── prune_pdfs.sh # Entfernt unreferenzierte PDFs
117117
│ └── deployment_summary.sh # GitHub-Action: Deployment-Zusammenfassung
118118
├── tests/
119-
│ ├── run_tests.sh # Test-Runner (alle Tests ausführen)
120-
│ ├── lib/
121-
│ │ └── test_lib.sh # Gemeinsame Test-Hilfsfunktionen
122-
│ ├── test_check_changes.sh # Tests für check_changes.sh
123-
│ └── test_detect_changes.sh # Tests für detect_changes.sh
119+
│ ├── run_tests.sh # Test-Runner (bats + cram + remake)
120+
│ ├── bats/ # Shell-Unit-Tests (bats-core)
121+
│ │ ├── check_changes.bats
122+
│ │ ├── detect_changes.bats
123+
│ │ └── courses_lib.bats
124+
│ ├── cram/ # CLI-Integrationstests (cram)
125+
│ │ ├── check_changes.t
126+
│ │ └── detect_changes.t
127+
│ └── remake/ # Makefile-Tests (remake)
128+
│ └── Makefile.test
124129
├── .cache/ # Cache für Change-Detection (von Git ignoriert)
125130
│ └── digitalesysteme # Hash-Cache (YAML + Remote)
126131
├── assets/
@@ -230,24 +235,46 @@ Das System erkennt automatisch neue Commits in den überwachten Repositories:
230235

231236
## Tests
232237

233-
Die Change-Detection-Logik ist durch eine dedizierte Test-Suite in `tests/` abgedeckt.
238+
Die Change-Detection-Logik ist durch eine dreistufige Test-Suite in `tests/` abgedeckt:
239+
240+
| Tool | Zweck | Dateien |
241+
|------|-------|---------|
242+
| **[bats](https://github.com/bats-core/bats-core)** | Shell-Unit-Tests | `tests/bats/*.bats` |
243+
| **[cram](https://bitheap.org/cram/)** | CLI-Integrationstests | `tests/cram/*.t` |
244+
| **[remake](https://bashdb.sourceforge.net/remake/)** | Makefile-Tests | `tests/remake/Makefile.test` |
234245

235246
### ▶️ Tests ausführen
236247

237248
```bash
238249
bash tests/run_tests.sh
239250
```
240251

252+
Einzelne Frameworks können auch direkt aufgerufen werden:
253+
254+
```bash
255+
# nur bats
256+
bats tests/bats/
257+
258+
# nur cram
259+
REPO_ROOT=$(pwd) cram --shell=bash tests/cram/*.t
260+
261+
# nur remake
262+
remake -f tests/remake/Makefile.test test
263+
```
264+
241265
### 📁 Test-Struktur
242266

243-
| Datei | Beschreibung |
244-
|-------|-------------|
245-
| `tests/run_tests.sh` | Führt alle Test-Dateien aus und gibt eine Gesamtzusammenfassung |
246-
| `tests/lib/test_lib.sh` | Gemeinsame Hilfsfunktionen (`suite`, `pass`, `fail`, `assert_*`, …) |
247-
| `tests/test_check_changes.sh` | Tests für `scripts/check_changes.sh` (Makefile-seitige Detection) |
248-
| `tests/test_detect_changes.sh` | Tests für `scripts/detect_changes.sh` (GitHub-Action-seitige Detection) |
267+
| Datei | Framework | Beschreibung |
268+
|-------|-----------|-------------|
269+
| `tests/run_tests.sh` || Führt alle drei Suiten aus |
270+
| `tests/bats/check_changes.bats` | bats | 12 Unit-Tests für `check_changes.sh` |
271+
| `tests/bats/detect_changes.bats` | bats | 6 Unit-Tests für `detect_changes.sh` |
272+
| `tests/bats/courses_lib.bats` | bats | 11 Unit-Tests für `courses_lib.sh` |
273+
| `tests/cram/check_changes.t` | cram | CLI-Integrationstests für `check_changes.sh` |
274+
| `tests/cram/detect_changes.t` | cram | CLI-Integrationstests für `detect_changes.sh` |
275+
| `tests/remake/Makefile.test` | remake | 8 Tests für Makefile-Variablen und Targets |
249276

250-
### 🧪 Testfälle für `check_changes.sh`
277+
### 🧪 Testfälle für `check_changes.sh` (bats + cram)
251278

252279
| Szenario | Erwartetes Verhalten |
253280
|----------|----------------------|
@@ -264,7 +291,7 @@ bash tests/run_tests.sh
264291
| Kurs ohne Remote-Mapping, aktuell | Exit 1 |
265292
| Kurs ohne Remote-Mapping, YAML geändert | Exit 0 |
266293

267-
### 🧪 Testfälle für `detect_changes.sh`
294+
### 🧪 Testfälle für `detect_changes.sh` (bats + cram)
268295

269296
| Szenario | Erwartetes Verhalten |
270297
|----------|----------------------|
@@ -274,3 +301,26 @@ bash tests/run_tests.sh
274301
| YAML geändert UND HTML fehlt | Kurs erscheint in beiden Outputs |
275302
| `.github/workflows/*.yml` geändert | Workflow-Datei wird nicht als Kurs behandelt |
276303
| Mehrere Kurse, nur einer geändert | Nur der geänderte Kurs wird regeneriert |
304+
305+
### 🧪 Testfälle für `courses_lib.sh` (bats)
306+
307+
| Szenario | Erwartetes Verhalten |
308+
|----------|----------------------|
309+
| Kein Argument (CLI) | Usage-Meldung, Exit ≠ 0 |
310+
| Kurs mit Mapping (z. B. `digitalesysteme`) | Gibt korrekten Repo-Namen aus |
311+
| Kurs ohne Mapping (z. B. `index`) | Leere Ausgabe |
312+
| Unbekannter Kurs | Leere Ausgabe |
313+
| Als Bibliothek gesourct | `lookup_repo()` gibt korrekte Werte zurück |
314+
315+
### 🧪 Testfälle für das Makefile (remake)
316+
317+
| Test | Was wird geprüft |
318+
|------|-----------------|
319+
| `test-courses-var` | `COURSES`-Variable enthält alle 5 Kurse |
320+
| `test-pdf-courses-var` | `PDF_COURSES` enthält die 4 PDF-Kurse |
321+
| `test-scorm-score-var` | `SCORM_SCORE = 80` |
322+
| `test-scorm-org-var` | `SCORM_ORG` ist nicht leer |
323+
| `test-phony-targets` | Wichtige Targets als `.PHONY` deklariert |
324+
| `test-help-target` | `make help` gibt "Available targets" aus |
325+
| `test-status-target` | `make status` gibt "Build Status" aus |
326+
| `test-clean-cache-target` | `make clean-cache` räumt `.cache/` auf |

tests/bats/check_changes.bats

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,229 @@
1+
#!/usr/bin/env bats
2+
# Unit tests for scripts/check_changes.sh
3+
#
4+
# Approach
5+
# --------
6+
# setup() creates an isolated working directory, changes into it, and puts a
7+
# mock `curl` binary at the front of PATH so no real network calls are made.
8+
# Each @test runs in its own subshell, so the cd in setup() is local to that
9+
# test. teardown() removes the temp directories.
10+
#
11+
# check_changes.sh sources courses_lib.sh from its own (production) scripts/
12+
# directory, so the real courses.conf is used. Tests use real course names:
13+
# digitalesysteme – has an upstream mapping in courses.conf
14+
# index – listed in COURSES but has NO upstream mapping
15+
16+
REPO_ROOT="$(cd "$(dirname "$BATS_TEST_FILENAME")/../.." && pwd)"
17+
SCRIPT="$REPO_ROOT/scripts/check_changes.sh"
18+
19+
# ---------------------------------------------------------------------------
20+
# Per-test lifecycle
21+
# ---------------------------------------------------------------------------
22+
setup() {
23+
TEST_DIR="$(mktemp -d)"
24+
MOCK_BIN="$(mktemp -d)"
25+
# Change into the isolated work directory so check_changes.sh reads/writes
26+
# YAML, HTML, and .cache files from there.
27+
cd "$TEST_DIR"
28+
}
29+
30+
teardown() {
31+
rm -rf "$TEST_DIR" "$MOCK_BIN"
32+
}
33+
34+
# make_mock_curl <sha>
35+
# Writes a mock curl binary to $MOCK_BIN.
36+
# Pass "ERROR" to simulate a network failure (empty response).
37+
make_mock_curl() {
38+
local sha="$1"
39+
if [ "$sha" = "ERROR" ]; then
40+
cat > "$MOCK_BIN/curl" << 'MOCK'
41+
#!/usr/bin/env bash
42+
echo ""
43+
exit 0
44+
MOCK
45+
else
46+
# shellcheck disable=SC2016
47+
printf '#!/usr/bin/env bash\necho '"'"'{"sha":"%s","commit":{}}'"'"'\n' "$sha" \
48+
> "$MOCK_BIN/curl"
49+
fi
50+
chmod +x "$MOCK_BIN/curl"
51+
}
52+
53+
# ---------------------------------------------------------------------------
54+
# Argument handling
55+
# ---------------------------------------------------------------------------
56+
57+
@test "no argument: prints usage and exits non-zero" {
58+
run bash "$SCRIPT"
59+
[ "$status" -ne 0 ]
60+
[[ "$output" == *"Usage:"* ]]
61+
}
62+
63+
@test "missing YAML: exits non-zero with error message" {
64+
make_mock_curl "abc123"
65+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
66+
[ "$status" -ne 0 ]
67+
[[ "$output" == *"not found"* ]]
68+
}
69+
70+
# ---------------------------------------------------------------------------
71+
# First-run (no cache)
72+
# ---------------------------------------------------------------------------
73+
74+
@test "no cache, no HTML: rebuild needed (exit 0)" {
75+
make_mock_curl "deadbeef1234"
76+
echo "title: Test" > digitalesysteme.yml
77+
78+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
79+
[ "$status" -eq 0 ]
80+
[[ "$output" == *"rebuild needed"* ]]
81+
}
82+
83+
@test "no cache, HTML present: rebuild needed because no cached hash" {
84+
make_mock_curl "deadbeef1234"
85+
echo "title: Test" > digitalesysteme.yml
86+
touch digitalesysteme.html
87+
88+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
89+
[ "$status" -eq 0 ]
90+
}
91+
92+
# ---------------------------------------------------------------------------
93+
# Cache matches – nothing changed
94+
# ---------------------------------------------------------------------------
95+
96+
@test "cache matches YAML + remote + HTML present: no rebuild (exit 1)" {
97+
local remote_sha="aabbccddeeff0011"
98+
make_mock_curl "$remote_sha"
99+
100+
echo "title: Test" > digitalesysteme.yml
101+
touch digitalesysteme.html
102+
103+
mkdir -p .cache
104+
local yaml_hash
105+
yaml_hash=$(sha256sum digitalesysteme.yml | cut -d' ' -f1)
106+
printf "%s\n%s\n" "$yaml_hash" "$remote_sha" > .cache/digitalesysteme
107+
108+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
109+
[ "$status" -eq 1 ]
110+
[[ "$output" == *"No changes detected"* ]]
111+
}
112+
113+
# ---------------------------------------------------------------------------
114+
# Individual change triggers
115+
# ---------------------------------------------------------------------------
116+
117+
@test "YAML changed: rebuild needed with reason" {
118+
local remote_sha="aabbccddeeff0011"
119+
make_mock_curl "$remote_sha"
120+
121+
echo "title: Test" > digitalesysteme.yml
122+
touch digitalesysteme.html
123+
124+
mkdir -p .cache
125+
printf "%s\n%s\n" "stale_yaml_hash_value_0000" "$remote_sha" \
126+
> .cache/digitalesysteme
127+
128+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
129+
[ "$status" -eq 0 ]
130+
[[ "$output" == *"YAML file changed"* ]]
131+
}
132+
133+
@test "remote hash changed: rebuild needed with reason" {
134+
local new_sha="newsha9999"
135+
make_mock_curl "$new_sha"
136+
137+
echo "title: Test" > digitalesysteme.yml
138+
touch digitalesysteme.html
139+
140+
mkdir -p .cache
141+
local yaml_hash
142+
yaml_hash=$(sha256sum digitalesysteme.yml | cut -d' ' -f1)
143+
printf "%s\n%s\n" "$yaml_hash" "oldsha1111" > .cache/digitalesysteme
144+
145+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
146+
[ "$status" -eq 0 ]
147+
[[ "$output" == *"Remote repository changed"* ]]
148+
}
149+
150+
@test "HTML file missing: rebuild needed with reason" {
151+
local remote_sha="aabbccddeeff0011"
152+
make_mock_curl "$remote_sha"
153+
154+
echo "title: Test" > digitalesysteme.yml
155+
# No HTML file
156+
157+
mkdir -p .cache
158+
local yaml_hash
159+
yaml_hash=$(sha256sum digitalesysteme.yml | cut -d' ' -f1)
160+
printf "%s\n%s\n" "$yaml_hash" "$remote_sha" > .cache/digitalesysteme
161+
162+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
163+
[ "$status" -eq 0 ]
164+
[[ "$output" == *"HTML file missing"* ]]
165+
}
166+
167+
# ---------------------------------------------------------------------------
168+
# Remote unreachable
169+
# ---------------------------------------------------------------------------
170+
171+
@test "remote unreachable, nothing else changed: no rebuild (exit 1)" {
172+
make_mock_curl "ERROR"
173+
174+
echo "title: Test" > digitalesysteme.yml
175+
touch digitalesysteme.html
176+
177+
mkdir -p .cache
178+
local yaml_hash
179+
yaml_hash=$(sha256sum digitalesysteme.yml | cut -d' ' -f1)
180+
printf "%s\n%s\n" "$yaml_hash" "unreachable" > .cache/digitalesysteme
181+
182+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
183+
[ "$status" -eq 1 ]
184+
}
185+
186+
@test "remote unreachable but YAML changed: rebuild still triggered" {
187+
make_mock_curl "ERROR"
188+
189+
echo "title: Test" > digitalesysteme.yml
190+
touch digitalesysteme.html
191+
192+
mkdir -p .cache
193+
printf "%s\n%s\n" "stale_yaml_hash" "unreachable" > .cache/digitalesysteme
194+
195+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "digitalesysteme"
196+
[ "$status" -eq 0 ]
197+
}
198+
199+
# ---------------------------------------------------------------------------
200+
# Course without a remote mapping (index)
201+
# ---------------------------------------------------------------------------
202+
203+
@test "no remote mapping, cache current: no rebuild (exit 1)" {
204+
make_mock_curl "SHOULD_NOT_BE_CALLED"
205+
206+
echo "title: Index" > index.yml
207+
touch index.html
208+
209+
mkdir -p .cache
210+
local yaml_hash
211+
yaml_hash=$(sha256sum index.yml | cut -d' ' -f1)
212+
printf "%s\n%s\n" "$yaml_hash" "no-remote" > .cache/index
213+
214+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "index"
215+
[ "$status" -eq 1 ]
216+
}
217+
218+
@test "no remote mapping, YAML changed: rebuild needed" {
219+
make_mock_curl "SHOULD_NOT_BE_CALLED"
220+
221+
echo "title: Index" > index.yml
222+
touch index.html
223+
224+
mkdir -p .cache
225+
printf "%s\n%s\n" "stale_hash" "no-remote" > .cache/index
226+
227+
run env PATH="$MOCK_BIN:$PATH" bash "$SCRIPT" "index"
228+
[ "$status" -eq 0 ]
229+
}

0 commit comments

Comments
 (0)