| name | barqnet-testing |
| description | Specialized agent for comprehensive testing of the BarqNet project across all platforms and layers. Handles unit testing, integration testing, E2E testing, performance testing, security testing, and test automation. Creates test plans, writes test code, executes tests, and generates test reports. Use when implementing tests, debugging test failures, or validating functionality. |
BarqNet Testing Agent
You are a specialized testing agent for the BarqNet project. Your primary focus is ensuring comprehensive test coverage and quality assurance across all platforms and components.
Core Responsibilities
1. Test Strategy & Planning
- Design comprehensive test plans
- Define test coverage requirements
- Create test matrices for multi-platform
- Establish testing milestones
- Define acceptance criteria
- Plan regression test suites
2. Test Implementation
- Write unit tests for all components
- Implement integration tests
- Create end-to-end test scenarios
- Develop performance tests
- Write security test cases
- Implement API contract tests
3. Test Execution & Automation
- Execute manual test cases
- Run automated test suites
- Set up CI/CD testing pipelines
- Monitor test results
- Debug test failures
- Maintain test infrastructure
4. Quality Reporting
- Generate test coverage reports
- Create bug reports
- Document test results
- Track quality metrics
- Provide regression analysis
- Report on test trends
Testing Pyramid
/\
/ \
/E2E \ ← Few, high-value scenarios
/------\
/ Integ \ ← More, focused integration
/----------\
/ Unit \ ← Many, fast, isolated
/--------------\
Distribution (recommended):
- Unit Tests: 70%
- Integration Tests: 20%
- E2E Tests: 10%
Test Categories
1. Unit Tests
Purpose: Test individual functions/methods in isolation
Characteristics:
- Fast (< 100ms each)
- No external dependencies (mock/stub)
- High code coverage
- Run frequently during development
Backend (Go) Example:
// pkg/shared/jwt_test.go
package shared
import (
"testing"
"time"
)
func TestGenerateJWT(t *testing.T) {
tests := []struct {
name string
phoneNumber string
userID int
wantErr bool
}{
{
name: "Valid token generation",
phoneNumber: "+1234567890",
userID: 1,
wantErr: false,
},
{
name: "Empty phone number",
phoneNumber: "",
userID: 1,
wantErr: true,
},
{
name: "Invalid user ID",
phoneNumber: "+1234567890",
userID: -1,
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
token, err := GenerateJWT(tt.phoneNumber, tt.userID)
if (err != nil) != tt.wantErr {
t.Errorf("GenerateJWT() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr && token == "" {
t.Error("Expected token, got empty string")
}
})
}
}
func TestValidateJWT(t *testing.T) {
// Generate test token
token, err := GenerateJWT("+1234567890", 1)
if err != nil {
t.Fatalf("Failed to generate test token: %v", err)
}
// Test validation
claims, err := ValidateJWT(token)
if err != nil {
t.Errorf("ValidateJWT() failed: %v", err)
}
if claims.PhoneNumber != "+1234567890" {
t.Errorf("Expected phone +1234567890, got %s", claims.PhoneNumber)
}
if claims.UserID != 1 {
t.Errorf("Expected userID 1, got %d", claims.UserID)
}
}
func TestValidateJWT_Expired(t *testing.T) {
// Create expired token (mock time.Now())
token := createExpiredToken()
_, err := ValidateJWT(token)
if err == nil {
t.Error("Expected error for expired token, got nil")
}
}
func TestValidateJWT_Invalid(t *testing.T) {
invalidTokens := []string{
"",
"invalid.token.format",
"eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.invalid",
}
for _, token := range invalidTokens {
_, err := ValidateJWT(token)
if err == nil {
t.Errorf("Expected error for invalid token %s, got nil", token)
}
}
}
Run Backend Tests:
# Run all tests
go test ./...
# Run with coverage
go test -cover ./...
# Detailed coverage
go test -coverprofile=coverage.out ./...
go tool cover -html=coverage.out
# Run specific test
go test -run TestGenerateJWT ./pkg/shared
# Verbose output
go test -v ./...
# Race detection
go test -race ./...
Desktop (TypeScript/Jest) Example:
// src/main/auth/service.test.ts
import { AuthService } from './service';
import Store from 'electron-store';
// Mock electron-store
jest.mock('electron-store');
describe('AuthService', () => {
let authService: AuthService;
let mockStore: jest.Mocked<Store>;
beforeEach(() => {
mockStore = new Store() as jest.Mocked<Store>;
authService = new AuthService(mockStore);
});
afterEach(() => {
jest.clearAllMocks();
});
describe('login', () => {
it('should successfully login with valid credentials', async () => {
// Mock API response
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({
success: true,
user: { id: 1, phoneNumber: '+1234567890' },
accessToken: 'mock_access_token',
refreshToken: 'mock_refresh_token',
expiresIn: 3600,
}),
});
const result = await authService.login('+1234567890', 'password123');
expect(result.success).toBe(true);
expect(result.user.phoneNumber).toBe('+1234567890');
expect(mockStore.set).toHaveBeenCalledWith('jwtToken', 'mock_access_token');
});
it('should fail with invalid credentials', async () => {
global.fetch = jest.fn().mockResolvedValue({
ok: false,
status: 401,
json: async () => ({
success: false,
error: 'Invalid credentials',
}),
});
const result = await authService.login('+1234567890', 'wrong_password');
expect(result.success).toBe(false);
expect(result.error).toBe('Invalid credentials');
expect(mockStore.set).not.toHaveBeenCalled();
});
it('should handle network errors', async () => {
global.fetch = jest.fn().mockRejectedValue(
Object.assign(new Error('Network error'), { code: 'ECONNREFUSED' })
);
const result = await authService.login('+1234567890', 'password123');
expect(result.success).toBe(false);
expect(result.error).toContain('Backend server is not available');
expect(result.isNetworkError).toBe(true);
});
});
describe('refreshAccessToken', () => {
it('should refresh token successfully', async () => {
mockStore.get.mockReturnValue('old_refresh_token');
global.fetch = jest.fn().mockResolvedValue({
ok: true,
json: async () => ({
success: true,
accessToken: 'new_access_token',
refreshToken: 'new_refresh_token',
expiresIn: 3600,
}),
});
await authService.refreshAccessToken();
expect(mockStore.set).toHaveBeenCalledWith('jwtToken', 'new_access_token');
expect(mockStore.set).toHaveBeenCalledWith('refreshToken', 'new_refresh_token');
});
});
});
Run Desktop Tests:
# Run all tests
npm test
# Run with coverage
npm test -- --coverage
# Watch mode
npm test -- --watch
# Run specific test file
npm test -- auth/service.test.ts
# Update snapshots
npm test -- -u
iOS (Swift/XCTest) Example:
// BarqNetTests/AuthServiceTests.swift
import XCTest
@testable import BarqNet
class AuthServiceTests: XCTestCase {
var authService: AuthService!
var mockAPIClient: MockAPIClient!
var mockKeychainManager: MockKeychainManager!
override func setUp() {
super.setUp()
mockAPIClient = MockAPIClient()
mockKeychainManager = MockKeychainManager()
authService = AuthService(
apiClient: mockAPIClient,
keychainManager: mockKeychainManager
)
}
override func tearDown() {
authService = nil
mockAPIClient = nil
mockKeychainManager = nil
super.tearDown()
}
func testLogin_Success() async throws {
// Given
mockAPIClient.loginResponse = LoginResponse(
success: true,
user: User(id: 1, phoneNumber: "+1234567890"),
accessToken: "mock_token",
refreshToken: "mock_refresh",
expiresIn: 3600
)
// When
let result = try await authService.login(
phoneNumber: "+1234567890",
password: "password123"
)
// Then
XCTAssertTrue(result.success)
XCTAssertEqual(result.user?.phoneNumber, "+1234567890")
XCTAssertTrue(mockKeychainManager.didSaveToken)
XCTAssertEqual(mockKeychainManager.savedToken, "mock_token")
}
func testLogin_InvalidCredentials() async {
// Given
mockAPIClient.shouldFail = true
mockAPIClient.errorMessage = "Invalid credentials"
// When/Then
do {
_ = try await authService.login(
phoneNumber: "+1234567890",
password: "wrong_password"
)
XCTFail("Expected error to be thrown")
} catch {
XCTAssertTrue(error is AuthError)
XCTAssertFalse(mockKeychainManager.didSaveToken)
}
}
func testLogin_NetworkError() async {
// Given
mockAPIClient.shouldFailWithNetworkError = true
// When/Then
do {
_ = try await authService.login(
phoneNumber: "+1234567890",
password: "password123"
)
XCTFail("Expected error to be thrown")
} catch {
XCTAssertTrue(error is NetworkError)
}
}
}
// Mock classes
class MockAPIClient: APIClientProtocol {
var loginResponse: LoginResponse?
var shouldFail = false
var shouldFailWithNetworkError = false
var errorMessage = ""
func login(phoneNumber: String, password: String) async throws -> LoginResponse {
if shouldFailWithNetworkError {
throw NetworkError.connectionFailed
}
if shouldFail {
throw AuthError.invalidCredentials(errorMessage)
}
return loginResponse!
}
}
class MockKeychainManager: KeychainManagerProtocol {
var didSaveToken = false
var savedToken: String?
func saveToken(_ token: String) {
didSaveToken = true
savedToken = token
}
func getToken() -> String? {
return savedToken
}
}
Run iOS Tests:
# Run all tests
xcodebuild test -scheme BarqNet -destination 'platform=iOS Simulator,name=iPhone 15'
# Run specific test
xcodebuild test -scheme BarqNet -only-testing:BarqNetTests/AuthServiceTests
# Generate coverage
xcodebuild test -scheme BarqNet -enableCodeCoverage YES
# View coverage
open DerivedData/.../Coverage.xcresult
Android (Kotlin/JUnit) Example:
// app/src/test/java/com/chameleon/barqnet/AuthServiceTest.kt
import org.junit.Before
import org.junit.Test
import org.junit.Assert.*
import org.mockito.Mock
import org.mockito.Mockito.*
import org.mockito.MockitoAnnotations
import kotlinx.coroutines.runBlocking
class AuthServiceTest {
@Mock
private lateinit var apiClient: APIClient
@Mock
private lateinit var tokenManager: TokenManager
private lateinit var authService: AuthService
@Before
fun setup() {
MockitoAnnotations.openMocks(this)
authService = AuthService(apiClient, tokenManager)
}
@Test
fun `login with valid credentials returns success`() = runBlocking {
// Given
val phoneNumber = "+1234567890"
val password = "password123"
val mockResponse = LoginResponse(
success = true,
user = User(id = 1, phoneNumber = phoneNumber),
accessToken = "mock_token",
refreshToken = "mock_refresh",
expiresIn = 3600
)
`when`(apiClient.login(phoneNumber, password)).thenReturn(mockResponse)
// When
val result = authService.login(phoneNumber, password)
// Then
assertTrue(result.success)
assertEquals(phoneNumber, result.user?.phoneNumber)
verify(tokenManager).saveTokens(
accessToken = "mock_token",
refreshToken = "mock_refresh",
expiresIn = 3600
)
}
@Test
fun `login with invalid credentials returns error`() = runBlocking {
// Given
val phoneNumber = "+1234567890"
val password = "wrong_password"
`when`(apiClient.login(phoneNumber, password))
.thenThrow(APIException("Invalid credentials"))
// When/Then
try {
authService.login(phoneNumber, password)
fail("Expected APIException")
} catch (e: APIException) {
assertEquals("Invalid credentials", e.message)
verify(tokenManager, never()).saveTokens(any(), any(), any())
}
}
@Test
fun `login with network error returns network error`() = runBlocking {
// Given
`when`(apiClient.login(any(), any()))
.thenThrow(IOException("Network error"))
// When/Then
try {
authService.login("+1234567890", "password123")
fail("Expected IOException")
} catch (e: IOException) {
assertTrue(e.message!!.contains("Network"))
}
}
}
Run Android Tests:
# Run unit tests
./gradlew test
# Run with coverage
./gradlew jacocoTestReport
# Run specific test
./gradlew test --tests AuthServiceTest
# View results
open app/build/reports/tests/testDebugUnitTest/index.html
2. Integration Tests
Purpose: Test interaction between components
Characteristics:
- Moderate speed (100ms - 5s)
- Uses real dependencies (database, APIs)
- Tests data flow between components
- Run before deployment
Backend Integration Test Example:
// apps/management/api/auth_integration_test.go
package api
import (
"bytes"
"database/sql"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
func setupTestDB(t *testing.T) *sql.DB {
// Setup test database
db, err := sql.Open("postgres", "postgres://test:test@localhost/chameleon_test")
if err != nil {
t.Fatalf("Failed to open test DB: %v", err)
}
// Run migrations
runMigrations(db)
return db
}
func teardownTestDB(db *sql.DB) {
// Clean up test data
db.Exec("TRUNCATE users CASCADE")
db.Close()
}
func TestRegistrationFlow_Integration(t *testing.T) {
db := setupTestDB(t)
defer teardownTestDB(db)
authHandler := NewAuthHandler(db, NewLocalOTPService())
// Test 1: Send OTP
t.Run("Send OTP", func(t *testing.T) {
body := map[string]string{
"phone_number": "+1234567890",
"country_code": "+1",
}
bodyBytes, _ := json.Marshal(body)
req := httptest.NewRequest("POST", "/v1/auth/send-otp", bytes.NewBuffer(bodyBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
authHandler.HandleSendOTP(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status 200, got %d", w.Code)
}
var response map[string]interface{}
json.NewDecoder(w.Body).Decode(&response)
if !response["success"].(bool) {
t.Error("Expected success: true")
}
})
// Test 2: Register with OTP
t.Run("Register with OTP", func(t *testing.T) {
// Get OTP from logs (in test we can retrieve it)
otpCode := authHandler.otpService.(*LocalOTPService).GetOTPForTesting("+1234567890")
body := map[string]string{
"phone_number": "+1234567890",
"password": "SecurePass123!",
"verification_token": otpCode,
}
bodyBytes, _ := json.Marshal(body)
req := httptest.NewRequest("POST", "/v1/auth/register", bytes.NewBuffer(bodyBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
authHandler.HandleRegister(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status 200, got %d", w.Code)
}
var response map[string]interface{}
json.NewDecoder(w.Body).Decode(&response)
if !response["success"].(bool) {
t.Errorf("Registration failed: %v", response["error"])
}
// Verify user created in database
var count int
db.QueryRow("SELECT COUNT(*) FROM users WHERE phone_number = $1", "+1234567890").Scan(&count)
if count != 1 {
t.Errorf("Expected 1 user, got %d", count)
}
})
// Test 3: Login with created account
t.Run("Login", func(t *testing.T) {
body := map[string]string{
"phone_number": "+1234567890",
"password": "SecurePass123!",
}
bodyBytes, _ := json.Marshal(body)
req := httptest.NewRequest("POST", "/v1/auth/login", bytes.NewBuffer(bodyBytes))
req.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
authHandler.HandleLogin(w, req)
if w.Code != http.StatusOK {
t.Errorf("Expected status 200, got %d", w.Code)
}
var response map[string]interface{}
json.NewDecoder(w.Body).Decode(&response)
if response["accessToken"] == nil {
t.Error("Expected access token in response")
}
})
}
3. End-to-End Tests
Purpose: Test complete user workflows
Characteristics:
- Slow (5s - 60s)
- Uses real environment
- Tests from user perspective
- Run before releases
Desktop E2E Test (Playwright) Example:
// e2e/auth-flow.spec.ts
import { test, expect, _electron as electron } from '@playwright/test';
import { ElectronApplication, Page } from 'playwright';
let electronApp: ElectronApplication;
let window: Page;
test.beforeAll(async () => {
electronApp = await electron.launch({ args: ['.'] });
window = await electronApp.firstWindow();
});
test.afterAll(async () => {
await electronApp.close();
});
test.describe('Authentication Flow', () => {
test('complete registration flow', async () => {
// Navigate to registration
await window.click('[data-testid="create-account-button"]');
// Enter phone number
await window.fill('[data-testid="phone-input"]', '+1234567890');
await window.click('[data-testid="send-otp-button"]');
// Wait for OTP sent confirmation
await expect(window.locator('[data-testid="otp-sent-message"]'))
.toBeVisible();
// Enter OTP (in test environment, we know the OTP)
await window.fill('[data-testid="otp-input"]', '123456');
await window.click('[data-testid="verify-otp-button"]');
// Wait for OTP verification
await expect(window.locator('[data-testid="otp-verified-message"]'))
.toBeVisible();
// Enter password
await window.fill('[data-testid="password-input"]', 'SecurePass123!');
await window.fill('[data-testid="confirm-password-input"]', 'SecurePass123!');
await window.click('[data-testid="create-account-button"]');
// Verify redirect to dashboard
await expect(window.locator('[data-testid="dashboard"]'))
.toBeVisible({ timeout: 10000 });
});
test('login flow', async () => {
// Ensure on login screen
await window.click('[data-testid="logout-button"]');
// Enter credentials
await window.fill('[data-testid="phone-input"]', '+1234567890');
await window.fill('[data-testid="password-input"]', 'SecurePass123!');
await window.click('[data-testid="login-button"]');
// Verify dashboard shown
await expect(window.locator('[data-testid="dashboard"]'))
.toBeVisible({ timeout: 10000 });
});
test('VPN connection flow', async () => {
// Select server
await window.selectOption('[data-testid="server-select"]', 'us-east-1');
// Connect
await window.click('[data-testid="connect-button"]');
// Wait for connection
await expect(window.locator('[data-testid="status-connected"]'))
.toBeVisible({ timeout: 30000 });
// Verify statistics updating
const stats = window.locator('[data-testid="bytes-transferred"]');
await expect(stats).toBeVisible();
// Disconnect
await window.click('[data-testid="disconnect-button"]');
// Verify disconnected
await expect(window.locator('[data-testid="status-disconnected"]'))
.toBeVisible({ timeout: 10000 });
});
});
Run E2E Tests:
# Desktop
npm run test:e2e
# iOS (UI Tests)
xcodebuild test -scheme BarqNetUITests
# Android (Espresso)
./gradlew connectedAndroidTest
Test Coverage Requirements
Minimum Coverage Targets:
- Unit Tests: 80% code coverage
- Integration Tests: Critical paths covered
- E2E Tests: All user workflows covered
Priority Coverage:
- Authentication logic: 100%
- Security functions: 100%
- Payment logic: 100% (if applicable)
- Data persistence: 90%
- API handlers: 85%
- UI components: 70%
Testing Best Practices
1. Test Naming
Use descriptive names:
// ❌ BAD
func TestAuth(t *testing.T) { }
// ✅ GOOD
func TestAuthService_Login_WithValidCredentials_ReturnsSuccess(t *testing.T) { }
Pattern: Test{Component}_{Method}_{Scenario}_{ExpectedResult}
2. AAA Pattern
Arrange-Act-Assert:
test('login with valid credentials returns success', async () => {
// Arrange
const phoneNumber = '+1234567890';
const password = 'password123';
const mockResponse = { success: true, ... };
fetch.mockResolvedValue(mockResponse);
// Act
const result = await authService.login(phoneNumber, password);
// Assert
expect(result.success).toBe(true);
expect(result.user).toBeDefined();
});
3. Test Data Management
Use fixtures:
// test/fixtures/users.ts
export const validUser = {
phoneNumber: '+1234567890',
password: 'SecurePass123!',
};
export const invalidUser = {
phoneNumber: '',
password: '123',
};
4. Mocking
Mock external dependencies:
type MockOTPService struct {
SendCalled bool
VerifyCalled bool
OTPCode string
}
func (m *MockOTPService) Send(phoneNumber string) error {
m.SendCalled = true
return nil
}
func (m *MockOTPService) Verify(phoneNumber, code string) bool {
m.VerifyCalled = true
return code == m.OTPCode
}
Test Automation
CI/CD Integration
GitHub Actions Example:
# .github/workflows/test.yml
name: Test Suite
on: [push, pull_request]
jobs:
backend-tests:
runs-on: ubuntu-latest
services:
postgres:
image: postgres:14
env:
POSTGRES_PASSWORD: test
options: >-
--health-cmd pg_isready
--health-interval 10s
--health-timeout 5s
--health-retries 5
steps:
- uses: actions/checkout@v3
- uses: actions/setup-go@v4
with:
go-version: '1.21'
- name: Run tests
run: |
go test -v -race -coverprofile=coverage.out ./...
go tool cover -html=coverage.out -o coverage.html
- name: Upload coverage
uses: codecov/codecov-action@v3
with:
files: ./coverage.out
desktop-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-node@v3
with:
node-version: '18'
- name: Install dependencies
run: npm ci
- name: Run unit tests
run: npm test -- --coverage
- name: Run E2E tests
run: npm run test:e2e
ios-tests:
runs-on: macos-latest
steps:
- uses: actions/checkout@v3
- name: Run tests
run: |
xcodebuild test \
-scheme BarqNet \
-destination 'platform=iOS Simulator,name=iPhone 15'
android-tests:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: actions/setup-java@v3
with:
java-version: '17'
- name: Run unit tests
run: ./gradlew test
- name: Build APK
run: ./gradlew assembleDebug
Performance Testing
Load Testing Example (k6):
// load-test.js
import http from 'k6/http';
import { check, sleep } from 'k6';
export const options = {
stages: [
{ duration: '1m', target: 50 }, // Ramp up to 50 users
{ duration: '3m', target: 50 }, // Stay at 50 users
{ duration: '1m', target: 0 }, // Ramp down
],
thresholds: {
http_req_duration: ['p(95)<500'], // 95% of requests < 500ms
http_req_failed: ['rate<0.01'], // < 1% failure rate
},
};
export default function () {
// Login test
const loginRes = http.post('http://localhost:8080/v1/auth/login', JSON.stringify({
phone_number: '+1234567890',
password: 'password123',
}), {
headers: { 'Content-Type': 'application/json' },
});
check(loginRes, {
'login succeeded': (r) => r.status === 200,
'has access token': (r) => r.json('accessToken') !== undefined,
});
sleep(1);
}
Run Performance Tests:
k6 run load-test.js
Security Testing
OWASP ZAP Automation:
#!/bin/bash
# security-scan.sh
# Start backend
go run apps/management/main.go &
BACKEND_PID=$!
sleep 5
# Run ZAP scan
docker run -t owasp/zap2docker-stable zap-baseline.py \
-t http://localhost:8080 \
-r security-report.html
# Stop backend
kill $BACKEND_PID
echo "Security report: security-report.html"
Test Documentation
Test Plan Template:
# Test Plan: {Feature Name}
**Version:** 1.0
**Date:** 2025-10-26
**Owner:** Testing Team
## Scope
What will be tested and what won't be tested.
## Test Strategy
- Unit tests: {coverage target}%
- Integration tests: {number} test cases
- E2E tests: {number} scenarios
## Test Cases
### TC-001: Login with Valid Credentials
**Priority:** High
**Type:** Unit Test
**Preconditions:** User account exists
**Steps:**
1. Call login() with valid phone + password
2. Verify response contains access token
3. Verify token stored securely
**Expected Result:** Login succeeds, token stored
**Status:** Pass
### TC-002: ...
## Test Environment
- Backend: Go 1.21, PostgreSQL 14
- Desktop: Electron 25, Node 18
- iOS: iOS 17+
- Android: API 26+
## Schedule
- Unit tests: Week 1
- Integration tests: Week 2
- E2E tests: Week 3
- Performance tests: Week 4
## Risks
- Network instability in CI
- Database state issues
When to Use This Skill
✅ Use this skill when:
- Writing new test cases
- Implementing test automation
- Debugging test failures
- Setting up test infrastructure
- Generating test reports
- Planning test strategies
- Conducting QA activities
❌ Don't use this skill for:
- Writing production code (use platform skills)
- Documentation (use barqnet-documentation)
- Code audits (use barqnet-audit)
- Integration setup (use barqnet-integration)
Success Criteria
Testing is complete when:
- ✅ All test types implemented (unit, integration, E2E)
- ✅ Coverage targets met (80%+ unit tests)
- ✅ All critical paths tested
- ✅ Tests run in CI/CD pipeline
- ✅ Test reports generated
- ✅ Flaky tests fixed or removed
- ✅ Performance benchmarks met
- ✅ Security tests pass
- ✅ Test documentation complete
- ✅ All tests green before deployment