Files
pos-system/tests/load/k6/ads-tracking.js

122 lines
4.4 KiB
JavaScript

/**
* EN: k6 load test — Ads tracking event ingestion (POST /api/v1/tracking/events).
* VI: k6 load test — nhập event tracking quảng cáo (POST /api/v1/tracking/events).
*
* EN: Ads tracking is a high-throughput, fire-and-forget endpoint.
* VI: Tracking là endpoint thông lượng cao, fire-and-forget.
*
* Usage:
* k6 run tests/load/k6/ads-tracking.js
* k6 run --env BASE_URL=http://api.staging.goodgo.vn tests/load/k6/ads-tracking.js
*
* Thresholds:
* - 95th-percentile response time < 100ms
* - Error rate < 0.1%
* - Throughput > 500 req/s
*/
import http from 'k6/http';
import { check, sleep } from 'k6';
import { Rate, Trend, Counter } from 'k6/metrics';
// ---------------------------------------------------------------------------
// Custom metrics
// ---------------------------------------------------------------------------
const trackingErrors = new Rate('tracking_errors');
const trackingDuration = new Trend('tracking_duration', true);
const eventsIngested = new Counter('events_ingested');
// ---------------------------------------------------------------------------
// Test options — simulate high-frequency event stream
// ---------------------------------------------------------------------------
export const options = {
stages: [
{ duration: '20s', target: 100 },
{ duration: '1m', target: 300 },
{ duration: '30s', target: 500 }, // EN: Peak throughput / VI: Đỉnh thông lượng
{ duration: '20s', target: 0 },
],
thresholds: {
// EN: Tracking must be very fast — p95 < 100ms / VI: Tracking phải rất nhanh
http_req_duration: ['p(95)<100', 'p(99)<200'],
tracking_errors: ['rate<0.001'],
tracking_duration: ['p(95)<100'],
},
};
// ---------------------------------------------------------------------------
// Config
// ---------------------------------------------------------------------------
const BASE_URL = __ENV.BASE_URL || 'http://localhost:5080';
const API_KEY = __ENV.TRACKING_API_KEY || 'test-tracking-key';
const headers = {
'Content-Type': 'application/json',
'X-Api-Key': API_KEY,
};
// ---------------------------------------------------------------------------
// Event types
// ---------------------------------------------------------------------------
const EVENT_TYPES = ['impression', 'click', 'view', 'conversion', 'engagement'];
const AD_IDS = [
'00000000-0000-0000-0000-000000000011',
'00000000-0000-0000-0000-000000000012',
'00000000-0000-0000-0000-000000000013',
];
const CAMPAIGN_IDS = [
'00000000-0000-0000-0000-000000000021',
'00000000-0000-0000-0000-000000000022',
];
// ---------------------------------------------------------------------------
// Helper — build a tracking event payload
// ---------------------------------------------------------------------------
function buildTrackingEvent() {
const eventType = EVENT_TYPES[Math.floor(Math.random() * EVENT_TYPES.length)];
const adId = AD_IDS[Math.floor(Math.random() * AD_IDS.length)];
const campaignId = CAMPAIGN_IDS[Math.floor(Math.random() * CAMPAIGN_IDS.length)];
return JSON.stringify({
eventType,
adId,
campaignId,
sessionId: `sess-${Math.random().toString(36).substring(2, 10)}`,
userId: Math.random() > 0.3 ? `user-${Math.floor(Math.random() * 10000)}` : null,
deviceType: ['mobile', 'desktop', 'tablet'][Math.floor(Math.random() * 3)],
platform: ['ios', 'android', 'web'][Math.floor(Math.random() * 3)],
timestamp: new Date().toISOString(),
metadata: {
pageUrl: '/pos/menu',
referrer: 'organic',
duration: eventType === 'view' ? Math.floor(Math.random() * 10000) : null,
},
});
}
// ---------------------------------------------------------------------------
// Default function
// ---------------------------------------------------------------------------
export default function () {
const payload = buildTrackingEvent();
const res = http.post(`${BASE_URL}/api/v1/tracking/events`, payload, { headers });
trackingDuration.add(res.timings.duration);
const ok = check(res, {
// EN: 202 Accepted or 200 OK / VI: 202 Accepted hoặc 200 OK
'status 200 or 202': (r) => r.status === 200 || r.status === 202,
'response < 100ms': (r) => r.timings.duration < 100,
});
if (ok) {
eventsIngested.add(1);
trackingErrors.add(0);
} else {
trackingErrors.add(1);
}
// EN: No sleep — fire-and-forget high throughput / VI: Không sleep — thông lượng cao
sleep(0.01);
}