chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,74 @@
|
||||
import http from "k6/http";
|
||||
import crypto from "k6/crypto";
|
||||
import exec from "k6/execution";
|
||||
import { check } from "k6";
|
||||
|
||||
const baseURL = __ENV.CALLBACK_URL || "http://host.docker.internal:58081";
|
||||
const secret = __ENV.PROVIDER_CALLBACK_SECRET;
|
||||
const fixtureText = __ENV.PROVIDER_FIXTURES || `${__ENV.CONVERSATION_ID || ""}|${__ENV.PROVIDER_TASK_REF || ""}`;
|
||||
const fixtures = fixtureText.split(",").filter(Boolean).map((item) => {
|
||||
const [conversationID, taskRef] = item.split("|");
|
||||
return { conversationID, taskRef };
|
||||
});
|
||||
const smoke = (__ENV.PROFILE || "acceptance").toLowerCase() === "smoke";
|
||||
const targetRate = Number(__ENV.RATE || (smoke ? 25 : 500));
|
||||
const duration = __ENV.DURATION || (smoke ? "10s" : "10m");
|
||||
const duplicateWindow = Number(__ENV.DUPLICATE_WINDOW || (smoke ? 100 : 300000));
|
||||
const occurredAt = __ENV.OCCURRED_AT || new Date().toISOString();
|
||||
|
||||
if (!secret || !fixtures.length || fixtures.some((item) => !item.conversationID || !item.taskRef)) {
|
||||
throw new Error("PROVIDER_CALLBACK_SECRET and valid PROVIDER_FIXTURES are required");
|
||||
}
|
||||
|
||||
export const options = {
|
||||
scenarios: {
|
||||
provider_notifications: {
|
||||
executor: "constant-arrival-rate",
|
||||
rate: targetRate,
|
||||
timeUnit: "1s",
|
||||
duration,
|
||||
preAllocatedVUs: smoke ? 50 : 600,
|
||||
maxVUs: smoke ? 150 : 1500,
|
||||
gracefulStop: "30s",
|
||||
},
|
||||
},
|
||||
thresholds: {
|
||||
http_req_failed: [smoke ? "rate<0.01" : "rate<0.001"],
|
||||
http_req_duration: ["p(95)<200", "p(99)<1000"],
|
||||
checks: ["rate>0.999"],
|
||||
},
|
||||
};
|
||||
|
||||
function base64URL(value) {
|
||||
return value.replace(/=/g, "").replace(/\+/g, "-").replace(/\//g, "_");
|
||||
}
|
||||
|
||||
export default function () {
|
||||
const eventNumber = exec.scenario.iterationInTest % duplicateWindow;
|
||||
const fixture = fixtures[eventNumber % fixtures.length];
|
||||
const body = JSON.stringify({
|
||||
provider: "mock",
|
||||
event_id: `load-started-${eventNumber}`,
|
||||
task_ref: fixture.taskRef,
|
||||
conversation_id: fixture.conversationID,
|
||||
event_type: "voice_chat.started",
|
||||
status: "ACTIVE",
|
||||
occurred_at: occurredAt,
|
||||
});
|
||||
const timestamp = new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
||||
const digest = crypto.sha256(body, "hex");
|
||||
const signature = base64URL(crypto.hmac("sha256", secret, `${timestamp}\n${digest}`, "base64"));
|
||||
const response = http.post(`${baseURL}/callbacks/v1/vertc/voice-chat`, body, {
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
"X-Provider-Timestamp": timestamp,
|
||||
"X-Provider-Signature": signature,
|
||||
},
|
||||
tags: { operation: "provider_callback" },
|
||||
timeout: "3s",
|
||||
});
|
||||
check(response, {
|
||||
"callback acknowledged": (item) => item.status === 200,
|
||||
"callback response is correlated": (item) => item.status === 200 && item.body.includes('"request_id"'),
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user