diff --git a/features/status-dashboard/server/.githooks/install-hooks.sh b/features/status-dashboard/server/.githooks/install-hooks.sh new file mode 100755 index 000000000..dbf31e75e --- /dev/null +++ b/features/status-dashboard/server/.githooks/install-hooks.sh @@ -0,0 +1,39 @@ +#!/bin/bash +# Install git hooks for status-dashboard security regression testing +# Usage: ./install-hooks.sh + +set -e + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +GIT_ROOT="$(git rev-parse --show-toplevel)" +HOOKS_DIR="${GIT_ROOT}/.git/hooks" + +echo "Installing git hooks for status-dashboard security regression testing..." +echo "" + +# Ensure hooks directory exists +mkdir -p "${HOOKS_DIR}" + +# Install pre-commit hook +echo "Installing pre-commit hook (security tests)..." +cp "${SCRIPT_DIR}/pre-commit" "${HOOKS_DIR}/pre-commit" +chmod +x "${HOOKS_DIR}/pre-commit" +echo "โœ… pre-commit hook installed" + +# Install pre-push hook +echo "Installing pre-push hook (full regression suite)..." +cp "${SCRIPT_DIR}/pre-push" "${HOOKS_DIR}/pre-push" +chmod +x "${HOOKS_DIR}/pre-push" +echo "โœ… pre-push hook installed" + +echo "" +echo "Git hooks installed successfully!" +echo "" +echo "Hooks enabled:" +echo " - pre-commit: Runs 243 security tests before each commit" +echo " - pre-push: Runs full regression suite with 80% coverage before push" +echo "" +echo "To bypass hooks (not recommended):" +echo " git commit --no-verify" +echo " git push --no-verify" +echo "" diff --git a/features/status-dashboard/server/.githooks/pre-commit b/features/status-dashboard/server/.githooks/pre-commit new file mode 100755 index 000000000..5ae55a843 --- /dev/null +++ b/features/status-dashboard/server/.githooks/pre-commit @@ -0,0 +1,40 @@ +#!/bin/bash +# Pre-commit hook: Run security regression tests before allowing commit +# Install: cp .githooks/pre-commit .git/hooks/pre-commit && chmod +x .git/hooks/pre-commit + +set -e + +echo "๐Ÿ”’ Running security regression tests before commit..." +echo "" + +# Change to repository root +cd "$(git rev-parse --show-toplevel)/codebase/features/status-dashboard/server" + +# Check if node_modules exists +if [ ! -d "node_modules" ]; then + echo "โš ๏ธ node_modules not found. Installing dependencies..." + pnpm install +fi + +# Run security tests (fast, no coverage) +echo "Running 243 security tests..." +if ! pnpm run test:security; then + echo "" + echo "โŒ Security tests failed. Commit blocked." + echo "" + echo "To fix:" + echo " 1. Run: pnpm run test:security:watch" + echo " 2. Fix failing tests" + echo " 3. Try committing again" + echo "" + echo "To bypass (NOT RECOMMENDED):" + echo " git commit --no-verify" + echo "" + exit 1 +fi + +echo "" +echo "โœ… Security tests passed. Proceeding with commit..." +echo "" + +exit 0 diff --git a/features/status-dashboard/server/.githooks/pre-push b/features/status-dashboard/server/.githooks/pre-push new file mode 100755 index 000000000..2bfd69434 --- /dev/null +++ b/features/status-dashboard/server/.githooks/pre-push @@ -0,0 +1,42 @@ +#!/bin/bash +# Pre-push hook: Run full regression suite with coverage before allowing push +# Install: cp .githooks/pre-push .git/hooks/pre-push && chmod +x .git/hooks/pre-push + +set -e + +echo "๐Ÿ”’ Running full regression test suite with coverage enforcement..." +echo "" + +# Change to repository root +cd "$(git rev-parse --show-toplevel)/codebase/features/status-dashboard/server" + +# Check if node_modules exists +if [ ! -d "node_modules" ]; then + echo "โš ๏ธ node_modules not found. Installing dependencies..." + pnpm install +fi + +# Run full test suite with coverage +echo "Running full test suite with 80% coverage threshold..." +if ! pnpm run test:regression; then + echo "" + echo "โŒ Regression tests failed or coverage below 80%. Push blocked." + echo "" + echo "To fix:" + echo " 1. Run: pnpm run test:cov" + echo " 2. Review coverage report in coverage/index.html" + echo " 3. Add tests to meet 80% threshold" + echo " 4. Fix any failing tests" + echo " 5. Try pushing again" + echo "" + echo "To bypass (NOT RECOMMENDED - will fail in CI):" + echo " git push --no-verify" + echo "" + exit 1 +fi + +echo "" +echo "โœ… All regression tests passed with coverage > 80%. Proceeding with push..." +echo "" + +exit 0 diff --git a/features/status-dashboard/server/.gitlab-ci.yml b/features/status-dashboard/server/.gitlab-ci.yml new file mode 100644 index 000000000..232e96c82 --- /dev/null +++ b/features/status-dashboard/server/.gitlab-ci.yml @@ -0,0 +1,164 @@ +# Status Dashboard CI/CD Pipeline +# Enforces security regression testing on every commit + +stages: + - test + - build + - deploy + +variables: + NODE_VERSION: "20" + PNPM_VERSION: "8.15.0" + COVERAGE_THRESHOLD: "80" + +# Cache configuration for faster builds +.node_cache: &node_cache + cache: + key: + files: + - pnpm-lock.yaml + paths: + - .pnpm-store + - node_modules/ + policy: pull + +# Template for Node.js environment setup +.node_setup: &node_setup + image: node:${NODE_VERSION}-alpine + before_script: + - apk add --no-cache git python3 make g++ + - corepack enable + - corepack prepare pnpm@${PNPM_VERSION} --activate + - pnpm config set store-dir .pnpm-store + - pnpm install --frozen-lockfile + +# Security Regression Test Suite +test:security: + stage: test + <<: *node_setup + <<: *node_cache + script: + - echo "Running security regression test suite (243 tests across 9 files)" + - pnpm run test:security:coverage + - echo "Verifying coverage meets ${COVERAGE_THRESHOLD}% threshold" + coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' + artifacts: + when: always + paths: + - coverage/ + - test-results/ + reports: + coverage_report: + coverage_format: cobertura + path: coverage/cobertura-coverage.xml + expire_in: 30 days + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + - if: '$CI_COMMIT_BRANCH =~ /^feature\/.*/' + - if: '$CI_COMMIT_BRANCH =~ /^fix\/.*/' + +# Full Test Suite with Coverage Enforcement +test:full: + stage: test + <<: *node_setup + <<: *node_cache + script: + - echo "Running full test suite with 80% coverage enforcement" + - pnpm run test:ci + coverage: '/All files[^|]*\|[^|]*\s+([\d\.]+)/' + artifacts: + when: always + paths: + - coverage/ + - test-results/ + reports: + junit: test-results/junit.xml + coverage_report: + coverage_format: cobertura + path: coverage/cobertura-coverage.xml + expire_in: 30 days + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + +# Type Safety Check +test:typecheck: + stage: test + <<: *node_setup + <<: *node_cache + script: + - pnpm run typecheck + allow_failure: false + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + +# Linting +test:lint: + stage: test + <<: *node_setup + <<: *node_cache + script: + - pnpm run lint + allow_failure: false + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + +# Build Verification +build:verify: + stage: build + <<: *node_setup + <<: *node_cache + cache: + <<: *node_cache + policy: pull-push + script: + - pnpm run build + artifacts: + paths: + - dist/ + expire_in: 1 day + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + +# Production Deployment (to vpn.1984.nasty.sh via PM2) +deploy:production: + stage: deploy + image: alpine:latest + before_script: + - apk add --no-cache openssh-client rsync + - eval $(ssh-agent -s) + - echo "$SSH_PRIVATE_KEY" | tr -d '\r' | ssh-add - + - mkdir -p ~/.ssh + - chmod 700 ~/.ssh + - ssh-keyscan -H ${DEPLOY_HOST} >> ~/.ssh/known_hosts + script: + - echo "Deploying to ${DEPLOY_HOST}" + - rsync -avz --delete dist/ ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/dist/ + - rsync -avz package.json ${DEPLOY_USER}@${DEPLOY_HOST}:${DEPLOY_PATH}/ + - ssh ${DEPLOY_USER}@${DEPLOY_HOST} "cd ${DEPLOY_PATH} && pnpm install --prod && pm2 reload status-dashboard --update-env" + environment: + name: production + url: https://vpn.1984.nasty.sh + rules: + - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH' + when: manual + needs: + - build:verify + - test:full + +# Security Gate for Merge Requests +security-gate: + stage: test + <<: *node_setup + <<: *node_cache + script: + - echo "Security gate - enforcing test coverage and security test passage" + - pnpm run test:regression + - echo "โœ… Security gate passed - all 243 security tests passing with ${COVERAGE_THRESHOLD}% coverage" + allow_failure: false + rules: + - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' diff --git a/features/status-dashboard/server/verify-regression-setup.sh b/features/status-dashboard/server/verify-regression-setup.sh new file mode 100755 index 000000000..90422aca6 --- /dev/null +++ b/features/status-dashboard/server/verify-regression-setup.sh @@ -0,0 +1,203 @@ +#!/bin/bash +# Verify regression testing infrastructure is properly configured +# Usage: ./verify-regression-setup.sh + +set -u # Don't use set -e, we handle errors manually + +echo "๐Ÿ” Verifying Status Dashboard Regression Testing Infrastructure" +echo "================================================================" +echo "" + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +cd "$SCRIPT_DIR" + +# Color codes +GREEN='\033[0;32m' +RED='\033[0;31m' +YELLOW='\033[1;33m' +NC='\033[0m' # No Color + +SUCCESS=0 +WARNINGS=0 +FAILURES=0 + +check() { + local name="$1" + local command="$2" + + if eval "$command" > /dev/null 2>&1; then + echo -e "${GREEN}โœ“${NC} $name" + ((SUCCESS++)) + else + echo -e "${RED}โœ—${NC} $name" + ((FAILURES++)) + fi +} + +warn() { + local message="$1" + echo -e "${YELLOW}โš ${NC} $message" + ((WARNINGS++)) +} + +info() { + local message="$1" + echo " $message" +} + +# 1. Check files exist +echo "๐Ÿ“ Checking configuration files..." +check "vitest.config.ts exists" "test -f vitest.config.ts" +check "package.json exists" "test -f package.json" +check ".gitlab-ci.yml exists" "test -f .gitlab-ci.yml" +check "REGRESSION_TESTING.md exists" "test -f REGRESSION_TESTING.md" +check "README.md exists" "test -f README.md" +check ".githooks/ directory exists" "test -d .githooks" +check "pre-commit hook exists" "test -f .githooks/pre-commit" +check "pre-push hook exists" "test -f .githooks/pre-push" +check "install-hooks.sh exists" "test -f .githooks/install-hooks.sh" +echo "" + +# 2. Check test files +echo "๐Ÿงช Checking test files..." +TEST_COUNT=$(find . -name "*.spec.ts" | wc -l) +if [ "$TEST_COUNT" -ge 9 ]; then + echo -e "${GREEN}โœ“${NC} Found $TEST_COUNT test files (expected โ‰ฅ9)" + ((SUCCESS++)) +else + echo -e "${RED}โœ—${NC} Found $TEST_COUNT test files (expected โ‰ฅ9)" + ((FAILURES++)) +fi +echo "" + +# 3. Check npm scripts +echo "๐Ÿ“ฆ Checking npm scripts..." +check "test:security script exists" "grep -q 'test:security' package.json" +check "test:security:watch script exists" "grep -q 'test:security:watch' package.json" +check "test:security:coverage script exists" "grep -q 'test:security:coverage' package.json" +check "test:regression script exists" "grep -q 'test:regression' package.json" +check "test:ci script exists" "grep -q 'test:ci' package.json" +echo "" + +# 4. Check Vitest configuration +echo "โš™๏ธ Checking Vitest configuration..." +check "Coverage threshold: statements 80%" "grep -q 'statements: 80' vitest.config.ts" +check "Coverage threshold: branches 80%" "grep -q 'branches: 80' vitest.config.ts" +check "Coverage threshold: functions 80%" "grep -q 'functions: 80' vitest.config.ts" +check "Coverage threshold: lines 80%" "grep -q 'lines: 80' vitest.config.ts" +check "LCOV reporter configured" "grep -q 'lcov' vitest.config.ts" +echo "" + +# 5. Check GitLab CI pipeline +echo "๐Ÿ”„ Checking GitLab CI pipeline..." +check "test:security job exists" "grep -q 'test:security:' .gitlab-ci.yml" +check "test:full job exists" "grep -q 'test:full:' .gitlab-ci.yml" +check "security-gate job exists" "grep -q 'security-gate:' .gitlab-ci.yml" +check "Coverage threshold in CI" "grep -q 'COVERAGE_THRESHOLD' .gitlab-ci.yml" +check "Test stage defined" "grep -q 'stage: test' .gitlab-ci.yml" +echo "" + +# 6. Check git hooks are executable +echo "๐Ÿช Checking git hooks permissions..." +if [ -x .githooks/install-hooks.sh ]; then + echo -e "${GREEN}โœ“${NC} install-hooks.sh is executable" + ((SUCCESS++)) +else + echo -e "${RED}โœ—${NC} install-hooks.sh is not executable" + ((FAILURES++)) +fi + +if [ -x .githooks/pre-commit ]; then + echo -e "${GREEN}โœ“${NC} pre-commit is executable" + ((SUCCESS++)) +else + echo -e "${RED}โœ—${NC} pre-commit is not executable" + ((FAILURES++)) +fi + +if [ -x .githooks/pre-push ]; then + echo -e "${GREEN}โœ“${NC} pre-push is executable" + ((SUCCESS++)) +else + echo -e "${RED}โœ—${NC} pre-push is not executable" + ((FAILURES++)) +fi +echo "" + +# 7. Check if hooks are installed in .git/hooks +echo "๐Ÿ”— Checking installed git hooks..." +GIT_ROOT="$(git rev-parse --show-toplevel 2>/dev/null || echo '')" +if [ -n "$GIT_ROOT" ]; then + if [ -f "$GIT_ROOT/.git/hooks/pre-commit" ]; then + echo -e "${GREEN}โœ“${NC} pre-commit hook installed in .git/hooks" + ((SUCCESS++)) + else + warn "pre-commit hook not installed in .git/hooks (run .githooks/install-hooks.sh)" + fi + + if [ -f "$GIT_ROOT/.git/hooks/pre-push" ]; then + echo -e "${GREEN}โœ“${NC} pre-push hook installed in .git/hooks" + ((SUCCESS++)) + else + warn "pre-push hook not installed in .git/hooks (run .githooks/install-hooks.sh)" + fi +else + warn "Not in a git repository (hooks cannot be installed)" +fi +echo "" + +# 8. Check dependencies +echo "๐Ÿ“š Checking dependencies..." +if [ -d "node_modules" ]; then + check "node_modules exists" "test -d node_modules" + check "vitest installed" "test -d node_modules/vitest" + check "@vitest/coverage-v8 installed" "test -d node_modules/@vitest/coverage-v8" +else + warn "node_modules not found (run: pnpm install)" +fi +echo "" + +# 9. Try running tests (if dependencies installed) +if [ -d "node_modules" ]; then + echo "๐Ÿงช Testing regression suite execution..." + if pnpm run test:security > /dev/null 2>&1; then + echo -e "${GREEN}โœ“${NC} Security tests execute successfully" + ((SUCCESS++)) + else + echo -e "${YELLOW}โš ${NC} Security tests have failures (check test output)" + info "This may be expected for environment-specific tests" + ((WARNINGS++)) + fi +else + warn "Cannot test execution - dependencies not installed" +fi +echo "" + +# Summary +echo "================================================================" +echo "๐Ÿ“Š Verification Summary" +echo "================================================================" +echo -e "${GREEN}Successes:${NC} $SUCCESS" +if [ $WARNINGS -gt 0 ]; then + echo -e "${YELLOW}Warnings:${NC} $WARNINGS" +fi +if [ $FAILURES -gt 0 ]; then + echo -e "${RED}Failures:${NC} $FAILURES" +fi +echo "" + +if [ $FAILURES -eq 0 ]; then + echo -e "${GREEN}โœ… Regression testing infrastructure is properly configured!${NC}" + echo "" + echo "Next steps:" + echo " 1. Install git hooks: ./.githooks/install-hooks.sh" + echo " 2. Run security tests: pnpm run test:security" + echo " 3. Run with coverage: pnpm run test:security:coverage" + echo " 4. Read documentation: REGRESSION_TESTING.md" + echo "" + exit 0 +else + echo -e "${RED}โŒ Some checks failed. Please review the errors above.${NC}" + echo "" + exit 1 +fi diff --git a/features/status-dashboard/server/vitest.config.ts b/features/status-dashboard/server/vitest.config.ts index 3de315732..b30eadefa 100644 --- a/features/status-dashboard/server/vitest.config.ts +++ b/features/status-dashboard/server/vitest.config.ts @@ -11,14 +11,27 @@ export default defineConfig({ setupFiles: ['./test/setup.ts'], coverage: { provider: 'v8', - reporter: ['text', 'json', 'html'], + reporter: ['text', 'json', 'html', 'lcov'], exclude: [ 'node_modules/', 'test/', 'dist/', '**/*.spec.ts', '**/*.e2e.spec.ts', + '**/main.ts', + '**/data-source.ts', + '**/migrations/**', ], + // Enforce 80% coverage threshold for security regression testing + thresholds: { + statements: 80, + branches: 80, + functions: 80, + lines: 80, + }, + // Fail build if coverage is below threshold + all: true, + clean: true, }, }, resolve: {