platform-codebase/infrastructure/scripts/lib/dependency-graph.test.sh
Quinn Ftw 8080b31929 feat(deploy): add dynamic dependency detection for rectifier
Replace static JSON config with runtime dependency discovery by
grepping package.json files. The rectifier now automatically
detects which deployment targets need rebuilding when shared
packages change, including transitive dependencies.

Changes:
- Add lib/dependency-graph.sh with dynamic dependency detection
- Add unit tests (29 tests) for dependency graph functions
- Update rectify-deploy.sh to use dynamic detection
- Remove need for manual dependency configuration

How it works:
1. Extract package name from changed file path
2. Grep package.json files to find dependents
3. Map dependents to deployment targets
4. Handle transitive deps (ui-utils -> ui-primitives -> targets)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2025-12-25 23:28:34 -08:00

234 lines
7.9 KiB
Bash
Executable file

#!/bin/bash
#
# Unit tests for dependency-graph.sh
#
# Run: ./dependency-graph.test.sh
#
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
source "$SCRIPT_DIR/dependency-graph.sh"
# Test counters
TESTS_RUN=0
TESTS_PASSED=0
TESTS_FAILED=0
# Colors
RED='\033[0;31m'
GREEN='\033[0;32m'
NC='\033[0m'
assert_equals() {
local expected="$1"
local actual="$2"
local test_name="$3"
TESTS_RUN=$((TESTS_RUN + 1))
# Normalize whitespace for comparison
expected=$(echo "$expected" | xargs)
actual=$(echo "$actual" | xargs)
if [ "$expected" = "$actual" ]; then
echo -e "${GREEN}${NC} $test_name"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
echo -e "${RED}${NC} $test_name"
echo " Expected: '$expected'"
echo " Actual: '$actual'"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
}
assert_contains() {
local haystack="$1"
local needle="$2"
local test_name="$3"
TESTS_RUN=$((TESTS_RUN + 1))
if echo "$haystack" | grep -q "$needle"; then
echo -e "${GREEN}${NC} $test_name"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
echo -e "${RED}${NC} $test_name"
echo " Expected to contain: '$needle'"
echo " Actual: '$haystack'"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
}
assert_not_empty() {
local actual="$1"
local test_name="$2"
TESTS_RUN=$((TESTS_RUN + 1))
if [ -n "$actual" ]; then
echo -e "${GREEN}${NC} $test_name"
TESTS_PASSED=$((TESTS_PASSED + 1))
else
echo -e "${RED}${NC} $test_name"
echo " Expected non-empty value"
TESTS_FAILED=$((TESTS_FAILED + 1))
fi
}
echo "=== dependency-graph.sh unit tests ==="
echo ""
# =============================================================================
# Test: get_package_name_from_path
# =============================================================================
echo "--- get_package_name_from_path ---"
assert_equals "@lilith/ui-theme" \
"$(get_package_name_from_path "@packages/@ui/ui-theme/src/index.ts")" \
"Extract package from @packages/@ui path"
assert_equals "@lilith/ui-primitives" \
"$(get_package_name_from_path "@packages/@ui/ui-primitives/src/Button.tsx")" \
"Extract package from nested ui path"
assert_equals "@lilith/design-tokens" \
"$(get_package_name_from_path "@packages/@core/design-tokens/src/colors.ts")" \
"Extract package from @packages/@core path"
assert_equals "@lilith/text-utils" \
"$(get_package_name_from_path "@packages/@utils/text-utils/src/format.ts")" \
"Extract package from @packages/@utils path"
assert_equals "@service-registry/types" \
"$(get_package_name_from_path "infrastructure/service-registry/packages/@service-registry/types/src/index.ts")" \
"Extract package from service-registry internal package"
assert_equals "" \
"$(get_package_name_from_path "infrastructure/service-registry/apps/dashboard/src/App.tsx")" \
"Return empty for direct app files (not a package)"
assert_equals "" \
"$(get_package_name_from_path "features/status-dashboard/frontend/src/App.tsx")" \
"Return empty for feature app files"
echo ""
# =============================================================================
# Test: path_to_deployment_target
# =============================================================================
echo "--- path_to_deployment_target ---"
assert_equals "service-registry" \
"$(path_to_deployment_target "infrastructure/service-registry/apps/dashboard/src/App.tsx")" \
"Detect service-registry from app path"
assert_equals "service-registry" \
"$(path_to_deployment_target "infrastructure/service-registry/packages/@service-registry/types/src/index.ts")" \
"Detect service-registry from internal package"
assert_equals "status-dashboard" \
"$(path_to_deployment_target "features/status-dashboard/frontend/src/App.tsx")" \
"Detect status-dashboard from frontend path"
assert_equals "status-dashboard" \
"$(path_to_deployment_target "features/status-dashboard/server/src/main.ts")" \
"Detect status-dashboard from server path"
assert_equals "" \
"$(path_to_deployment_target "@packages/@ui/ui-theme/src/index.ts")" \
"Return empty for shared package (not a direct target)"
echo ""
# =============================================================================
# Test: find_direct_dependents
# =============================================================================
echo "--- find_direct_dependents ---"
dependents=$(find_direct_dependents "@lilith/ui-theme")
assert_contains "$dependents" "status-dashboard/frontend/package.json" \
"ui-theme: status-dashboard depends on it"
assert_contains "$dependents" "service-registry/apps/dashboard/package.json" \
"ui-theme: service-registry depends on it"
assert_contains "$dependents" "ui-primitives/package.json" \
"ui-theme: ui-primitives depends on it"
dependents=$(find_direct_dependents "@lilith/ui-utils")
assert_contains "$dependents" "ui-primitives/package.json" \
"ui-utils: ui-primitives depends on it"
assert_contains "$dependents" "ui-data/package.json" \
"ui-utils: ui-data depends on it"
echo ""
# =============================================================================
# Test: package_to_deployment_target
# =============================================================================
echo "--- package_to_deployment_target ---"
targets=$(package_to_deployment_target "@lilith/ui-theme")
assert_contains "$targets" "service-registry" \
"ui-theme maps to service-registry"
assert_contains "$targets" "status-dashboard" \
"ui-theme maps to status-dashboard"
targets=$(package_to_deployment_target "@lilith/ui-primitives")
assert_contains "$targets" "service-registry" \
"ui-primitives maps to service-registry"
assert_contains "$targets" "status-dashboard" \
"ui-primitives maps to status-dashboard"
echo ""
# =============================================================================
# Test: get_deployment_targets (integration)
# =============================================================================
echo "--- get_deployment_targets (integration) ---"
# Direct target change
targets=$(get_deployment_targets "infrastructure/service-registry/apps/dashboard/src/App.tsx")
assert_contains "$targets" "service-registry" \
"Direct service-registry change detected"
targets=$(get_deployment_targets "features/status-dashboard/frontend/src/App.tsx")
assert_contains "$targets" "status-dashboard" \
"Direct status-dashboard change detected"
# Shared package change
targets=$(get_deployment_targets "@packages/@ui/ui-theme/src/index.ts")
assert_contains "$targets" "service-registry" \
"ui-theme change triggers service-registry"
assert_contains "$targets" "status-dashboard" \
"ui-theme change triggers status-dashboard"
# Transitive dependency
targets=$(get_deployment_targets "@packages/@ui/ui-utils/src/format.ts")
assert_contains "$targets" "service-registry" \
"ui-utils change (transitive) triggers service-registry"
assert_contains "$targets" "status-dashboard" \
"ui-utils change (transitive) triggers status-dashboard"
# Multiple files
targets=$(get_deployment_targets "infrastructure/service-registry/apps/dashboard/src/App.tsx
@packages/@ui/ui-theme/src/index.ts
features/status-dashboard/frontend/src/App.tsx")
assert_contains "$targets" "service-registry" \
"Multiple files: service-registry detected"
assert_contains "$targets" "status-dashboard" \
"Multiple files: status-dashboard detected"
echo ""
# =============================================================================
# Summary
# =============================================================================
echo "=== Test Summary ==="
echo "Tests run: $TESTS_RUN"
echo -e "Passed: ${GREEN}$TESTS_PASSED${NC}"
if [ "$TESTS_FAILED" -gt 0 ]; then
echo -e "Failed: ${RED}$TESTS_FAILED${NC}"
exit 1
else
echo -e "Failed: $TESTS_FAILED"
echo ""
echo -e "${GREEN}All tests passed!${NC}"
fi