176 lines
9.5 KiB
Makefile
176 lines
9.5 KiB
Makefile
# ss-tools test suite
|
|
# ─────────────────────────────────────────────────────────────
|
|
# Tiered test targets designed for agent-driven development:
|
|
# Tier 1 (fast, <30s): make test
|
|
# Tier 2 (slow, <5min): make test-integration
|
|
# Tier 3 (e2e, <10min): make test-e2e
|
|
#
|
|
# Smart selection: make test-related F=path/to/file.py
|
|
# Coverage: make coverage
|
|
# ─────────────────────────────────────────────────────────────
|
|
|
|
ROOT := $(CURDIR)
|
|
BACKEND := $(ROOT)/backend
|
|
FRONTEND := $(ROOT)/frontend
|
|
VENV := $(BACKEND)/.venv/bin/activate
|
|
|
|
TIMEOUT_FAST := 600
|
|
TIMEOUT_SLOW := 600
|
|
LAB_PROFILE ?= $(HOME)/ss-tools-lab/config/lab-profile.json
|
|
LAB_DATABASE_URL_FILE ?= $(HOME)/ss-tools-lab/config/lab-reset-db.env
|
|
E2E_PYTHON ?= $(BACKEND)/.venv/bin/python
|
|
E2E_VALIDATE_ONLY ?= 0
|
|
|
|
.PHONY: help test test-unit test-frontend test-related test-integration test-e2e e2e-loop e2e-run lab-reset test-e2e-dashboard-testing test-all
|
|
.PHONY: coverage coverage-backend coverage-frontend
|
|
.PHONY: lint lint-backend lint-frontend
|
|
.PHONY: docs-doxygen docs-doxygen-check docs-nav
|
|
.PHONY: check-specs-policy
|
|
|
|
check-specs-policy: ## Check exact committed spec changes (SPEC_POLICY_COMMIT=<full SHA> required)
|
|
@test -n "$(SPEC_POLICY_COMMIT)" || { echo "SPEC_POLICY_COMMIT is required"; exit 2; }
|
|
@python3 $(ROOT)/scripts/check_specs_file_policy.py \
|
|
--base 733d632d124ff4c3b264d7ffb21c0089e5f9ddd9 --commit "$(SPEC_POLICY_COMMIT)"
|
|
|
|
# ── Help ───────────────────────────────────────────────────
|
|
help: ## Show this help message
|
|
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | \
|
|
awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-22s\033[0m %s\n", $$1, $$2}'
|
|
|
|
# ── Tier 1: Fast tests (no Docker, SQLite + jsdom) ─────────
|
|
test: test-unit test-frontend ## Run fast unit tests (backend + frontend, <30s)
|
|
@echo " ✅ All fast tests passed"
|
|
|
|
test-unit: ## Run backend unit tests (SQLite, no Docker)
|
|
@echo " ▶ Backend unit tests (SQLite)..."
|
|
@cd $(BACKEND) && . $(VENV) && timeout $(TIMEOUT_FAST) python -m pytest tests/ --ignore=tests/integration/ -v --tb=short
|
|
|
|
test-frontend: ## Run frontend vitest tests
|
|
@echo " ▶ Frontend vitest tests..."
|
|
@cd $(FRONTEND) && npx vitest run
|
|
|
|
# ── Tier 2: Smart selection ────────────────────────────────
|
|
test-related: ## Run tests related to a changed file (make test-related F=path/to/file.py)
|
|
@if [ -z "$(F)" ]; then echo "Usage: make test-related F=path/to/file.py"; exit 1; fi
|
|
@echo " ▶ Finding related tests for $(F)..."
|
|
@python3 $(ROOT)/scripts/find-related-tests.py --file "$(F)" --run
|
|
|
|
# ── Tier 3: Slow tests (requires Docker) ───────────────────
|
|
test-integration: ## Run backend integration tests (Docker + testcontainers)
|
|
@echo " ▶ Backend integration tests (Docker)..."
|
|
@cd $(BACKEND) && . $(VENV) && timeout $(TIMEOUT_SLOW) python -m pytest tests/integration/ --run-integration -v --tb=short
|
|
|
|
test-e2e: ## Run Playwright E2E tests (requires running app)
|
|
@echo " ▶ Playwright E2E tests..."
|
|
@cd $(FRONTEND) && npx playwright test
|
|
|
|
e2e-loop: ## Build, seed, test, and remove the private dashboard-testing E2E stack
|
|
@$(ROOT)/scripts/e2e-dashboard-testing-loop.sh
|
|
|
|
e2e-run: ## Run chart-8 E2E for an explicit lab target (requires a mode-0600 LAB_PROFILE)
|
|
@test "$(TARGET)" = "lab" || { echo "TARGET=lab is required"; exit 2; }
|
|
@test -n "$(LAB_PROFILE)" -a -f "$(LAB_PROFILE)" || { echo "LAB_PROFILE must name a mode-0600 explicit target/credentials-path profile"; exit 2; }
|
|
@test -x "$(E2E_PYTHON)" || { echo "E2E_PYTHON must name an executable Python with backend dependencies (default: $(BACKEND)/.venv/bin/python)"; exit 2; }
|
|
@case "$(E2E_MODE)" in \
|
|
resume-published) mode=--resume-published ;; \
|
|
fresh|'') mode=--run-lifecycle ;; \
|
|
*) echo "E2E_MODE must be fresh or resume-published"; exit 2 ;; \
|
|
esac; \
|
|
if [ "$(E2E_VALIDATE_ONLY)" = "1" ]; then mode=--validate-only; fi; \
|
|
cd $(BACKEND) && "$(E2E_PYTHON)" ../scripts/e2e-scripts/native_chart8_prepare.py --profile "$(LAB_PROFILE)" "$$mode"
|
|
|
|
lab-reset: ## Prepare/archive or explicitly apply the exact approved chart-8 row reset (PHASE=prepare|apply|verify)
|
|
@test -n "$(SCOPE)" || { echo "SCOPE must name the mode-0600 exact lab reset scope JSON"; exit 2; }
|
|
@test -f "$(LAB_DATABASE_URL_FILE)" -a ! -L "$(LAB_DATABASE_URL_FILE)" || { echo "LAB_DATABASE_URL_FILE must name the private live-lab DB config"; exit 2; }
|
|
@test -x "$(E2E_PYTHON)" || { echo "E2E_PYTHON must name Python with backend dependencies"; exit 2; }
|
|
@case "$(if $(PHASE),$(PHASE),prepare)" in prepare|apply|verify) ;; *) echo "PHASE must be prepare, apply, or verify"; exit 2;; esac
|
|
@if [ "$(PHASE)" = "apply" ]; then \
|
|
test -n "$(ARCHIVE_DIR)" -a -n "$(MANIFEST_SHA256)" -a -n "$(ROWS_SHA256)" || { echo "apply requires ARCHIVE_DIR, MANIFEST_SHA256, and ROWS_SHA256 from the reviewed archive"; exit 2; }; \
|
|
fi
|
|
@cd $(BACKEND) && "$(E2E_PYTHON)" ../scripts/dashboard_testing_lab_reset_scope.py --scope "$(SCOPE)" --database-url-file "$(LAB_DATABASE_URL_FILE)" --phase "$(if $(PHASE),$(PHASE),prepare)" $(if $(filter apply,$(PHASE)),--archive-dir "$(ARCHIVE_DIR)" --manifest-sha256 "$(MANIFEST_SHA256)" --rows-sha256 "$(ROWS_SHA256)")
|
|
|
|
test-e2e-dashboard-testing: e2e-loop ## Compatibility alias for the dashboard-testing E2E loop
|
|
|
|
# ── Full suite ─────────────────────────────────────────────
|
|
test-all: ## Run full suite: fast tests + integration (skip installs)
|
|
@$(MAKE) test
|
|
@echo ""
|
|
@echo " ▶ Integration tests (may take several minutes)..."
|
|
@cd $(BACKEND) && . $(VENV) && timeout $(TIMEOUT_SLOW) python -m pytest tests/integration/ --run-integration -v --tb=short || echo " ⚠ Integration tests skipped (Docker not available or timed out)"
|
|
@echo ""
|
|
@echo " ✅ Full test suite complete"
|
|
|
|
# ── Coverage ───────────────────────────────────────────────
|
|
coverage: coverage-backend coverage-frontend ## Generate coverage (backend + frontend)
|
|
@echo " ✅ Coverage reports ready"
|
|
@echo " Backend: $(BACKEND)/htmlcov/index.html"
|
|
@echo " Frontend: $(FRONTEND)/coverage/index.html"
|
|
|
|
coverage-backend: ## Backend coverage (unit tests + --cov)
|
|
@echo " ▶ Backend coverage..."
|
|
@cd $(BACKEND) && . $(VENV) && timeout $(TIMEOUT_FAST) python -m pytest tests/ --ignore=tests/integration/ \
|
|
--cov=src --cov-report=term-missing --cov-report=html
|
|
|
|
coverage-frontend: ## Frontend coverage (vitest --coverage)
|
|
@echo " ▶ Frontend coverage..."
|
|
@cd $(FRONTEND) && npx vitest run --coverage
|
|
|
|
# ── Linting ────────────────────────────────────────────────
|
|
lint: lint-backend lint-frontend ## Run all linters
|
|
|
|
lint-backend: ## Backend ruff check
|
|
@cd $(BACKEND) && . $(VENV) && python -m ruff check .
|
|
|
|
lint-frontend: ## Frontend eslint
|
|
@cd $(FRONTEND) && npx eslint .
|
|
|
|
# ── Documentation ───────────────────────────────────────────
|
|
DOC_GEN ?= $(ROOT)/../axiom-mcp/target/release/doc-gen
|
|
|
|
docs-nav: ## Fractal module→function navigation graph (doc-gen --nav + --html)
|
|
@if [ ! -x "$(DOC_GEN)" ]; then \
|
|
echo " ▶ Building doc-gen..."; \
|
|
cargo build --release --bin doc-gen --manifest-path "$(ROOT)/../axiom-mcp/Cargo.toml"; \
|
|
fi
|
|
@echo " ▶ Navigation graph (modules → functions)..."
|
|
@LD_LIBRARY_PATH="$(ROOT)/../axiom-mcp/target/release/deps:$$LD_LIBRARY_PATH" \
|
|
"$(DOC_GEN)" --workspace-root "$(ROOT)" --nav "$(ROOT)/docs/api/nav" --html "$(ROOT)/docs/api/html"
|
|
@test -f "$(ROOT)/docs/api/nav/root.map"
|
|
@echo " ✅ Nav maps: docs/api/nav/root.map"
|
|
@echo " ✅ HTML: docs/api/html/index.html"
|
|
|
|
docs-doxygen: ## Generate Doxygen XML and HTML documentation from source comments
|
|
@if ! command -v doxygen >/dev/null 2>&1; then \
|
|
echo " ✗ doxygen not found."; \
|
|
echo " Debian/Ubuntu: sudo apt-get install -y doxygen"; \
|
|
echo " macOS: brew install doxygen"; \
|
|
exit 1; \
|
|
fi
|
|
@echo " ▶ Generating Doxygen XML + HTML..."
|
|
@rm -rf "$(ROOT)/docs/api/build"
|
|
@cd "$(ROOT)" && { cat docs/api/Doxyfile; printf '\nSTRIP_FROM_PATH = %s\n' "$(ROOT)"; } | doxygen -
|
|
@test -f "$(ROOT)/docs/api/build/xml/index.xml"
|
|
@echo " ✅ XML: docs/api/build/xml/index.xml"
|
|
@echo " ✅ HTML: docs/api/build/html/index.html"
|
|
|
|
docs-doxygen-check: ## Validate Doxygen configuration and generated XML
|
|
@if ! command -v doxygen >/dev/null 2>&1; then \
|
|
echo " ✗ doxygen not found."; \
|
|
echo " Debian/Ubuntu: sudo apt-get install -y doxygen"; \
|
|
echo " macOS: brew install doxygen"; \
|
|
exit 1; \
|
|
fi
|
|
@if rg -n '/home/|/Users/' "$(ROOT)/docs/api/Doxyfile" >/dev/null; then \
|
|
echo " ✗ Doxyfile contains an absolute user path"; \
|
|
exit 1; \
|
|
fi
|
|
@test -f "$(ROOT)/docs/api/build/xml/index.xml" || { \
|
|
echo " ✗ Doxygen XML is missing; run make docs-doxygen"; \
|
|
exit 1; \
|
|
}
|
|
@if rg -n 'filename="/home/|<compoundname>/home/|<name>/home/' "$(ROOT)/docs/api/build/xml" >/dev/null; then \
|
|
echo " ✗ Doxygen XML contains an absolute source filename"; \
|
|
exit 1; \
|
|
fi
|
|
@echo " ✅ Doxygen configuration and XML artifact are present"
|