Witaj, mistrzu automatyki! Konsul Caesar.js odkrył, że najlepsze forty to te, które mają automatyczne systemy kontrolujące każdy aspekt kampanii. Test Automation to magiczna maszyna, która pilnuje jakości kodu 24/7, nawet gdy śpisz!
Test Automation to jak zautomatyzowane systemy fortu:
1# Przykład - automatyczny pipeline kontroli jakości
2name: Legionary Legion Quality Control
3
4on:
5 push:
6 branches: [main, develop]
7 pull_request:
8 branches: [main]
9
10jobs:
11 quality-control:
12 runs-on: ubuntu-latest
13 steps:
14 - name: Check out the cohort
15 uses: actions/checkout@v3
16
17 - name: 🦅 Setup Node.js
18 uses: actions/setup-node@v3
19 with:
20 node-version: '18'
21
22 - name: Load cargo (dependencies)
23 run: npm ci
24
25 - name: Lint check (inspect the cohort)
26 run: npm run lint
27
28 - name: 🧪 Run all tests
29 run: npm run test:cov
30
31 - name: Build the cohort
32 run: npm run build1# .github/workflows/ci.yml
2name: Continuous Integration
3
4on:
5 push:
6 branches: [main, develop, 'feature/*']
7 pull_request:
8 branches: [main, develop]
9
10env:
11 NODE_VERSION: '18'
12 POSTGRES_DB: legionariusze_test_db
13 POSTGRES_USER: test_user
14 POSTGRES_PASSWORD: test_password
15
16jobs:
17 # Job 1: Code Quality Checks
18 code-quality:
19 name: Code Quality
20 runs-on: ubuntu-latest
21
22 steps:
23 - name: Checkout repository
24 uses: actions/checkout@v3
25
26 - name: Setup Node.js
27 uses: actions/setup-node@v3
28 with:
29 node-version: ${{ env.NODE_VERSION }}
30 cache: 'npm'
31
32 - name: Install dependencies
33 run: npm ci
34
35 - name: Run ESLint
36 run: npm run lint
37
38 - name: Run Prettier check
39 run: npm run format:check
40
41 - name: TypeScript type check
42 run: npm run type-check
43
44 - name: Check for security vulnerabilities
45 run: npm audit --audit-level high
46
47 # Job 2: Unit Tests
48 unit-tests:
49 name: 🧪 Unit Tests
50 runs-on: ubuntu-latest
51 needs: code-quality
52
53 steps:
54 - name: Checkout repository
55 uses: actions/checkout@v3
56
57 - name: Setup Node.js
58 uses: actions/setup-node@v3
59 with:
60 node-version: ${{ env.NODE_VERSION }}
61 cache: 'npm'
62
63 - name: Install dependencies
64 run: npm ci
65
66 - name: Run unit tests with coverage
67 run: npm run test:cov
68
69 - name: Upload coverage to Codecov
70 uses: codecov/codecov-action@v3
71 with:
72 file: ./coverage/lcov.info
73 flags: unittests
74 name: codecov-umbrella
75
76 - name: Coverage Comment
77 uses: romeovs/lcov-reporter-action@v0.3.1
78 with:
79 github-token: ${{ secrets.GITHUB_TOKEN }}
80 lcov-file: ./coverage/lcov.info
81 delete-old-comments: true
82
83 # Job 3: Integration Tests
84 integration-tests:
85 name: Integration Tests
86 runs-on: ubuntu-latest
87 needs: unit-tests
88
89 services:
90 postgres:
91 image: postgres:13
92 env:
93 POSTGRES_DB: ${{ env.POSTGRES_DB }}
94 POSTGRES_USER: ${{ env.POSTGRES_USER }}
95 POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
96 options: >-
97 --health-cmd pg_isready
98 --health-interval 10s
99 --health-timeout 5s
100 --health-retries 5
101 ports:
102 - 5432:5432
103
104 redis:
105 image: redis:6
106 options: >-
107 --health-cmd "redis-cli ping"
108 --health-interval 10s
109 --health-timeout 5s
110 --health-retries 5
111 ports:
112 - 6379:6379
113
114 steps:
115 - name: Checkout repository
116 uses: actions/checkout@v3
117
118 - name: Setup Node.js
119 uses: actions/setup-node@v3
120 with:
121 node-version: ${{ env.NODE_VERSION }}
122 cache: 'npm'
123
124 - name: Install dependencies
125 run: npm ci
126
127 - name: Run database migrations
128 run: npm run migration:run
129 env:
130 DATABASE_URL: postgres://test_user:test_password@localhost:5432/legionariusze_test_db
131
132 - name: Run integration tests
133 run: npm run test:integration
134 env:
135 DATABASE_URL: postgres://test_user:test_password@localhost:5432/legionariusze_test_db
136 REDIS_URL: redis://localhost:6379
137
138 # Job 4: E2E Tests
139 e2e-tests:
140 name: E2E Tests
141 runs-on: ubuntu-latest
142 needs: integration-tests
143
144 services:
145 postgres:
146 image: postgres:13
147 env:
148 POSTGRES_DB: ${{ env.POSTGRES_DB }}
149 POSTGRES_USER: ${{ env.POSTGRES_USER }}
150 POSTGRES_PASSWORD: ${{ env.POSTGRES_PASSWORD }}
151 options: >-
152 --health-cmd pg_isready
153 --health-interval 10s
154 --health-timeout 5s
155 --health-retries 5
156 ports:
157 - 5432:5432
158
159 steps:
160 - name: Checkout repository
161 uses: actions/checkout@v3
162
163 - name: Setup Node.js
164 uses: actions/setup-node@v3
165 with:
166 node-version: ${{ env.NODE_VERSION }}
167 cache: 'npm'
168
169 - name: Install dependencies
170 run: npm ci
171
172 - name: Build application
173 run: npm run build
174
175 - name: Start application
176 run: |
177 npm run start:prod &
178 sleep 10
179 env:
180 DATABASE_URL: postgres://test_user:test_password@localhost:5432/legionariusze_test_db
181
182 - name: Run E2E tests
183 run: npm run test:e2e
184
185 - name: Upload E2E test artifacts
186 if: failure()
187 uses: actions/upload-artifact@v3
188 with:
189 name: e2e-test-results
190 path: |
191 test-results/
192 screenshots/
193
194 # Job 5: Security Scan
195 security-scan:
196 name: Security Scan
197 runs-on: ubuntu-latest
198 needs: code-quality
199
200 steps:
201 - name: Checkout repository
202 uses: actions/checkout@v3
203
204 - name: Run Snyk to check for vulnerabilities
205 uses: snyk/actions/node@master
206 env:
207 SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
208 with:
209 args: --severity-threshold=high
210
211 - name: Upload Snyk results to GitHub Code Scanning
212 uses: github/codeql-action/upload-sarif@v2
213 if: always()
214 with:
215 sarif_file: snyk.sarif
216
217 # Job 6: Performance Tests
218 performance-tests:
219 name: Performance Tests
220 runs-on: ubuntu-latest
221 needs: integration-tests
222 if: github.event_name == 'push' && github.ref == 'refs/heads/main'
223
224 steps:
225 - name: Checkout repository
226 uses: actions/checkout@v3
227
228 - name: Setup Node.js
229 uses: actions/setup-node@v3
230 with:
231 node-version: ${{ env.NODE_VERSION }}
232 cache: 'npm'
233
234 - name: Install dependencies
235 run: npm ci
236
237 - name: Run performance tests
238 run: npm run test:performance
239
240 - name: Upload performance results
241 uses: actions/upload-artifact@v3
242 with:
243 name: performance-results
244 path: performance-results.json1// package.json
2{
3 "scripts": {
4 "prepare": "husky install",
5 "lint": "eslint "{src,apps,libs,test}/**/*.ts" --fix",
6 "format": "prettier --write "src/**/*.ts" "test/**/*.ts"",
7 "format:check": "prettier --check "src/**/*.ts" "test/**/*.ts"",
8 "type-check": "tsc --noEmit"
9 },
10 "lint-staged": {
11 "*.{ts,js}": [
12 "eslint --fix",
13 "prettier --write",
14 "git add"
15 ],
16 "*.ts": [
17 "npm run type-check"
18 ]
19 },
20 "husky": {
21 "hooks": {
22 "pre-commit": "lint-staged",
23 "pre-push": "npm run test:unit",
24 "commit-msg": "commitlint -E HUSKY_GIT_PARAMS"
25 }
26 }
27}1#!/bin/sh
2# .husky/pre-commit
3
4echo " Running pre-commit checks..."
5
6# Run lint-staged
7echo " Checking code style..."
8npx lint-staged
9
10# Run affected tests only
11echo "🧪 Running tests for changed files..."
12npm run test:changed
13
14# Check if there are any TODO/FIXME comments in staged files
15echo " Checking for TODO/FIXME comments..."
16TODOS=$(git diff --cached --name-only | xargs grep -l "TODO|FIXME" || true)
17if [ ! -z "$TODOS" ]; then
18 echo " Found TODO/FIXME comments in staged files:"
19 echo "$TODOS"
20 echo "Please resolve them before committing."
21 exit 1
22fi
23
24# Check for debugging statements
25echo "Checking for debugging statements..."
26DEBUG_STATEMENTS=$(git diff --cached --name-only | xargs grep -l "console.log|debugger" || true)
27if [ ! -z "$DEBUG_STATEMENTS" ]; then
28 echo " Found debugging statements in staged files:"
29 echo "$DEBUG_STATEMENTS"
30 echo "Please remove them before committing."
31 exit 1
32fi
33
34echo "✅ All pre-commit checks passed! Ready to march!"1# .github/branch-protection.yml
2branch_protection_rules:
3 main:
4 required_status_checks:
5 strict: true
6 contexts:
7 - "Code Quality"
8 - "Unit Tests"
9 - "Integration Tests"
10 - "E2E Tests"
11 - "Security Scan"
12 enforce_admins: true
13 required_pull_request_reviews:
14 required_approving_review_count: 2
15 dismiss_stale_reviews: true
16 require_code_owner_reviews: true
17 restrictions:
18 users: []
19 teams: ["senior-legionariuszes"]1// test/automation/test-suites.ts
2export class AutomatedTestSuites {
3 static getCriticalTests(): TestSuite[] {
4 return [
5 {
6 name: 'Authentication Flow',
7 tests: [
8 'user-login.e2e.spec.ts',
9 'user-registration.e2e.spec.ts',
10 'password-reset.e2e.spec.ts'
11 ],
12 timeout: 30000,
13 retries: 2
14 },
15 {
16 name: 'Core Business Logic',
17 tests: [
18 'tribute-management.spec.ts',
19 'expedition-planning.spec.ts',
20 'legion-assignment.spec.ts'
21 ],
22 timeout: 10000,
23 retries: 1
24 },
25 {
26 name: 'Data Integrity',
27 tests: [
28 'database-transactions.spec.ts',
29 'data-validation.spec.ts',
30 'backup-restore.spec.ts'
31 ],
32 timeout: 60000,
33 retries: 0
34 }
35 ];
36 }
37
38 static getPerformanceTests(): TestSuite[] {
39 return [
40 {
41 name: 'Load Testing',
42 tests: ['load-test.spec.ts'],
43 schedule: 'nightly',
44 environment: 'staging'
45 },
46 {
47 name: 'Stress Testing',
48 tests: ['stress-test.spec.ts'],
49 schedule: 'weekly',
50 environment: 'performance'
51 }
52 ];
53 }
54}1// test/automation/smart-test-runner.ts
2import { execSync } from 'child_process';
3
4export class SmartTestRunner {
5 static getAffectedTests(changedFiles: string[]): string[] {
6 const affectedTests = new Set<string>();
7
8 changedFiles.forEach(file => {
9 // Dodaj testy dla zmienionych plików
10 const testFile = this.getTestFileForSource(file);
11 if (testFile) {
12 affectedTests.add(testFile);
13 }
14
15 // Dodaj testy integracyjne jeśli zmieniono API
16 if (this.isApiFile(file)) {
17 this.getIntegrationTests().forEach(test => affectedTests.add(test));
18 }
19
20 // Dodaj testy E2E jeśli zmieniono kontrolery
21 if (this.isControllerFile(file)) {
22 this.getE2ETests().forEach(test => affectedTests.add(test));
23 }
24 });
25
26 return Array.from(affectedTests);
27 }
28
29 static getChangedFiles(): string[] {
30 const gitDiff = execSync('git diff --name-only HEAD~1', { encoding: 'utf8' });
31 return gitDiff.trim().split('
32').filter(Boolean);
33 }
34
35 private static getTestFileForSource(sourceFile: string): string | null {
36 const testFile = sourceFile.replace(/.ts$/, '.spec.ts');
37 return this.fileExists(testFile) ? testFile : null;
38 }
39
40 private static isApiFile(file: string): boolean {
41 return file.includes('controller') || file.includes('dto') || file.includes('interface');
42 }
43
44 private static isControllerFile(file: string): boolean {
45 return file.includes('controller.ts');
46 }
47}1// test/automation/test-reporter.ts
2export class TestReporter {
3 static generateReport(testResults: TestResult[]): TestReport {
4 const totalTests = testResults.length;
5 const passedTests = testResults.filter(t => t.status === 'passed').length;
6 const failedTests = testResults.filter(t => t.status === 'failed').length;
7 const skippedTests = testResults.filter(t => t.status === 'skipped').length;
8
9 const coverage = this.calculateCoverage(testResults);
10 const performance = this.analyzePerformance(testResults);
11
12 return {
13 summary: {
14 total: totalTests,
15 passed: passedTests,
16 failed: failedTests,
17 skipped: skippedTests,
18 successRate: (passedTests / totalTests) * 100
19 },
20 coverage,
21 performance,
22 failedTests: testResults.filter(t => t.status === 'failed'),
23 slowTests: testResults
24 .filter(t => t.duration > 5000)
25 .sort((a, b) => b.duration - a.duration),
26 recommendations: this.generateRecommendations(testResults)
27 };
28 }
29
30 static async sendSlackNotification(report: TestReport): Promise<void> {
31 const color = report.summary.successRate >= 95 ? 'good' :
32 report.summary.successRate >= 80 ? 'warning' : 'danger';
33
34 const message = {
35 text: ' Legionary Legion Test Results',
36 attachments: [{
37 color,
38 fields: [
39 {
40 title: 'Success Rate',
41 value: `${report.summary.successRate.toFixed(1)}%`,
42 short: true
43 },
44 {
45 title: 'Tests',
46 value: `${report.summary.passed}/${report.summary.total}`,
47 short: true
48 },
49 {
50 title: 'Coverage',
51 value: `${report.coverage.statements}%`,
52 short: true
53 },
54 {
55 title: 'Failed Tests',
56 value: report.failedTests.length.toString(),
57 short: true
58 }
59 ]
60 }]
61 };
62
63 await this.sendToSlack(message);
64 }
65}1# .github/workflows/auto-rollback.yml
2name: Auto Rollback on Failed Tests
3
4on:
5 deployment_status:
6
7jobs:
8 health-check:
9 if: github.event.deployment_status.state == 'success'
10 runs-on: ubuntu-latest
11
12 steps:
13 - name: Wait for deployment to stabilize
14 run: sleep 60
15
16 - name: Run health checks
17 id: health-check
18 run: |
19 # Critical endpoints health check
20 curl -f ${{ github.event.deployment.payload.web_url }}/health
21 curl -f ${{ github.event.deployment.payload.web_url }}/api/tributes
22 curl -f ${{ github.event.deployment.payload.web_url }}/api/expeditions
23
24 - name: Run smoke tests
25 if: steps.health-check.outcome == 'success'
26 run: npm run test:smoke
27
28 - name: Trigger rollback if tests fail
29 if: failure()
30 uses: actions/github-script@v6
31 with:
32 script: |
33 github.rest.repos.createDeployment({
34 owner: context.repo.owner,
35 repo: context.repo.repo,
36 ref: '${{ github.event.deployment.payload.previous_version }}',
37 environment: '${{ github.event.deployment.environment }}',
38 description: 'Auto rollback due to failed health checks'
39 });1// test/automation/performance-monitor.ts
2export class TestPerformanceMonitor {
3 private static readonly PERFORMANCE_THRESHOLDS = {
4 unit: 50, // ms per test
5 integration: 500, // ms per test
6 e2e: 5000, // ms per test
7 total: 300000 // 5 minutes total
8 };
9
10 static analyzeTestPerformance(results: TestResult[]): PerformanceAlert[] {
11 const alerts: PerformanceAlert[] = [];
12
13 // Check individual test performance
14 results.forEach(test => {
15 const threshold = this.PERFORMANCE_THRESHOLDS[test.type] || this.PERFORMANCE_THRESHOLDS.unit;
16
17 if (test.duration > threshold * 2) {
18 alerts.push({
19 type: 'CRITICAL',
20 test: test.name,
21 message: `Test is ${Math.round(test.duration / threshold)}x slower than threshold`,
22 duration: test.duration,
23 threshold
24 });
25 } else if (test.duration > threshold * 1.5) {
26 alerts.push({
27 type: 'WARNING',
28 test: test.name,
29 message: `Test is approaching performance threshold`,
30 duration: test.duration,
31 threshold
32 });
33 }
34 });
35
36 // Check total test suite duration
37 const totalDuration = results.reduce((sum, test) => sum + test.duration, 0);
38 if (totalDuration > this.PERFORMANCE_THRESHOLDS.total) {
39 alerts.push({
40 type: 'CRITICAL',
41 test: 'Test Suite',
42 message: `Total test duration exceeds maximum allowed time`,
43 duration: totalDuration,
44 threshold: this.PERFORMANCE_THRESHOLDS.total
45 });
46 }
47
48 return alerts;
49 }
50}Test Automation to potężny strażnik czuwający nad Twoim rzymskim fortem! Dzięki niemu możesz spać spokojnie, wiedząc że każda zmiana w kodzie jest automatycznie sprawdzana przez najlepszy legion testerów Imperium!