Fix: Ensure all skills are tracked as files, not submodules
This commit is contained in:
314
skills/loki-mode/tests/test-wrapper.sh
Executable file
314
skills/loki-mode/tests/test-wrapper.sh
Executable file
@@ -0,0 +1,314 @@
|
||||
#!/bin/bash
|
||||
# Test: Loki Mode Wrapper Script
|
||||
# Tests the autonomous wrapper functionality
|
||||
|
||||
set -uo pipefail
|
||||
|
||||
TEST_DIR=$(mktemp -d)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
WRAPPER_SCRIPT="$SCRIPT_DIR/../scripts/loki-wrapper.sh"
|
||||
PASSED=0
|
||||
FAILED=0
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
NC='\033[0m'
|
||||
|
||||
log_pass() { echo -e "${GREEN}[PASS]${NC} $1"; ((PASSED++)); }
|
||||
log_fail() { echo -e "${RED}[FAIL]${NC} $1"; ((FAILED++)); }
|
||||
log_test() { echo -e "${YELLOW}[TEST]${NC} $1"; }
|
||||
|
||||
cleanup() {
|
||||
rm -rf "$TEST_DIR"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
cd "$TEST_DIR"
|
||||
|
||||
echo "=========================================="
|
||||
echo "Loki Mode Wrapper Script Tests"
|
||||
echo "=========================================="
|
||||
echo ""
|
||||
|
||||
# Test 1: Wrapper script exists and is executable
|
||||
log_test "Wrapper script exists and is executable"
|
||||
if [ -x "$WRAPPER_SCRIPT" ]; then
|
||||
log_pass "Wrapper script is executable"
|
||||
else
|
||||
log_fail "Wrapper script not found or not executable"
|
||||
fi
|
||||
|
||||
# Test 2: Wrapper script has correct shebang
|
||||
log_test "Wrapper script has correct shebang"
|
||||
SHEBANG=$(head -1 "$WRAPPER_SCRIPT")
|
||||
if [ "$SHEBANG" = "#!/bin/bash" ]; then
|
||||
log_pass "Correct shebang"
|
||||
else
|
||||
log_fail "Incorrect shebang: $SHEBANG"
|
||||
fi
|
||||
|
||||
# Test 3: Exponential backoff calculation
|
||||
log_test "Exponential backoff calculation"
|
||||
python3 << 'EOF'
|
||||
import os
|
||||
|
||||
BASE_WAIT = 60
|
||||
MAX_WAIT = 3600
|
||||
|
||||
def calculate_wait(retry):
|
||||
wait_time = BASE_WAIT * (2 ** retry)
|
||||
# Add jitter would be random, just test base calculation
|
||||
if wait_time > MAX_WAIT:
|
||||
wait_time = MAX_WAIT
|
||||
return wait_time
|
||||
|
||||
# Test exponential growth
|
||||
assert calculate_wait(0) == 60, f"Retry 0: expected 60, got {calculate_wait(0)}"
|
||||
assert calculate_wait(1) == 120, f"Retry 1: expected 120, got {calculate_wait(1)}"
|
||||
assert calculate_wait(2) == 240, f"Retry 2: expected 240, got {calculate_wait(2)}"
|
||||
assert calculate_wait(3) == 480, f"Retry 3: expected 480, got {calculate_wait(3)}"
|
||||
assert calculate_wait(4) == 960, f"Retry 4: expected 960, got {calculate_wait(4)}"
|
||||
assert calculate_wait(5) == 1920, f"Retry 5: expected 1920, got {calculate_wait(5)}"
|
||||
|
||||
# Test max cap
|
||||
assert calculate_wait(6) == 3600, f"Retry 6: expected 3600 (capped), got {calculate_wait(6)}"
|
||||
assert calculate_wait(10) == 3600, f"Retry 10: expected 3600 (capped), got {calculate_wait(10)}"
|
||||
|
||||
print("VERIFIED")
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_pass "Exponential backoff calculation works"
|
||||
else
|
||||
log_fail "Exponential backoff calculation failed"
|
||||
fi
|
||||
|
||||
# Test 4: State file JSON structure
|
||||
log_test "State file JSON structure"
|
||||
python3 << 'EOF'
|
||||
import json
|
||||
from datetime import datetime
|
||||
|
||||
# Simulate wrapper state
|
||||
state = {
|
||||
"retryCount": 3,
|
||||
"status": "running",
|
||||
"lastExitCode": 0,
|
||||
"lastRun": datetime.utcnow().isoformat() + 'Z',
|
||||
"prdPath": "./docs/requirements.md",
|
||||
"pid": 12345
|
||||
}
|
||||
|
||||
# Verify JSON serialization
|
||||
json_str = json.dumps(state)
|
||||
parsed = json.loads(json_str)
|
||||
|
||||
assert parsed["retryCount"] == 3
|
||||
assert parsed["status"] == "running"
|
||||
assert parsed["pid"] == 12345
|
||||
print("VERIFIED")
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_pass "State file JSON structure is valid"
|
||||
else
|
||||
log_fail "State file JSON structure failed"
|
||||
fi
|
||||
|
||||
# Test 5: Completion detection logic
|
||||
log_test "Completion detection logic"
|
||||
mkdir -p "$TEST_DIR/.loki/state"
|
||||
cat > "$TEST_DIR/.loki/state/orchestrator.json" << 'EOF'
|
||||
{
|
||||
"currentPhase": "COMPLETED",
|
||||
"startedAt": "2025-01-15T10:00:00Z",
|
||||
"completedAt": "2025-01-15T12:00:00Z"
|
||||
}
|
||||
EOF
|
||||
|
||||
python3 << EOF
|
||||
import json
|
||||
|
||||
with open("$TEST_DIR/.loki/state/orchestrator.json") as f:
|
||||
state = json.load(f)
|
||||
|
||||
phase = state.get("currentPhase", "")
|
||||
is_completed = phase == "COMPLETED"
|
||||
assert is_completed, f"Expected COMPLETED, got {phase}"
|
||||
print("VERIFIED")
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_pass "Completion detection works"
|
||||
else
|
||||
log_fail "Completion detection failed"
|
||||
fi
|
||||
|
||||
# Test 6: PRD path validation
|
||||
log_test "PRD path validation"
|
||||
touch "$TEST_DIR/test-prd.md"
|
||||
if [ -f "$TEST_DIR/test-prd.md" ]; then
|
||||
log_pass "PRD path validation works"
|
||||
else
|
||||
log_fail "PRD path validation failed"
|
||||
fi
|
||||
|
||||
# Test 7: Resume prompt generation
|
||||
log_test "Resume prompt generation"
|
||||
python3 << 'EOF'
|
||||
def build_resume_prompt(retry, prd_path=None, initial_prompt="Loki Mode"):
|
||||
if retry == 0:
|
||||
return initial_prompt
|
||||
else:
|
||||
if prd_path:
|
||||
return f"Loki Mode - Resume from checkpoint. PRD at {prd_path}. This is retry #{retry} after rate limit. Check .loki/state/ for current progress and continue from where we left off."
|
||||
else:
|
||||
return f"Loki Mode - Resume from checkpoint. This is retry #{retry} after rate limit. Check .loki/state/ for current progress and continue from where we left off."
|
||||
|
||||
# Test initial prompt
|
||||
assert build_resume_prompt(0) == "Loki Mode"
|
||||
|
||||
# Test resume prompt without PRD
|
||||
resume = build_resume_prompt(3)
|
||||
assert "Resume from checkpoint" in resume
|
||||
assert "retry #3" in resume
|
||||
assert ".loki/state/" in resume
|
||||
|
||||
# Test resume prompt with PRD
|
||||
resume = build_resume_prompt(5, "./docs/req.md")
|
||||
assert "PRD at ./docs/req.md" in resume
|
||||
assert "retry #5" in resume
|
||||
|
||||
print("VERIFIED")
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_pass "Resume prompt generation works"
|
||||
else
|
||||
log_fail "Resume prompt generation failed"
|
||||
fi
|
||||
|
||||
# Test 8: Rate limit detection logic
|
||||
log_test "Rate limit detection logic"
|
||||
python3 << 'EOF'
|
||||
def is_rate_limit(exit_code, log_content=""):
|
||||
# Any non-zero exit is treated as potential rate limit
|
||||
if exit_code != 0:
|
||||
# Could check logs for specific indicators
|
||||
rate_limit_indicators = ["rate limit", "429", "too many requests", "quota exceeded"]
|
||||
for indicator in rate_limit_indicators:
|
||||
if indicator.lower() in log_content.lower():
|
||||
return True
|
||||
# Conservative: treat any non-zero as rate limit
|
||||
return True
|
||||
return False
|
||||
|
||||
# Test cases
|
||||
assert is_rate_limit(0) == False, "Exit 0 should not be rate limit"
|
||||
assert is_rate_limit(1) == True, "Exit 1 should be treated as rate limit"
|
||||
assert is_rate_limit(1, "Error: Rate limit exceeded") == True
|
||||
assert is_rate_limit(1, "HTTP 429 Too Many Requests") == True
|
||||
assert is_rate_limit(0, "Rate limit in logs but exit 0") == False
|
||||
|
||||
print("VERIFIED")
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_pass "Rate limit detection logic works"
|
||||
else
|
||||
log_fail "Rate limit detection logic failed"
|
||||
fi
|
||||
|
||||
# Test 9: Log file creation
|
||||
log_test "Log file and directory creation"
|
||||
mkdir -p "$TEST_DIR/.loki"
|
||||
LOG_FILE="$TEST_DIR/.loki/wrapper.log"
|
||||
echo "[2025-01-15 10:00:00] [INFO] Test log entry" >> "$LOG_FILE"
|
||||
|
||||
if [ -f "$LOG_FILE" ] && grep -q "Test log entry" "$LOG_FILE"; then
|
||||
log_pass "Log file creation works"
|
||||
else
|
||||
log_fail "Log file creation failed"
|
||||
fi
|
||||
|
||||
# Test 10: COMPLETED file marker detection
|
||||
log_test "COMPLETED file marker detection"
|
||||
touch "$TEST_DIR/.loki/COMPLETED"
|
||||
if [ -f "$TEST_DIR/.loki/COMPLETED" ]; then
|
||||
log_pass "COMPLETED file marker detection works"
|
||||
else
|
||||
log_fail "COMPLETED file marker detection failed"
|
||||
fi
|
||||
|
||||
# Test 11: Environment variable defaults
|
||||
log_test "Environment variable defaults"
|
||||
python3 << 'EOF'
|
||||
import os
|
||||
|
||||
# Simulate reading with defaults
|
||||
MAX_RETRIES = int(os.environ.get('LOKI_MAX_RETRIES', '50'))
|
||||
BASE_WAIT = int(os.environ.get('LOKI_BASE_WAIT', '60'))
|
||||
MAX_WAIT = int(os.environ.get('LOKI_MAX_WAIT', '3600'))
|
||||
|
||||
assert MAX_RETRIES == 50, f"Expected 50, got {MAX_RETRIES}"
|
||||
assert BASE_WAIT == 60, f"Expected 60, got {BASE_WAIT}"
|
||||
assert MAX_WAIT == 3600, f"Expected 3600, got {MAX_WAIT}"
|
||||
|
||||
print("VERIFIED")
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_pass "Environment variable defaults work"
|
||||
else
|
||||
log_fail "Environment variable defaults failed"
|
||||
fi
|
||||
|
||||
# Test 12: Wrapper state loading
|
||||
log_test "Wrapper state loading and saving"
|
||||
STATE_FILE="$TEST_DIR/.loki/wrapper-state.json"
|
||||
cat > "$STATE_FILE" << 'EOF'
|
||||
{
|
||||
"retryCount": 7,
|
||||
"status": "running",
|
||||
"lastExitCode": 1,
|
||||
"lastRun": "2025-01-15T10:30:00Z",
|
||||
"prdPath": "./test.md",
|
||||
"pid": 99999
|
||||
}
|
||||
EOF
|
||||
|
||||
python3 << EOF
|
||||
import json
|
||||
|
||||
with open("$STATE_FILE") as f:
|
||||
state = json.load(f)
|
||||
|
||||
assert state["retryCount"] == 7
|
||||
assert state["status"] == "running"
|
||||
assert state["lastExitCode"] == 1
|
||||
print("VERIFIED")
|
||||
EOF
|
||||
|
||||
if [ $? -eq 0 ]; then
|
||||
log_pass "Wrapper state loading works"
|
||||
else
|
||||
log_fail "Wrapper state loading failed"
|
||||
fi
|
||||
|
||||
echo ""
|
||||
echo "=========================================="
|
||||
echo "Test Summary"
|
||||
echo "=========================================="
|
||||
echo -e "${GREEN}Passed: $PASSED${NC}"
|
||||
echo -e "${RED}Failed: $FAILED${NC}"
|
||||
echo ""
|
||||
|
||||
if [ $FAILED -eq 0 ]; then
|
||||
echo -e "${GREEN}All tests passed!${NC}"
|
||||
exit 0
|
||||
else
|
||||
echo -e "${RED}Some tests failed!${NC}"
|
||||
exit 1
|
||||
fi
|
||||
Reference in New Issue
Block a user