#!/usr/bin/env bash # Tests for deploy-skill.sh config injection functions # Run: bash bin/tests/test-deploy-skill.sh set -uo pipefail 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 # Extract inject_nix_config function eval "$(sed -n '/^inject_nix_config()/,/^}/p' "$SCRIPT_DIR/../deploy-skill.sh")" # Extract inject_home_file function eval "$(sed -n '/^inject_home_file()/,/^}/p' "$SCRIPT_DIR/../deploy-skill.sh")" # Test helpers 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 } 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'" ((FAILED++)) fi } 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, Actual: $actual" echo " Pattern: '$pattern'" ((FAILED++)) fi } assert_last_line() { local description="$1" local expected="$2" local file="$3" local actual actual=$(tail -n 1 "$file") if [[ "$actual" == "$expected" ]]; then echo -e "${GREEN}PASS${NC}: $description" ((PASSED++)) else echo -e "${RED}FAIL${NC}: $description" echo " Expected last line: '$expected'" echo " Actual last line: '$actual'" ((FAILED++)) fi } assert_output_contains() { local description="$1" local pattern="$2" local output="$3" if echo "$output" | grep -q "$pattern"; then echo -e "${GREEN}PASS${NC}: $description" ((PASSED++)) else echo -e "${RED}FAIL${NC}: $description" echo " Pattern not found: '$pattern'" echo " Output was: $output" ((FAILED++)) fi } # Setup: create a minimal Nix file setup_nix_file() { local content="$1" local test_file test_file=$(mktemp) echo -e "$content" > "$test_file" echo "$test_file" } cleanup() { rm -f "$1" } echo "=== Deploy Skill Config Injection Tests ===" echo "" # --- Basic Injection --- echo "## Basic Injection" # Test: Config injected before closing brace test_file=$(setup_nix_file "{ config, pkgs, ... }: { home.packages = [ pkgs.git ]; }") inject_nix_config "$test_file" " # Added config home.file.\"test\" = { source = ./test; };" "home.file.\"test\"" > /dev/null assert_contains "Config block injected" "home.file.\"test\"" "$test_file" assert_last_line "Closing brace preserved at end" "}" "$test_file" assert_contains "Original content preserved" "home.packages" "$test_file" cleanup "$test_file" # --- Idempotency --- echo "" echo "## Idempotency" # Test: Running twice doesn't duplicate test_file=$(setup_nix_file "{ config, pkgs, ... }: { home.packages = [ ]; }") inject_nix_config "$test_file" " home.file.\"skill-a\" = { source = ./a; };" "skill-a" > /dev/null inject_nix_config "$test_file" " home.file.\"skill-a\" = { source = ./a; };" "skill-a" > /dev/null assert_count "Config not duplicated" "1" "skill-a" "$test_file" cleanup "$test_file" # Test: Different configs can be added test_file=$(setup_nix_file "{ config, pkgs, ... }: { }") inject_nix_config "$test_file" " home.file.\"skill-b\" = {};" "skill-b" > /dev/null inject_nix_config "$test_file" " home.file.\"skill-c\" = {};" "skill-c" > /dev/null assert_contains "First config present" "skill-b" "$test_file" assert_contains "Second config present" "skill-c" "$test_file" assert_count "Each config appears once" "1" "skill-b" "$test_file" assert_count "Each config appears once (c)" "1" "skill-c" "$test_file" cleanup "$test_file" # --- File Not Found --- echo "" echo "## File Not Found Handling" output=$(inject_nix_config "/nonexistent/path/file.nix" "config" "marker" 2>&1) assert_output_contains "Skips missing file gracefully" "skipping" "$output" # --- Brace Structure --- echo "" echo "## Brace Structure Preservation" # Test: Complex nested structure test_file=$(setup_nix_file "{ config, pkgs, ... }: { programs.git = { enable = true; userName = \"test\"; }; home.packages = with pkgs; [ ripgrep fd ]; }") inject_nix_config "$test_file" " # New skill home.file.\".skill\" = { source = ./skill; recursive = true; };" ".skill" > /dev/null assert_last_line "Closing brace still at end after complex inject" "}" "$test_file" assert_contains "Nested braces preserved" "programs.git" "$test_file" assert_contains "New config added" ".skill" "$test_file" cleanup "$test_file" # --- inject_home_file Wrapper --- echo "" echo "## inject_home_file Wrapper" test_file=$(setup_nix_file "{ config, pkgs, ... }: { }") inject_home_file "$test_file" \ ".claude/skills/my-skill" \ "../claude/skills/my-skill" \ "recursive = true;" \ "my-skill" > /dev/null assert_contains "Home path in config" ".claude/skills/my-skill" "$test_file" assert_contains "Source path in config" "../claude/skills/my-skill" "$test_file" assert_contains "Extra props included" "recursive = true" "$test_file" assert_contains "Comment included" "# Skill: my-skill" "$test_file" cleanup "$test_file" # Test: inject_home_file idempotency test_file=$(setup_nix_file "{ config, pkgs, ... }: { }") inject_home_file "$test_file" ".test/path" "./source" "" "test" > /dev/null inject_home_file "$test_file" ".test/path" "./source" "" "test" > /dev/null assert_count "inject_home_file idempotent" "1" ".test/path" "$test_file" cleanup "$test_file" # --- Already Present Detection --- echo "" echo "## Already Present Detection" test_file=$(setup_nix_file "{ config, pkgs, ... }: { # Existing skill home.file.\".claude/skills/existing\" = { source = ./existing; }; }") output=$(inject_nix_config "$test_file" "new config" ".claude/skills/existing" 2>&1) assert_output_contains "Detects existing config" "already present" "$output" cleanup "$test_file" # --- Empty Extra Props --- echo "" echo "## Edge Cases" # Test: Empty extra props test_file=$(setup_nix_file "{ config, pkgs, ... }: { }") inject_home_file "$test_file" ".simple/path" "./src" "" "simple" > /dev/null assert_contains "Works with empty extra props" ".simple/path" "$test_file" cleanup "$test_file" # Test: Single line file (edge case - script assumes multi-line) test_file=$(setup_nix_file "{}") inject_nix_config "$test_file" " config = true;" "config" > /dev/null assert_contains "Works with minimal file" "config = true" "$test_file" # Note: Single-line "{}" becomes last line since head -n -1 returns empty # This is acceptable - real Nix files are always multi-line assert_last_line "Original content preserved as last line" "{}" "$test_file" cleanup "$test_file" # --- Summary --- echo "" echo "=== Summary ===" echo -e "Passed: ${GREEN}$PASSED${NC}" echo -e "Failed: ${RED}$FAILED${NC}" if [[ $FAILED -gt 0 ]]; then exit 1 fi