Files
website-enchun-mgr/apps/backend/tests/k6/lib/config.js
pkupuk 7fd73e0e3d Implement Sprint 1 stories: collections, RBAC, audit logging, load testing
Complete 6 Sprint 1 stories for Epic 1 web migration infrastructure.

Portfolio Collection:
- Add 7 fields: title, slug, url, image, description, websiteType, tags
- Configure R2 storage and authenticated access control

Categories Collection:
- Add nameEn, order, textColor, backgroundColor fields
- Add color picker UI configuration

Posts Collection:
- Add excerpt with 200 char limit and ogImage for social sharing
- Add showInFooter checkbox and status select (draft/review/published)

Role-Based Access Control:
- Add role field to Users collection (admin/editor)
- Create adminOnly and authenticated access functions
- Apply access rules to Portfolio, Categories, Posts, Users collections

Audit Logging System (NFR9):
- Create Audit collection with timestamps for 90-day retention
- Add auditLogger utility for login/logout/content change tracking
- Add auditChange and auditGlobalChange hooks to all collections and globals
- Add cleanupAuditLogs job with 90-day retention policy

Load Testing Framework (NFR4):
- Add k6 load testing with 3 scripts: public-browsing, admin-operations, api-performance
- Configure targets: p95 < 500ms, error rate < 1%, 100 concurrent users
- Add verification script and comprehensive documentation

Other Changes:
- Remove unused Form blocks
- Add Header/Footer audit hooks
- Regenerate Payload TypeScript types
2026-01-31 17:20:35 +08:00

196 lines
5.2 KiB
JavaScript

/**
* K6 Test Configuration
* Centralized configuration for all load tests
*/
// Get environment variables with defaults
export const config = {
// Base URL for the target server
get baseUrl() {
return __ENV.BASE_URL || 'http://localhost:3000';
},
// Admin credentials (from environment)
get adminEmail() {
return __ENV.ADMIN_EMAIL || 'admin@enchun.tw';
},
get adminPassword() {
return __ENV.ADMIN_PASSWORD || 'admin123';
},
// Load testing thresholds (NFR4 requirements)
thresholds: {
// Response time thresholds
httpReqDuration: ['p(95) < 500'], // 95th percentile < 500ms
httpReqDurationApi: ['p(95) < 300'], // API endpoints < 300ms
httpReqDurationAdmin: ['p(95) < 700'], // Admin operations < 700ms
// Error rate thresholds
httpReqFailed: ['rate < 0.01'], // < 1% error rate
httpReqFailedApi: ['rate < 0.005'], // API < 0.5% error rate
// Throughput thresholds
httpReqs: ['rate > 50'], // > 50 requests/second for pages
httpReqsApi: ['rate > 100'], // > 100 requests/second for API
// Check success rate
checks: ['rate > 0.99'], // 99% of checks should pass
},
// Stage configuration for gradual ramp-up
stages: {
// Public browsing: 100 users
publicBrowsing: [
{ duration: '30s', target: 20 }, // Ramp up to 20 users
{ duration: '30s', target: 50 }, // Ramp up to 50 users
{ duration: '1m', target: 100 }, // Ramp up to 100 users
{ duration: '2m', target: 100 }, // Stay at 100 users
{ duration: '30s', target: 0 }, // Ramp down
],
// Admin operations: 20 users
adminOperations: [
{ duration: '30s', target: 5 }, // Ramp up to 5 users
{ duration: '30s', target: 10 }, // Ramp up to 10 users
{ duration: '30s', target: 20 }, // Ramp up to 20 users
{ duration: '3m', target: 20 }, // Stay at 20 users
{ duration: '30s', target: 0 }, // Ramp down
],
// API performance: 50 users
apiPerformance: [
{ duration: '1m', target: 10 }, // Ramp up to 10 users
{ duration: '1m', target: 25 }, // Ramp up to 25 users
{ duration: '1m', target: 50 }, // Ramp up to 50 users
{ duration: '5m', target: 50 }, // Stay at 50 users
{ duration: '1m', target: 0 }, // Ramp down
],
},
// Common request options
requestOptions: {
timeout: '30s', // Request timeout
maxRedirects: 10, // Maximum redirects to follow
discardResponseBodies: true, // Discard response bodies to save memory
},
// Common headers
headers: {
'User-Agent': 'k6-load-test',
'Accept': 'text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8',
'Accept-Language': 'en-US,en;q=0.9,zh-TW;q=0.8',
},
};
// URL helpers
export const urls = {
get baseUrl() {
return config.baseUrl;
},
// Public pages
get home() {
return `${config.baseUrl}/`;
},
get about() {
return `${config.baseUrl}/about`;
},
get solutions() {
return `${config.baseUrl}/solutions`;
},
get portfolio() {
return `${config.baseUrl}/portfolio`;
},
get blog() {
return `${config.baseUrl}/blog`;
},
get contact() {
return `${config.baseUrl}/contact`;
},
// API endpoints
get api() {
return `${config.baseUrl}/api`;
},
get graphql() {
return `${config.baseUrl}/api/graphql`;
},
get global() {
return `${config.baseUrl}/api/global`;
},
// Admin endpoints
get admin() {
return `${config.baseUrl}/admin`;
},
get collections() {
return `${config.baseUrl}/api/collections`;
},
get pages() {
return `${config.baseUrl}/api/pages`;
},
get posts() {
return `${config.baseUrl}/api/posts`;
},
get portfolioItems() {
return `${config.baseUrl}/api/portfolio`;
},
get categories() {
return `${config.baseUrl}/api/categories`;
},
// Auth endpoints
get login() {
return `${config.baseUrl}/api/users/login`;
},
get logout() {
return `${config.baseUrl}/api/users/logout`;
},
get me() {
return `${config.baseUrl}/api/users/me`;
},
};
// Common checks
export const checks = {
// HTTP status checks
status200: (res) => res.status === 200,
status201: (res) => res.status === 201,
status204: (res) => res.status === 204,
// Response time checks
responseTimeFast: (res) => res.timings.duration < 200,
responseTimeOk: (res) => res.timings.duration < 500,
responseTimeSlow: (res) => res.timings.duration < 1000,
// Content checks
hasContent: (res) => res.body.length > 0,
hasJson: (res) => res.headers['Content-Type'].includes('application/json'),
// Performance checks (NFR4)
nfr4ResponseTime: (res) => res.timings.duration < 500, // p95 < 500ms
nfr4ApiResponseTime: (res) => res.timings.duration < 300, // API < 300ms
};
// Threshold groups for different test types
export const thresholdGroups = {
public: {
http_req_duration: ['p(95) < 500', 'p(99) < 1000'],
http_req_failed: ['rate < 0.01'],
checks: ['rate > 0.99'],
},
admin: {
http_req_duration: ['p(95) < 700', 'p(99) < 1500'],
http_req_failed: ['rate < 0.01'],
checks: ['rate > 0.99'],
},
api: {
http_req_duration: ['p(95) < 300', 'p(99) < 500'],
http_req_failed: ['rate < 0.005'],
checks: ['rate > 0.995'],
},
};