Files
claude-archeflow-plugin/tests/archeflow-memory.bats
Christian Nennemann 6a49c21bbe test: add bats test suite for lib/ helper scripts
110 tests across 10 test files covering all lib/ scripts:
- archeflow-event.sh: JSONL format, seq numbering, parent fields, validation
- archeflow-memory.sh: add/list/decay/forget/inject/extract commands
- archeflow-git.sh: branch creation, commit format, merge strategies, safety
- archeflow-report.sh: markdown output, summary mode, in-progress handling
- archeflow-progress.sh: progress.md generation, JSON mode, error handling
- archeflow-score.sh: archetype scoring, effectiveness report, validation
- archeflow-dag.sh: DAG rendering, color flags, tree structure
- archeflow-rollback.sh: arg parsing, phase validation, mutual exclusivity
- archeflow-init.sh: template listing, clone from project, arg validation
- archeflow-review.sh: diff modes, stats, branch/commit range review

Includes test_helper.bash (shared setup/teardown with temp git repos)
and scripts/run-tests.sh runner.
2026-04-06 21:20:05 +02:00

228 lines
7.8 KiB
Bash

# Tests for archeflow-memory.sh — cross-run lesson memory management.
#
# Validates: add, list, decay, forget, inject filtering, and JSONL format.
setup() {
load test_helper
_common_setup
}
teardown() {
_common_teardown
}
# --- Usage / error handling ---
@test "memory: exits 1 with usage when called with no args" {
run "$LIB_DIR/archeflow-memory.sh"
[ "$status" -eq 1 ]
[[ "$output" == *"Usage"* ]]
}
@test "memory: exits 1 for unknown command" {
run "$LIB_DIR/archeflow-memory.sh" nonexistent
[ "$status" -eq 1 ]
[[ "$output" == *"Unknown command"* ]]
}
# --- add ---
@test "memory add: creates lessons.jsonl and appends a valid JSONL line" {
run "$LIB_DIR/archeflow-memory.sh" add preference "Always validate inputs"
[ "$status" -eq 0 ]
[ -f ".archeflow/memory/lessons.jsonl" ]
jq empty ".archeflow/memory/lessons.jsonl"
}
@test "memory add: lesson has correct fields" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Guardian misses SQL injection" 2>/dev/null
[ "$(jq -r '.type' .archeflow/memory/lessons.jsonl)" = "pattern" ]
[ "$(jq -r '.description' .archeflow/memory/lessons.jsonl)" = "Guardian misses SQL injection" ]
[ "$(jq -r '.source' .archeflow/memory/lessons.jsonl)" = "user_feedback" ]
[ "$(jq -r '.frequency' .archeflow/memory/lessons.jsonl)" = "1" ]
[ "$(jq -r '.run_id' .archeflow/memory/lessons.jsonl)" = "manual" ]
[ "$(jq -r '.domain' .archeflow/memory/lessons.jsonl)" = "general" ]
}
@test "memory add: generates sequential IDs" {
"$LIB_DIR/archeflow-memory.sh" add pattern "first lesson" 2>/dev/null
"$LIB_DIR/archeflow-memory.sh" add pattern "second lesson" 2>/dev/null
local id1 id2
id1=$(head -1 ".archeflow/memory/lessons.jsonl" | jq -r '.id')
id2=$(tail -1 ".archeflow/memory/lessons.jsonl" | jq -r '.id')
[ "$id1" = "m-001" ]
[ "$id2" = "m-002" ]
}
@test "memory add: generates tags from description" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Guardian misses SQL injection attacks" 2>/dev/null
local tags_count
tags_count=$(head -1 ".archeflow/memory/lessons.jsonl" | jq '.tags | length')
[ "$tags_count" -gt 0 ]
}
@test "memory add: exits 1 when description is missing" {
run "$LIB_DIR/archeflow-memory.sh" add pattern
[ "$status" -eq 1 ]
[[ "$output" == *"Usage"* ]]
}
# --- list ---
@test "memory list: shows message when no lessons exist" {
run bash -c "'$LIB_DIR/archeflow-memory.sh' list 2>&1"
[ "$status" -eq 0 ]
[[ "$output" == *"No lessons"* ]]
}
@test "memory list: shows table header and lesson data" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Test lesson for listing" 2>/dev/null
run "$LIB_DIR/archeflow-memory.sh" list
[ "$status" -eq 0 ]
[[ "$output" == *"ID"* ]]
[[ "$output" == *"Freq"* ]]
[[ "$output" == *"m-001"* ]]
[[ "$output" == *"Test lesson for listing"* ]]
}
# --- decay ---
@test "memory decay: increments runs_since_last_seen" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Decay test lesson" 2>/dev/null
"$LIB_DIR/archeflow-memory.sh" decay 2>/dev/null
local runs_since
runs_since=$(head -1 ".archeflow/memory/lessons.jsonl" | jq '.runs_since_last_seen')
[ "$runs_since" -eq 1 ]
}
@test "memory decay: decrements frequency after 10 runs" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Decay frequency test" 2>/dev/null
# Set frequency=3 and runs_since=9 to trigger decay on next call
local tmp=".archeflow/memory/lessons.jsonl.tmp"
head -1 ".archeflow/memory/lessons.jsonl" | jq -c '.frequency = 3 | .runs_since_last_seen = 9' > "$tmp"
mv "$tmp" ".archeflow/memory/lessons.jsonl"
"$LIB_DIR/archeflow-memory.sh" decay 2>/dev/null
local freq
freq=$(head -1 ".archeflow/memory/lessons.jsonl" | jq '.frequency')
[ "$freq" -eq 2 ]
}
@test "memory decay: archives lesson when frequency reaches 0" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Will be archived" 2>/dev/null
# Set frequency=1 and runs_since=9 to trigger archival
local tmp=".archeflow/memory/lessons.jsonl.tmp"
head -1 ".archeflow/memory/lessons.jsonl" | jq -c '.frequency = 1 | .runs_since_last_seen = 9' > "$tmp"
mv "$tmp" ".archeflow/memory/lessons.jsonl"
"$LIB_DIR/archeflow-memory.sh" decay 2>/dev/null
# Lesson should be gone from lessons file (file should be empty)
local remaining
remaining=$(wc -l < ".archeflow/memory/lessons.jsonl" | tr -d ' ')
[ "$remaining" -eq 0 ]
# And present in archive
[ -f ".archeflow/memory/archive.jsonl" ]
local archived_count
archived_count=$(wc -l < ".archeflow/memory/archive.jsonl" | tr -d ' ')
[ "$archived_count" -eq 1 ]
}
@test "memory decay: does nothing when no lessons exist" {
run "$LIB_DIR/archeflow-memory.sh" decay
[ "$status" -eq 0 ]
}
# --- forget ---
@test "memory forget: moves lesson to archive" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Will forget this" 2>/dev/null
"$LIB_DIR/archeflow-memory.sh" forget m-001 2>/dev/null
# Lessons file should be empty
local remaining
remaining=$(wc -l < ".archeflow/memory/lessons.jsonl" | tr -d ' ')
[ "$remaining" -eq 0 ]
# Archive should have it
[ -f ".archeflow/memory/archive.jsonl" ]
local archived_id
archived_id=$(head -1 ".archeflow/memory/archive.jsonl" | jq -r '.id')
[ "$archived_id" = "m-001" ]
}
@test "memory forget: exits 1 for nonexistent ID" {
"$LIB_DIR/archeflow-memory.sh" add pattern "test" 2>/dev/null
run "$LIB_DIR/archeflow-memory.sh" forget m-999
[ "$status" -eq 1 ]
[[ "$output" == *"not found"* ]]
}
@test "memory forget: exits 1 when no lessons file exists" {
run "$LIB_DIR/archeflow-memory.sh" forget m-001
[ "$status" -eq 1 ]
[[ "$output" == *"No lessons file"* ]]
}
# --- inject ---
@test "memory inject: outputs nothing when no lessons file exists" {
run "$LIB_DIR/archeflow-memory.sh" inject code guardian
[ "$status" -eq 0 ]
[ -z "$output" ]
}
@test "memory inject: outputs relevant lessons with frequency >= 2" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Test injection lesson" 2>/dev/null
# Bump frequency to 2
local tmp=".archeflow/memory/lessons.jsonl.tmp"
jq -c '.frequency = 2' ".archeflow/memory/lessons.jsonl" > "$tmp"
mv "$tmp" ".archeflow/memory/lessons.jsonl"
run "$LIB_DIR/archeflow-memory.sh" inject "" ""
[ "$status" -eq 0 ]
[[ "$output" == *"Known Issues"* ]]
[[ "$output" == *"Test injection lesson"* ]]
}
@test "memory inject: skips lessons with frequency < 2 (except preferences)" {
"$LIB_DIR/archeflow-memory.sh" add pattern "Low frequency lesson" 2>/dev/null
# frequency is 1 by default, type is pattern -> should NOT be injected
run "$LIB_DIR/archeflow-memory.sh" inject "" ""
[ "$status" -eq 0 ]
[ -z "$output" ]
}
@test "memory inject: always injects preferences regardless of frequency" {
"$LIB_DIR/archeflow-memory.sh" add preference "User prefers explicit error messages" 2>/dev/null
run "$LIB_DIR/archeflow-memory.sh" inject "" ""
[ "$status" -eq 0 ]
[[ "$output" == *"User prefers explicit error messages"* ]]
}
# --- extract ---
@test "memory extract: exits 1 when events file not found" {
run "$LIB_DIR/archeflow-memory.sh" extract nonexistent.jsonl
[ "$status" -eq 1 ]
[[ "$output" == *"not found"* ]]
}
@test "memory extract: extracts findings from review.verdict events" {
# Create a mock events file with a review.verdict
mkdir -p .archeflow/events
cat > /tmp/test-events.jsonl <<'EOF'
{"run_id":"test-run","seq":1,"type":"run.start","phase":"plan","data":{"task":"test"}}
{"run_id":"test-run","seq":2,"type":"review.verdict","phase":"check","data":{"archetype":"guardian","verdict":"needs_changes","findings":[{"severity":"warning","description":"Missing input validation on user endpoint","category":"code"}]}}
EOF
run "$LIB_DIR/archeflow-memory.sh" extract /tmp/test-events.jsonl
[ "$status" -eq 0 ]
[ -f ".archeflow/memory/lessons.jsonl" ]
local desc
desc=$(jq -r '.description' ".archeflow/memory/lessons.jsonl")
[[ "$desc" == *"Missing input validation"* ]]
rm -f /tmp/test-events.jsonl
}