#!/usr/bin/env bash # Tests for update_existing_agent_file function # Run: bash .specify/scripts/bash/tests/test-agent-update.sh set -uo pipefail # Note: not using -e so we can continue after failed assertions SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PASSED=0 FAILED=0 # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' NC='\033[0m' # No Color # Test fixtures directory FIXTURES_DIR="$SCRIPT_DIR/fixtures/agent-update" mkdir -p "$FIXTURES_DIR" # Mock logging functions (silence in tests) log_info() { :; } log_error() { echo "ERROR: $1" >&2; } log_warning() { :; } log_success() { :; } # Extract format_technology_stack function eval "$(sed -n '/^format_technology_stack()/,/^}/p' "$SCRIPT_DIR/../update-agent-context.sh")" # Extract update_existing_agent_file function - use more specific pattern # The function ends with a single } on its own line before the next function eval "$(awk '/^update_existing_agent_file\(\)/,/^}$/ {print; if (/^}$/) exit}' "$SCRIPT_DIR/../update-agent-context.sh")" # Test helper: compare files assert_file_eq() { local description="$1" local expected_file="$2" local actual_file="$3" if diff -q "$expected_file" "$actual_file" > /dev/null 2>&1; then echo -e "${GREEN}PASS${NC}: $description" ((PASSED++)) else echo -e "${RED}FAIL${NC}: $description" echo " Expected:" cat "$expected_file" | sed 's/^/ /' echo " Actual:" cat "$actual_file" | sed 's/^/ /' echo " Diff:" diff "$expected_file" "$actual_file" | sed 's/^/ /' || true ((FAILED++)) fi } # Test helper: check file contains pattern assert_contains() { local description="$1" local pattern="$2" local file="$3" if grep -q "$pattern" "$file" 2>/dev/null; then echo -e "${GREEN}PASS${NC}: $description" ((PASSED++)) else echo -e "${RED}FAIL${NC}: $description" echo " Pattern not found: '$pattern'" echo " File contents:" cat "$file" | sed 's/^/ /' ((FAILED++)) fi } # Test helper: check file does NOT contain pattern assert_not_contains() { local description="$1" local pattern="$2" local file="$3" if ! grep -q "$pattern" "$file" 2>/dev/null; then echo -e "${GREEN}PASS${NC}: $description" ((PASSED++)) else echo -e "${RED}FAIL${NC}: $description" echo " Pattern found but should not be: '$pattern'" echo " File contents:" cat "$file" | sed 's/^/ /' ((FAILED++)) fi } # Test helper: count occurrences assert_count() { local description="$1" local expected="$2" local pattern="$3" local file="$4" local actual actual=$(grep -c "$pattern" "$file" 2>/dev/null || echo "0") if [[ "$actual" == "$expected" ]]; then echo -e "${GREEN}PASS${NC}: $description" ((PASSED++)) else echo -e "${RED}FAIL${NC}: $description" echo " Expected count: $expected" echo " Actual count: $actual" echo " Pattern: '$pattern'" ((FAILED++)) fi } # Setup test file setup_test_file() { local content="$1" local test_file test_file=$(mktemp) echo -e "$content" > "$test_file" echo "$test_file" } # Cleanup cleanup_test_file() { rm -f "$1" } echo "=== Agent File Update Tests ===" echo "" # --- Basic Functionality --- echo "## Basic Functionality" # Test: Normal update with both sections NEW_LANG="Python 3.11" NEW_FRAMEWORK="FastAPI" NEW_DB="" CURRENT_BRANCH="feature-auth" test_file=$(setup_test_file "# Project ## Active Technologies - JavaScript + React (main) ## Recent Changes - old-feature: Added logging ## Other Section Some content") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "Adds new tech entry" "Python 3.11 + FastAPI (feature-auth)" "$test_file" assert_contains "Adds new change entry" "feature-auth: Added Python 3.11 + FastAPI" "$test_file" assert_contains "Preserves existing tech entry" "JavaScript + React (main)" "$test_file" assert_contains "Preserves other sections" "## Other Section" "$test_file" cleanup_test_file "$test_file" # --- Missing Sections --- echo "" echo "## Missing Sections" # Test: Missing Active Technologies section NEW_LANG="Rust" NEW_FRAMEWORK="" NEW_DB="" CURRENT_BRANCH="add-rust" test_file=$(setup_test_file "# Project ## Some Other Section Content here ## Recent Changes - old: stuff") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "Creates Active Technologies section" "## Active Technologies" "$test_file" assert_contains "Adds tech to new section" "Rust (add-rust)" "$test_file" cleanup_test_file "$test_file" # Test: Missing Recent Changes section NEW_LANG="Go" NEW_FRAMEWORK="" NEW_DB="" CURRENT_BRANCH="add-go" test_file=$(setup_test_file "# Project ## Active Technologies - Python (main)") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "Creates Recent Changes section" "## Recent Changes" "$test_file" assert_contains "Adds change to new section" "add-go: Added Go" "$test_file" cleanup_test_file "$test_file" # Test: Both sections missing NEW_LANG="TypeScript" NEW_FRAMEWORK="Express" NEW_DB="" CURRENT_BRANCH="add-ts" test_file=$(setup_test_file "# Project Just some content here.") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "Creates Active Technologies when missing" "## Active Technologies" "$test_file" assert_contains "Creates Recent Changes when missing" "## Recent Changes" "$test_file" assert_contains "Adds tech entry" "TypeScript + Express (add-ts)" "$test_file" assert_contains "Adds change entry" "add-ts: Added TypeScript + Express" "$test_file" cleanup_test_file "$test_file" # --- Timestamp Updates --- echo "" echo "## Timestamp Updates" NEW_LANG="" NEW_FRAMEWORK="" NEW_DB="" CURRENT_BRANCH="update-test" test_file=$(setup_test_file "# Project **Last updated**: 2023-06-01 ## Active Technologies - Python (main)") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "Updates timestamp" "2024-01-15" "$test_file" assert_not_contains "Old timestamp removed" "2023-06-01" "$test_file" cleanup_test_file "$test_file" # --- Idempotency --- echo "" echo "## Idempotency" # Test: Don't add duplicate tech entries NEW_LANG="Python" NEW_FRAMEWORK="Django" NEW_DB="" CURRENT_BRANCH="feature-x" test_file=$(setup_test_file "# Project ## Active Technologies - Python + Django (feature-x) ## Recent Changes - feature-x: Added Python + Django") update_existing_agent_file "$test_file" "2024-01-15" # Check tech section specifically - should only have one tech entry line assert_count "No duplicate tech entry in Active Technologies" "1" "^- Python + Django (feature-x)$" "$test_file" cleanup_test_file "$test_file" # --- Change Entry Limits --- echo "" echo "## Change Entry Limits (max 2 kept)" NEW_LANG="Rust" NEW_FRAMEWORK="" NEW_DB="" CURRENT_BRANCH="new-feature" test_file=$(setup_test_file "# Project ## Recent Changes - old-1: First old change - old-2: Second old change - old-3: Third old change - old-4: Fourth old change ## Other Section") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "New change added first" "new-feature: Added Rust" "$test_file" assert_contains "Keeps first old change" "old-1: First old change" "$test_file" assert_contains "Keeps second old change" "old-2: Second old change" "$test_file" assert_not_contains "Removes third old change" "old-3:" "$test_file" assert_not_contains "Removes fourth old change" "old-4:" "$test_file" cleanup_test_file "$test_file" # --- Database Entries --- echo "" echo "## Database Entries" NEW_LANG="" NEW_FRAMEWORK="" NEW_DB="PostgreSQL" CURRENT_BRANCH="add-db" test_file=$(setup_test_file "# Project ## Active Technologies - Python (main) ## Recent Changes - old: stuff") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "Adds database entry to tech section" "PostgreSQL (add-db)" "$test_file" assert_contains "Adds database to change entry" "add-db: Added PostgreSQL" "$test_file" cleanup_test_file "$test_file" # Test: DB with N/A value not added NEW_LANG="" NEW_FRAMEWORK="" NEW_DB="N/A" CURRENT_BRANCH="no-db" test_file=$(setup_test_file "# Project ## Active Technologies - Python (main)") update_existing_agent_file "$test_file" "2024-01-15" assert_not_contains "N/A database not added" "N/A (no-db)" "$test_file" cleanup_test_file "$test_file" # --- Tech Entry Placement --- echo "" echo "## Tech Entry Placement" # Test: Tech entries added before next section header NEW_LANG="Java" NEW_FRAMEWORK="" NEW_DB="" CURRENT_BRANCH="add-java" test_file=$(setup_test_file "## Active Technologies - Python (main) ## Next Section content") update_existing_agent_file "$test_file" "2024-01-15" # The Java entry should appear between Python and Next Section assert_contains "Tech entry added to section" "Java (add-java)" "$test_file" # Verify order: Java should come after Python line if grep -n "Python" "$test_file" | head -1 | cut -d: -f1 > /tmp/python_line && \ grep -n "Java" "$test_file" | head -1 | cut -d: -f1 > /tmp/java_line; then python_line=$(cat /tmp/python_line) java_line=$(cat /tmp/java_line) if [[ "$java_line" -gt "$python_line" ]]; then echo -e "${GREEN}PASS${NC}: Tech entry appears after existing entries" ((PASSED++)) else echo -e "${RED}FAIL${NC}: Tech entry appears after existing entries" echo " Java at line $java_line, Python at line $python_line" ((FAILED++)) fi rm -f /tmp/python_line /tmp/java_line fi cleanup_test_file "$test_file" # Test: Tech entries added before blank line in section NEW_LANG="Ruby" NEW_FRAMEWORK="" NEW_DB="" CURRENT_BRANCH="add-ruby" test_file=$(setup_test_file "## Active Technologies - Python (main) ## Other Section") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "Tech entry added before blank line" "Ruby (add-ruby)" "$test_file" cleanup_test_file "$test_file" # --- Edge Cases --- echo "" echo "## Edge Cases" # Test: Empty NEW_LANG and NEW_FRAMEWORK (no tech to add) NEW_LANG="" NEW_FRAMEWORK="" NEW_DB="" CURRENT_BRANCH="no-tech" test_file=$(setup_test_file "# Project ## Active Technologies - Python (main) ## Recent Changes - old: change") update_existing_agent_file "$test_file" "2024-01-15" assert_not_contains "No empty tech entry added" "( no-tech)" "$test_file" cleanup_test_file "$test_file" # Test: NEEDS CLARIFICATION values not added NEW_LANG="NEEDS CLARIFICATION" NEW_FRAMEWORK="" NEW_DB="NEEDS CLARIFICATION" CURRENT_BRANCH="unclear" test_file=$(setup_test_file "# Project ## Active Technologies - Python (main)") update_existing_agent_file "$test_file" "2024-01-15" assert_not_contains "NEEDS CLARIFICATION language not added" "NEEDS CLARIFICATION" "$test_file" cleanup_test_file "$test_file" # Test: File ending with tech section (no next header) NEW_LANG="Scala" NEW_FRAMEWORK="" NEW_DB="" CURRENT_BRANCH="add-scala" test_file=$(setup_test_file "# Project ## Active Technologies - Python (main)") update_existing_agent_file "$test_file" "2024-01-15" assert_contains "Tech added to section at EOF" "Scala (add-scala)" "$test_file" cleanup_test_file "$test_file" # --- Format Technology Stack --- echo "" echo "## format_technology_stack Function" result=$(format_technology_stack "Python" "Flask") if [[ "$result" == "Python + Flask" ]]; then echo -e "${GREEN}PASS${NC}: format_technology_stack with lang and framework" ((PASSED++)) else echo -e "${RED}FAIL${NC}: format_technology_stack with lang and framework" echo " Expected: 'Python + Flask'" echo " Actual: '$result'" ((FAILED++)) fi result=$(format_technology_stack "Python" "") if [[ "$result" == "Python" ]]; then echo -e "${GREEN}PASS${NC}: format_technology_stack with lang only" ((PASSED++)) else echo -e "${RED}FAIL${NC}: format_technology_stack with lang only" echo " Expected: 'Python'" echo " Actual: '$result'" ((FAILED++)) fi result=$(format_technology_stack "" "") if [[ "$result" == "" ]]; then echo -e "${GREEN}PASS${NC}: format_technology_stack with nothing" ((PASSED++)) else echo -e "${RED}FAIL${NC}: format_technology_stack with nothing" echo " Expected: ''" echo " Actual: '$result'" ((FAILED++)) fi result=$(format_technology_stack "NEEDS CLARIFICATION" "Flask") if [[ "$result" == "Flask" ]]; then echo -e "${GREEN}PASS${NC}: format_technology_stack filters NEEDS CLARIFICATION" ((PASSED++)) else echo -e "${RED}FAIL${NC}: format_technology_stack filters NEEDS CLARIFICATION" echo " Expected: 'Flask'" echo " Actual: '$result'" ((FAILED++)) fi # --- Summary --- echo "" echo "=== Summary ===" echo -e "Passed: ${GREEN}$PASSED${NC}" echo -e "Failed: ${RED}$FAILED${NC}" # Cleanup fixtures rm -rf "$FIXTURES_DIR" if [[ $FAILED -gt 0 ]]; then exit 1 fi