- K6_ENDPOINTS_SUMMARY.md: Quick reference for all API endpoints with request/response shapes - K6_QUICK_START.md: Practical guide with executable examples for search, auth, listing, and payment load tests - Includes example K6 scripts, CI integration template, and troubleshooting - Complete with load test scenarios and reporting options Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
343 lines
9.5 KiB
Markdown
343 lines
9.5 KiB
Markdown
# GoodGo Platform — K6 Load Testing Endpoints Summary
|
|
|
|
Quick reference for all testable API endpoints.
|
|
|
|
## 📍 Base URL
|
|
```
|
|
http://localhost:3001/api/v1
|
|
```
|
|
|
|
---
|
|
|
|
## 🔐 Authentication (Auth Module)
|
|
|
|
### Public Endpoints (No Auth Required)
|
|
|
|
| Method | Path | Rate Limit | Purpose |
|
|
|--------|------|-----------|---------|
|
|
| **POST** | `/auth/register` | 5/hour | Register new user with phone/password/name |
|
|
| **POST** | `/auth/login` | 5/hour | Login with phone/password (basic auth) |
|
|
| **POST** | `/auth/refresh` | 5/hour | Refresh expired access token |
|
|
| **POST** | `/auth/logout` | None | Clear auth cookies |
|
|
| **POST** | `/auth/exchange-token` | None | Exchange OAuth tokens for httpOnly cookies |
|
|
|
|
### Protected Endpoints (JWT Required)
|
|
|
|
| Method | Path | Rate Limit | Purpose |
|
|
|--------|------|-----------|---------|
|
|
| **GET** | `/auth/profile` | None | Get authenticated user profile |
|
|
| **GET** | `/auth/profile/agent` | None | Get agent profile (if user is agent) |
|
|
|
|
### Admin Only
|
|
|
|
| Method | Path | Rate Limit | Auth | Purpose |
|
|
|--------|------|-----------|------|---------|
|
|
| **PATCH** | `/auth/kyc` | None | JWT+Admin | Verify/update user KYC status |
|
|
|
|
---
|
|
|
|
## 🏠 Listings (Listings Module)
|
|
|
|
### Public Endpoints
|
|
|
|
| Method | Path | Rate Limit | Purpose |
|
|
|--------|------|-----------|---------|
|
|
| **GET** | `/listings` | None | Search/filter listings (queryable) |
|
|
| **GET** | `/listings/:id` | None | Get listing detail by ID |
|
|
|
|
### Protected Endpoints (JWT Required)
|
|
|
|
| Method | Path | Quota Gate | Purpose |
|
|
|--------|------|-----------|---------|
|
|
| **POST** | `/listings` | Yes | Create new property listing |
|
|
| **PATCH** | `/listings/:id/status` | No | Update listing status (owner only) |
|
|
| **POST** | `/listings/:id/media` | No | Upload photo/video for listing (owner only) |
|
|
|
|
### Admin Only
|
|
|
|
| Method | Path | Purpose |
|
|
|--------|------|---------|
|
|
| **GET** | `/listings/pending` | Get listings awaiting moderation (paginated) |
|
|
| **PATCH** | `/listings/:id/moderate` | Approve/reject listing & score it |
|
|
|
|
---
|
|
|
|
## 💳 Payments (Payments Module)
|
|
|
|
### Protected Endpoints (JWT Required)
|
|
|
|
| Method | Path | Purpose |
|
|
|--------|------|---------|
|
|
| **POST** | `/payments` | Create payment (initiates payment flow) |
|
|
| **GET** | `/payments` | List user's transactions (paginated) |
|
|
| **GET** | `/payments/:id` | Get payment status by ID |
|
|
|
|
### Admin Only
|
|
|
|
| Method | Path | Purpose |
|
|
|--------|------|---------|
|
|
| **POST** | `/payments/:id/refund` | Initiate refund for payment |
|
|
|
|
### Webhook (Unthrottled, No Auth)
|
|
|
|
| Method | Path | Rate Limit | Purpose |
|
|
|--------|------|-----------|---------|
|
|
| **POST** | `/payments/callback/:provider` | 20/min | Handle payment provider callbacks (VNPay, MoMo, ZaloPay) |
|
|
|
|
---
|
|
|
|
## 🔍 Search (Search Module)
|
|
|
|
### Public Endpoints
|
|
|
|
| Method | Path | Purpose | Query Params |
|
|
|--------|------|---------|--------------|
|
|
| **GET** | `/search` | Full-text search listings | q, propertyType, transactionType, priceMin/Max, areaMin/Max, bedrooms, district, city, sortBy, page, perPage |
|
|
| **GET** | `/search/geo` | Geographic radius search | lat, lng, radiusKm, propertyType, transactionType, priceMin/Max, sortBy, page, perPage |
|
|
|
|
### Admin Only
|
|
|
|
| Method | Path | Purpose |
|
|
|--------|------|---------|
|
|
| **POST** | `/search/reindex` | Reindex all properties in search engine |
|
|
|
|
---
|
|
|
|
## 📊 Key Data Shapes
|
|
|
|
### User Registration
|
|
```json
|
|
POST /auth/register
|
|
{
|
|
"phone": "0901234567",
|
|
"password": "SecurePass123!",
|
|
"fullName": "Nguyen Van A",
|
|
"email": "user@example.com" // optional
|
|
}
|
|
```
|
|
|
|
### User Login
|
|
```json
|
|
POST /auth/login
|
|
{
|
|
"phone": "0901234567",
|
|
"password": "SecurePass123!"
|
|
}
|
|
```
|
|
|
|
### Create Listing (Minimal)
|
|
```json
|
|
POST /listings
|
|
{
|
|
"transactionType": "SALE",
|
|
"priceVND": "5500000000",
|
|
"propertyType": "APARTMENT",
|
|
"title": "Căn hộ 3PN view sông",
|
|
"description": "Căn hộ cao cấp 3 phòng ngủ, nội thất đầy đủ...",
|
|
"address": "208 Nguyễn Hữu Cảnh",
|
|
"ward": "Phường 22",
|
|
"district": "Bình Thạnh",
|
|
"city": "Hồ Chí Minh",
|
|
"latitude": 10.7942,
|
|
"longitude": 106.7219,
|
|
"areaM2": 85.5
|
|
}
|
|
```
|
|
|
|
### Search Listings
|
|
```json
|
|
GET /listings?transactionType=SALE&city=Hồ Chi Minh&minPrice=2000000000&maxPrice=10000000000&page=1&limit=20
|
|
```
|
|
|
|
### Full-Text Search
|
|
```json
|
|
GET /search?q=chung cu quan 7&propertyType=apartment&transactionType=sale&page=1&perPage=20
|
|
```
|
|
|
|
### Geo Search
|
|
```json
|
|
GET /search/geo?lat=10.7769&lng=106.7009&radiusKm=5&transactionType=sale&page=1&perPage=20
|
|
```
|
|
|
|
### Create Payment
|
|
```json
|
|
POST /payments
|
|
{
|
|
"provider": "VNPAY",
|
|
"type": "LISTING_FEE",
|
|
"amountVND": 500000,
|
|
"description": "Listing fee",
|
|
"returnUrl": "https://example.com/return",
|
|
"idempotencyKey": "uuid-123"
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 🎯 K6 Test Scenarios
|
|
|
|
### Load Test 1: Search Load (High Volume, Public)
|
|
- **Endpoint**: `GET /search` & `GET /search/geo`
|
|
- **Load Profile**: Ramp 100 → 1000 users over 10 min
|
|
- **Success Criteria**: p(95) < 500ms, p(99) < 1000ms
|
|
- **Expected**: Public search should handle high concurrent load
|
|
|
|
### Load Test 2: Authentication (Moderate Load, Gated)
|
|
- **Endpoint**: `POST /auth/register`, `POST /auth/login`
|
|
- **Load Profile**: Ramp 10 → 100 users
|
|
- **Rate Limit**: 5/hour per endpoint
|
|
- **Success Criteria**: All requests within rate limit, p(95) < 300ms
|
|
- **Expected**: Should gracefully reject over-limit requests
|
|
|
|
### Load Test 3: Listing Creation (Low Load, Quota Gated)
|
|
- **Endpoint**: `POST /listings`
|
|
- **Load Profile**: Ramp 5 → 50 users over 5 min
|
|
- **Rate Limit**: Quota-gated (per subscription plan)
|
|
- **Success Criteria**: 201 for successful creates, 403 for quota exceeded
|
|
- **Expected**: Quota guard enforces plan limits
|
|
|
|
### Load Test 4: Payment Processing (Medium Load, Unthrottled)
|
|
- **Endpoint**: `POST /payments`
|
|
- **Load Profile**: Ramp 20 → 200 users
|
|
- **Dependencies**: Requires authenticated session (JWT)
|
|
- **Success Criteria**: p(95) < 1s (external provider latency)
|
|
- **Expected**: System handles payment initiation concurrently
|
|
|
|
### Load Test 5: Payment Webhooks (High Volume, Throttled)
|
|
- **Endpoint**: `POST /payments/callback/vnpay`
|
|
- **Load Profile**: Sustained 20 requests/min (rate limit)
|
|
- **Rate Limit**: 20/min enforced
|
|
- **Success Criteria**: All requests process within rate limit
|
|
- **Expected**: Webhook queue prevents abuse
|
|
|
|
---
|
|
|
|
## 🔗 Authentication Flow for K6
|
|
|
|
### Cookie-Based Flow (Recommended)
|
|
```javascript
|
|
// 1. Register/Login
|
|
const loginRes = http.post(`${BASE_URL}/auth/login`, {
|
|
phone, password
|
|
});
|
|
// Cookies: access_token, refresh_token, goodgo_authenticated
|
|
|
|
// 2. Use cookies for authenticated requests
|
|
const profileRes = http.get(`${BASE_URL}/auth/profile`);
|
|
// Browser automatically sends cookies
|
|
|
|
// 3. Refresh when needed
|
|
const refreshRes = http.post(`${BASE_URL}/auth/refresh`);
|
|
```
|
|
|
|
### Token-Based Flow (Alternative)
|
|
```javascript
|
|
// 1. Capture tokens from register/login
|
|
const loginRes = http.post(`${BASE_URL}/auth/login`, { phone, password });
|
|
const { accessToken, refreshToken } = loginRes.json();
|
|
|
|
// 2. Use Bearer token header
|
|
const params = {
|
|
headers: { Authorization: `Bearer ${accessToken}` }
|
|
};
|
|
const profileRes = http.get(`${BASE_URL}/auth/profile`, params);
|
|
|
|
// 3. Refresh access token
|
|
const refreshRes = http.post(`${BASE_URL}/auth/refresh`,
|
|
{ refreshToken },
|
|
params
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## 🏪 Test Data Resources
|
|
|
|
### Available Seed Data
|
|
- **Users**: Various roles (USER, AGENT, ADMIN)
|
|
- **Listings**: Properties across Ho Chi Minh City districts
|
|
- **Districts/Wards**: Vietnamese administrative data
|
|
- **Property Types**: APARTMENT, HOUSE, LAND, SHOP, etc.
|
|
- **Transaction Types**: SALE, RENT
|
|
|
|
### Test Fixtures
|
|
- See `e2e/fixtures.ts` for test data generators
|
|
- Use `createTestUser()` to generate unique test users
|
|
- Test database seeded in `global-setup.ts`
|
|
|
|
---
|
|
|
|
## ⚡ Quick K6 Script Template
|
|
|
|
```javascript
|
|
import http from 'k6/http';
|
|
import { check, group, sleep } from 'k6';
|
|
|
|
const BASE_URL = __ENV.BASE_URL || 'http://localhost:3001/api/v1';
|
|
|
|
export const options = {
|
|
stages: [
|
|
{ duration: '2m', target: 100 }, // Ramp up
|
|
{ duration: '5m', target: 100 }, // Sustain
|
|
{ duration: '2m', target: 0 }, // Ramp down
|
|
],
|
|
thresholds: {
|
|
http_req_duration: ['p(95)<500', 'p(99)<1000'],
|
|
http_req_failed: ['rate<0.1'],
|
|
},
|
|
};
|
|
|
|
export default function() {
|
|
group('Search - Public, High Volume', () => {
|
|
const res = http.get(`${BASE_URL}/search?q=chung cu&page=1&perPage=20`);
|
|
check(res, { 'status is 200': (r) => r.status === 200 });
|
|
sleep(1);
|
|
});
|
|
|
|
group('Geo Search - Public', () => {
|
|
const res = http.get(`${BASE_URL}/search/geo?lat=10.77&lng=106.70&radiusKm=5&perPage=20`);
|
|
check(res, { 'status is 200': (r) => r.status === 200 });
|
|
sleep(1);
|
|
});
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## 📌 Important Rate Limits
|
|
|
|
| Endpoint | Limit | Window |
|
|
|----------|-------|--------|
|
|
| `/auth/register` | 5 | per hour |
|
|
| `/auth/login` | 5 | per hour |
|
|
| `/auth/refresh` | 5 | per hour |
|
|
| `/payments/callback/*` | 20 | per minute |
|
|
| Others | None | (quota gates apply instead) |
|
|
|
|
---
|
|
|
|
## 📁 Files to Reference
|
|
|
|
```
|
|
K6_LOAD_TESTING_GUIDE.md # Comprehensive guide (THIS FILE IS SUMMARY OF)
|
|
apps/api/src/modules/*/presentation/controllers/
|
|
apps/api/src/modules/*/presentation/dto/
|
|
e2e/fixtures.ts # Test data generators
|
|
e2e/api/ # Existing E2E tests (reference)
|
|
.env.example # Environment setup
|
|
```
|
|
|
|
---
|
|
|
|
## ✅ Checklist Before Running K6
|
|
|
|
- [ ] API running: `pnpm dev`
|
|
- [ ] Database seeded: `pnpm db:seed`
|
|
- [ ] Test database migrated: `.env.test` configured
|
|
- [ ] K6 installed: `brew install k6` or Docker
|
|
- [ ] JWT_SECRET set in `.env`
|
|
- [ ] Base URL correct: `http://localhost:3001/api/v1`
|
|
|
|
---
|
|
|