121 lines
4.0 KiB
JavaScript
121 lines
4.0 KiB
JavaScript
|
|
// HTTP helpers — every request goes through these so checks/envelope handling
|
||
|
|
// is consistent across smoke and journey scripts.
|
||
|
|
import http from 'k6/http';
|
||
|
|
import { check, fail } from 'k6';
|
||
|
|
import { cfg, SUCCESS_CODE } from './config.js';
|
||
|
|
|
||
|
|
const JSON_HEADERS = { 'Content-Type': 'application/json', Accept: 'application/json' };
|
||
|
|
|
||
|
|
function url(path) {
|
||
|
|
if (path.startsWith('http://') || path.startsWith('https://')) return path;
|
||
|
|
return cfg.baseUrl + path;
|
||
|
|
}
|
||
|
|
|
||
|
|
function mergeHeaders(extra) {
|
||
|
|
return Object.assign({}, JSON_HEADERS, extra || {});
|
||
|
|
}
|
||
|
|
|
||
|
|
export function withBearer(token, extra) {
|
||
|
|
return mergeHeaders(Object.assign({ Authorization: `Bearer ${token}` }, extra || {}));
|
||
|
|
}
|
||
|
|
|
||
|
|
export function get(path, headers, params) {
|
||
|
|
return http.get(url(path), Object.assign({ headers: mergeHeaders(headers) }, params || {}));
|
||
|
|
}
|
||
|
|
|
||
|
|
export function post(path, body, headers, params) {
|
||
|
|
return http.post(
|
||
|
|
url(path),
|
||
|
|
body == null ? null : JSON.stringify(body),
|
||
|
|
Object.assign({ headers: mergeHeaders(headers) }, params || {}),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function put(path, body, headers, params) {
|
||
|
|
return http.put(
|
||
|
|
url(path),
|
||
|
|
body == null ? null : JSON.stringify(body),
|
||
|
|
Object.assign({ headers: mergeHeaders(headers) }, params || {}),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function patch(path, body, headers, params) {
|
||
|
|
return http.patch(
|
||
|
|
url(path),
|
||
|
|
body == null ? null : JSON.stringify(body),
|
||
|
|
Object.assign({ headers: mergeHeaders(headers) }, params || {}),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
export function del(path, body, headers, params) {
|
||
|
|
return http.del(
|
||
|
|
url(path),
|
||
|
|
body == null ? null : JSON.stringify(body),
|
||
|
|
Object.assign({ headers: mergeHeaders(headers) }, params || {}),
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
// safeJson parses res.body; returns null when body is empty/not JSON.
|
||
|
|
export function safeJson(res) {
|
||
|
|
if (!res || !res.body || res.body.length === 0) return null;
|
||
|
|
try {
|
||
|
|
return JSON.parse(res.body);
|
||
|
|
} catch (_) {
|
||
|
|
return null;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// checkEnvelope verifies the standard CloudEP success envelope.
|
||
|
|
// { code: 102000, message: "SUCCESS", data: ... }
|
||
|
|
// Returns the parsed body so callers can keep chaining.
|
||
|
|
export function checkEnvelope(res, label, expectedStatus = 200, expectedCode = SUCCESS_CODE) {
|
||
|
|
const body = safeJson(res);
|
||
|
|
const ok = check(res, {
|
||
|
|
[`${label}: status ${expectedStatus}`]: (r) => r.status === expectedStatus,
|
||
|
|
[`${label}: code ${expectedCode}`]: () => body && body.code === expectedCode,
|
||
|
|
});
|
||
|
|
if (!ok) {
|
||
|
|
// surface real payload so failures are actionable in `make k6-*` output
|
||
|
|
fail(`${label} failed: status=${res.status} body=${res.body}`);
|
||
|
|
}
|
||
|
|
return body;
|
||
|
|
}
|
||
|
|
|
||
|
|
// checkError expects a non-2xx response and a business error code.
|
||
|
|
// expectedBiz is the 8-digit numeric SSCCCDDD code.
|
||
|
|
export function checkError(res, label, expectedStatus, expectedBiz) {
|
||
|
|
const body = safeJson(res);
|
||
|
|
const ok = check(res, {
|
||
|
|
[`${label}: status ${expectedStatus}`]: (r) => r.status === expectedStatus,
|
||
|
|
[`${label}: code ${expectedBiz}`]: () => body && body.code === expectedBiz,
|
||
|
|
});
|
||
|
|
if (!ok) {
|
||
|
|
fail(`${label} expected error ${expectedBiz} got status=${res.status} body=${res.body}`);
|
||
|
|
}
|
||
|
|
return body;
|
||
|
|
}
|
||
|
|
|
||
|
|
// checkErrorOneOf accepts any of several (status, code) pairs. Use for
|
||
|
|
// endpoints whose error path depends on environment wiring (e.g.
|
||
|
|
// /auth/login may legitimately return either:
|
||
|
|
// 401 + 28501000 (invalid credentials, OAuth wired)
|
||
|
|
// 502 + 28802000 (zitadel request failed, OAuth not wired or password
|
||
|
|
// grant not enabled — true of modern ZITADEL v2 by default)
|
||
|
|
// pairs: array of [status, bizCode] tuples.
|
||
|
|
export function checkErrorOneOf(res, label, pairs) {
|
||
|
|
const body = safeJson(res);
|
||
|
|
const matched = pairs.find(
|
||
|
|
([s, c]) => res.status === s && body && body.code === c,
|
||
|
|
);
|
||
|
|
const labelStr = pairs.map(([s, c]) => `${s}+${c}`).join(' | ');
|
||
|
|
const ok = check(res, {
|
||
|
|
[`${label}: one of {${labelStr}}`]: () => Boolean(matched),
|
||
|
|
});
|
||
|
|
if (!ok) {
|
||
|
|
fail(
|
||
|
|
`${label} expected one of [${labelStr}] got status=${res.status} body=${res.body}`,
|
||
|
|
);
|
||
|
|
}
|
||
|
|
return body;
|
||
|
|
}
|