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"'), }); }