181 lines
7.0 KiB
JSON
181 lines
7.0 KiB
JSON
{
|
|
"$schema": "E2E Global API Monitoring Configuration",
|
|
"version": "1.0.0",
|
|
"description": "모든 E2E 테스트 시나리오에 적용되는 API 요청/응답 검증 설정",
|
|
"lastUpdated": "2026-01-31",
|
|
|
|
"apiMonitoring": {
|
|
"enabled": true,
|
|
"captureAllRequests": true,
|
|
"baseUrl": "https://dev.codebridge-x.com",
|
|
"apiPathPrefix": "/api/",
|
|
|
|
"captureConfig": {
|
|
"includePatterns": [
|
|
"/api/v1/**",
|
|
"/api/**"
|
|
],
|
|
"excludePatterns": [
|
|
"/_next/**",
|
|
"/static/**",
|
|
"*.js",
|
|
"*.css",
|
|
"*.png",
|
|
"*.jpg",
|
|
"*.svg",
|
|
"*.woff",
|
|
"*.woff2"
|
|
],
|
|
"captureRequestBody": true,
|
|
"captureResponseBody": true,
|
|
"maxBodySize": 10240
|
|
}
|
|
},
|
|
|
|
"validation": {
|
|
"defaultRules": {
|
|
"statusCodes": {
|
|
"success": [200, 201, 204],
|
|
"clientError": [400, 401, 403, 404],
|
|
"serverError": [500, 502, 503]
|
|
},
|
|
"responseTime": {
|
|
"warning": 2000,
|
|
"error": 5000
|
|
},
|
|
"contentType": {
|
|
"json": "application/json",
|
|
"pdf": "application/pdf",
|
|
"excel": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"
|
|
}
|
|
},
|
|
|
|
"methodRules": {
|
|
"GET": {
|
|
"expectedStatus": [200],
|
|
"maxResponseTime": 3000
|
|
},
|
|
"POST": {
|
|
"expectedStatus": [200, 201],
|
|
"maxResponseTime": 5000,
|
|
"requireResponseBody": true
|
|
},
|
|
"PUT": {
|
|
"expectedStatus": [200],
|
|
"maxResponseTime": 5000
|
|
},
|
|
"DELETE": {
|
|
"expectedStatus": [200, 204],
|
|
"maxResponseTime": 3000
|
|
}
|
|
}
|
|
},
|
|
|
|
"scripts": {
|
|
"setupApiMonitoring": "(function() { window.__API_LOGS__ = []; window.__API_ERRORS__ = []; const originalFetch = window.fetch; window.fetch = async function(...args) { const startTime = Date.now(); const url = typeof args[0] === 'string' ? args[0] : args[0].url; const method = args[1]?.method || 'GET'; try { const response = await originalFetch.apply(this, args); const endTime = Date.now(); const logEntry = { url, method, status: response.status, duration: endTime - startTime, timestamp: new Date().toISOString(), ok: response.ok }; window.__API_LOGS__.push(logEntry); if (!response.ok) { window.__API_ERRORS__.push(logEntry); } return response; } catch (error) { const errorEntry = { url, method, error: error.message, timestamp: new Date().toISOString() }; window.__API_ERRORS__.push(errorEntry); throw error; } }; return 'API monitoring initialized'; })()",
|
|
|
|
"getApiLogs": "(function() { return JSON.stringify(window.__API_LOGS__ || []); })()",
|
|
|
|
"getApiErrors": "(function() { return JSON.stringify(window.__API_ERRORS__ || []); })()",
|
|
|
|
"clearApiLogs": "(function() { window.__API_LOGS__ = []; window.__API_ERRORS__ = []; return 'API logs cleared'; })()",
|
|
|
|
"getApiSummary": "(function() { const logs = window.__API_LOGS__ || []; const errors = window.__API_ERRORS__ || []; const summary = { total: logs.length, success: logs.filter(l => l.ok).length, failed: errors.length, avgResponseTime: logs.length > 0 ? Math.round(logs.reduce((sum, l) => sum + (l.duration || 0), 0) / logs.length) : 0, slowRequests: logs.filter(l => l.duration > 2000).length, byMethod: {}, byStatus: {} }; logs.forEach(l => { summary.byMethod[l.method] = (summary.byMethod[l.method] || 0) + 1; summary.byStatus[l.status] = (summary.byStatus[l.status] || 0) + 1; }); return JSON.stringify(summary); })()",
|
|
|
|
"waitForApiCall": "(async function(urlPattern, timeout = 5000) { const start = Date.now(); while (Date.now() - start < timeout) { const logs = window.__API_LOGS__ || []; const found = logs.find(l => l.url.includes(urlPattern)); if (found) return JSON.stringify(found); await new Promise(r => setTimeout(r, 100)); } return JSON.stringify({ error: 'timeout', pattern: urlPattern }); })",
|
|
|
|
"validateApiCall": "(function(urlPattern, expectedStatus) { const logs = window.__API_LOGS__ || []; const matching = logs.filter(l => l.url.includes(urlPattern)); if (matching.length === 0) return JSON.stringify({ valid: false, error: 'API call not found', pattern: urlPattern }); const latest = matching[matching.length - 1]; const statusMatch = Array.isArray(expectedStatus) ? expectedStatus.includes(latest.status) : latest.status === expectedStatus; return JSON.stringify({ valid: statusMatch, call: latest, expected: expectedStatus }); })"
|
|
},
|
|
|
|
"commonApiEndpoints": {
|
|
"auth": {
|
|
"login": { "method": "POST", "path": "/api/v1/auth/login", "expectedStatus": 200 },
|
|
"logout": { "method": "POST", "path": "/api/v1/auth/logout", "expectedStatus": 200 },
|
|
"refresh": { "method": "POST", "path": "/api/v1/auth/refresh", "expectedStatus": 200 }
|
|
},
|
|
"clients": {
|
|
"list": { "method": "GET", "path": "/api/v1/clients", "expectedStatus": 200 },
|
|
"detail": { "method": "GET", "path": "/api/v1/clients/{id}", "expectedStatus": 200 },
|
|
"create": { "method": "POST", "path": "/api/v1/clients", "expectedStatus": [200, 201] },
|
|
"update": { "method": "PUT", "path": "/api/v1/clients/{id}", "expectedStatus": 200 },
|
|
"delete": { "method": "DELETE", "path": "/api/v1/clients/{id}", "expectedStatus": [200, 204] }
|
|
},
|
|
"approvals": {
|
|
"inbox": { "method": "GET", "path": "/api/v1/approvals/inbox", "expectedStatus": 200 },
|
|
"detail": { "method": "GET", "path": "/api/v1/approvals/{id}", "expectedStatus": 200 },
|
|
"approve": { "method": "POST", "path": "/api/v1/approvals/{id}/approve", "expectedStatus": 200 },
|
|
"reject": { "method": "POST", "path": "/api/v1/approvals/{id}/reject", "expectedStatus": 200 }
|
|
}
|
|
},
|
|
|
|
"reportTemplate": {
|
|
"apiSection": {
|
|
"title": "API 호출 검증 결과",
|
|
"fields": [
|
|
"총 API 호출 수",
|
|
"성공 호출",
|
|
"실패 호출",
|
|
"평균 응답 시간",
|
|
"느린 요청 (>2초)"
|
|
]
|
|
},
|
|
"errorSection": {
|
|
"title": "API 오류 상세",
|
|
"fields": [
|
|
"URL",
|
|
"Method",
|
|
"Status",
|
|
"응답 시간",
|
|
"오류 메시지"
|
|
]
|
|
}
|
|
},
|
|
|
|
"actionHooks": {
|
|
"beforeScenario": {
|
|
"description": "시나리오 시작 전 API 모니터링 초기화",
|
|
"actions": [
|
|
"setupApiMonitoring",
|
|
"clearApiLogs"
|
|
]
|
|
},
|
|
"afterStep": {
|
|
"description": "각 스텝 후 API 호출 확인",
|
|
"actions": [
|
|
"checkForApiErrors"
|
|
]
|
|
},
|
|
"afterScenario": {
|
|
"description": "시나리오 종료 후 API 요약 생성",
|
|
"actions": [
|
|
"getApiSummary",
|
|
"generateApiReport"
|
|
]
|
|
}
|
|
},
|
|
|
|
"errorHandling": {
|
|
"on500Error": {
|
|
"action": "captureAndReport",
|
|
"screenshot": true,
|
|
"logApiDetails": true,
|
|
"continueTest": false
|
|
},
|
|
"on401Error": {
|
|
"action": "reAuthenticate",
|
|
"maxRetries": 1,
|
|
"continueTest": true
|
|
},
|
|
"on404Error": {
|
|
"action": "logAndContinue",
|
|
"screenshot": true,
|
|
"continueTest": true
|
|
},
|
|
"onTimeout": {
|
|
"action": "retry",
|
|
"maxRetries": 2,
|
|
"continueTest": true
|
|
}
|
|
}
|
|
}
|