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>
234 lines
7.9 KiB
Bash
Executable file
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
|