refactor: enhance cenovnici pipeline stability and API server improve… #23
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| name: Test Quality Gate | |
| on: | |
| push: | |
| branches: [ develop, main ] | |
| pull_request: | |
| branches: [ develop, main ] | |
| jobs: | |
| test-quality: | |
| runs-on: ubuntu-latest | |
| strategy: | |
| matrix: | |
| node-version: [20.x] | |
| steps: | |
| - name: Checkout code | |
| uses: actions/checkout@v4 | |
| - name: Setup Node.js ${{ matrix.node-version }} | |
| uses: actions/setup-node@v4 | |
| with: | |
| node-version: ${{ matrix.node-version }} | |
| cache: 'yarn' | |
| - name: Install dependencies | |
| run: yarn install --frozen-lockfile | |
| - name: Run linting | |
| run: yarn lint | |
| - name: Run type checking | |
| run: yarn typecheck | |
| - name: Run unit and integration tests with coverage | |
| run: yarn test:coverage | |
| working-directory: ./ai_working/vizualni-admin | |
| - name: Run accessibility tests | |
| run: yarn test --run --reporter=verbose --testNamePattern="a11y|accessibility" | |
| working-directory: ./ai_working/vizualni-admin | |
| - name: Run visual regression tests | |
| run: yarn test:visual || true # Don't fail the build on visual tests for now | |
| working-directory: ./ai_working/vizualni-admin | |
| env: | |
| CI: true | |
| - name: Upload coverage reports to Codecov | |
| uses: codecov/codecov-action@v4 | |
| with: | |
| file: ./ai_working/vizualni-admin/coverage/lcov.info | |
| flags: unittests | |
| name: codecov-umbrella | |
| fail_ci_if_error: false | |
| - name: Check coverage thresholds | |
| run: | | |
| # Extract coverage percentages from coverage summary | |
| COVERAGE_FILE="./ai_working/vizualni-admin/coverage/coverage-summary.json" | |
| if [ -f "$COVERAGE_FILE" ]; then | |
| LINES_PCT=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$COVERAGE_FILE', 'utf8')).total.lines.pct)") | |
| FUNCTIONS_PCT=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$COVERAGE_FILE', 'utf8')).total.functions.pct)") | |
| BRANCHES_PCT=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$COVERAGE_FILE', 'utf8')).total.branches.pct)") | |
| STATEMENTS_PCT=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$COVERAGE_FILE', 'utf8')).total.statements.pct)") | |
| echo "Coverage Report:" | |
| echo "Lines: ${LINES_PCT}%" | |
| echo "Functions: ${FUNCTIONS_PCT}%" | |
| echo "Branches: ${BRANCHES_PCT}%" | |
| echo "Statements: ${STATEMENTS_PCT}%" | |
| # Check if coverage meets thresholds (80% for lines, functions, statements; 75% for branches) | |
| MIN_LINES=80 | |
| MIN_FUNCTIONS=80 | |
| MIN_BRANCHES=75 | |
| MIN_STATEMENTS=80 | |
| if (( $(echo "$LINES_PCT < $MIN_LINES" | bc -l) )); then | |
| echo "❌ Lines coverage ${LINES_PCT}% is below threshold ${MIN_LINES}%" | |
| exit 1 | |
| fi | |
| if (( $(echo "$FUNCTIONS_PCT < $MIN_FUNCTIONS" | bc -l) )); then | |
| echo "❌ Functions coverage ${FUNCTIONS_PCT}% is below threshold ${MIN_FUNCTIONS}%" | |
| exit 1 | |
| fi | |
| if (( $(echo "$BRANCHES_PCT < $MIN_BRANCHES" | bc -l) )); then | |
| echo "❌ Branches coverage ${BRANCHES_PCT}% is below threshold ${MIN_BRANCHES}%" | |
| exit 1 | |
| fi | |
| if (( $(echo "$STATEMENTS_PCT < $MIN_STATEMENTS" | bc -l) )); then | |
| echo "❌ Statements coverage ${STATEMENTS_PCT}% is below threshold ${MIN_STATEMENTS}%" | |
| exit 1 | |
| fi | |
| echo "✅ All coverage thresholds passed!" | |
| else | |
| echo "❌ Coverage report not found" | |
| exit 1 | |
| fi | |
| - name: Run E2E tests | |
| run: yarn e2e | |
| working-directory: ./ai_working/vizualni-admin | |
| env: | |
| CI: true | |
| - name: Performance Audit | |
| run: | | |
| # Start the application | |
| yarn build:fast & | |
| BUILD_PID=$! | |
| # Wait for build to complete | |
| wait $BUILD_PID | |
| # Start the server in background | |
| yarn start & | |
| SERVER_PID=$! | |
| # Wait for server to be ready | |
| sleep 30 | |
| # Run Lighthouse audit | |
| yarn performance:lighthouse || true | |
| # Kill the server | |
| kill $SERVER_PID 2>/dev/null || true | |
| working-directory: ./ai_working/vizualni-admin | |
| - name: Security Audit | |
| run: yarn audit --level moderate | |
| working-directory: ./ai_working/vizualni-admin | |
| - name: Check for vulnerabilities | |
| run: | | |
| AUDIT_OUTPUT=$(yarn audit --json --level moderate 2>/dev/null) | |
| VULNERABILITIES=$(echo "$AUDIT_OUTPUT" | jq -r '.data.vulnerabilities | length' 2>/dev/null || echo "0") | |
| if [ "$VULNERABILITIES" -gt 0 ]; then | |
| echo "❌ Found $VULNERABILITIES moderate or high severity vulnerabilities" | |
| echo "$AUDIT_OUTPUT" | |
| exit 1 | |
| else | |
| echo "✅ No moderate or high severity vulnerabilities found" | |
| fi | |
| working-directory: ./ai_working/vizualni-admin | |
| - name: Generate test report | |
| run: | | |
| # Create comprehensive test report | |
| REPORT_DIR="./ai_working/vizualni-admin/test-reports" | |
| mkdir -p "$REPORT_DIR" | |
| # Create report summary | |
| cat > "$REPORT_DIR/quality-report.md" << EOF | |
| # Test Quality Gate Report | |
| ## Build Information | |
| - **Commit**: ${{ github.sha }} | |
| - **Branch**: ${{ github.ref_name }} | |
| - **Build Number**: ${{ github.run_number }} | |
| - **Timestamp**: $(date -u +"%Y-%m-%dT%H:%M:%SZ") | |
| ## Test Results | |
| - **Linting**: ✅ Passed | |
| - **Type Checking**: ✅ Passed | |
| - **Unit/Integration Tests**: ✅ Passed | |
| - **Accessibility Tests**: ✅ Passed | |
| - **E2E Tests**: ✅ Passed | |
| - **Security Audit**: ✅ Passed | |
| ## Coverage Metrics | |
| EOF | |
| # Append coverage data if available | |
| COVERAGE_FILE="./ai_working/vizualni-admin/coverage/coverage-summary.json" | |
| if [ -f "$COVERAGE_FILE" ]; then | |
| echo "" >> "$REPORT_DIR/quality-report.md" | |
| echo "| Metric | Percentage | Status |" >> "$REPORT_DIR/quality-report.md" | |
| echo "|--------|------------|--------|" >> "$REPORT_DIR/quality-report.md" | |
| LINES_PCT=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$COVERAGE_FILE', 'utf8')).total.lines.pct)") | |
| FUNCTIONS_PCT=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$COVERAGE_FILE', 'utf8')).total.functions.pct)") | |
| BRANCHES_PCT=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$COVERAGE_FILE', 'utf8')).total.branches.pct)") | |
| STATEMENTS_PCT=$(node -e "console.log(JSON.parse(require('fs').readFileSync('$COVERAGE_FILE', 'utf8')).total.statements.pct)") | |
| echo "| Lines | ${LINES_PCT}% | $(if (( $(echo "$LINES_PCT >= 80" | bc -l) )); then echo "✅"; else echo "❌"; fi) |" >> "$REPORT_DIR/quality-report.md" | |
| echo "| Functions | ${FUNCTIONS_PCT}% | $(if (( $(echo "$FUNCTIONS_PCT >= 80" | bc -l) )); then echo "✅"; else echo "❌"; fi) |" >> "$REPORT_DIR/quality-report.md" | |
| echo "| Branches | ${BRANCHES_PCT}% | $(if (( $(echo "$BRANCHES_PCT >= 75" | bc -l) )); then echo "✅"; else echo "❌"; fi) |" >> "$REPORT_DIR/quality-report.md" | |
| echo "| Statements | ${STATEMENTS_PCT}% | $(if (( $(echo "$STATEMENTS_PCT >= 80" | bc -l) )); then echo "✅"; else echo "❌"; fi) |" >> "$REPORT_DIR/quality-report.md" | |
| fi | |
| echo "✅ Test quality report generated" | |
| - name: Upload test artifacts | |
| uses: actions/upload-artifact@v4 | |
| if: always() | |
| with: | |
| name: test-reports-${{ github.run_number }} | |
| path: | | |
| ./ai_working/vizualni-admin/coverage/ | |
| ./ai_working/vizualni-admin/playwright-report/ | |
| ./ai_working/vizualni-admin/test-reports/ | |
| ./ai_working/vizualni-admin/screenshots/ | |
| retention-days: 30 | |
| - name: Comment PR with results | |
| if: github.event_name == 'pull_request' | |
| uses: actions/github-script@v7 | |
| with: | |
| script: | | |
| const fs = require('fs'); | |
| const path = './ai_working/vizualni-admin/test-reports/quality-report.md'; | |
| if (fs.existsSync(path)) { | |
| const report = fs.readFileSync(path, 'utf8'); | |
| await github.rest.issues.createComment({ | |
| issue_number: context.issue.number, | |
| owner: context.repo.owner, | |
| repo: context.repo.repo, | |
| body: report | |
| }); | |
| } |