feat(relay): enrich request lifecycle telemetry for retries and outcomes
This commit is contained in:
@@ -43,6 +43,7 @@ const test = (name, fn) => tests.push({ name, fn });
|
||||
|
||||
test("forwardGetData retries retryable HTTP status and then succeeds", async () => {
|
||||
let callCount = 0;
|
||||
const infoLogs = [];
|
||||
|
||||
const mod = loadRelayForwardingModule({
|
||||
axios: {
|
||||
@@ -61,7 +62,13 @@ test("forwardGetData retries retryable HTTP status and then succeeds", async ()
|
||||
hashAPIPath: () => "&hash=abc",
|
||||
azureHeaders: () => ({ headers: { Authorization: "Bearer token" } }),
|
||||
redactSensitive: (value) => value,
|
||||
consoleLogger: () => {}
|
||||
consoleLogger: () => {},
|
||||
console: {
|
||||
log: () => {},
|
||||
info: (...args) => infoLogs.push(args),
|
||||
warn: () => {},
|
||||
error: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
const result = await mod.forwardGetData({
|
||||
@@ -74,10 +81,39 @@ test("forwardGetData retries retryable HTTP status and then succeeds", async ()
|
||||
assert.strictEqual(callCount, 2);
|
||||
assert.deepStrictEqual(result.data, { ok: true });
|
||||
assert.strictEqual(result.accessToken, "token");
|
||||
|
||||
const eventNames = infoLogs.map((entry) => entry[0]);
|
||||
assert.ok(eventNames.includes("relay_request_started"));
|
||||
assert.ok(eventNames.includes("relay_request_retrying"));
|
||||
assert.ok(eventNames.includes("relay_request_succeeded"));
|
||||
|
||||
const retryEvent = infoLogs.find(
|
||||
(entry) => entry[0] === "relay_request_retrying"
|
||||
);
|
||||
assert.ok(retryEvent);
|
||||
|
||||
const retryPayload = retryEvent[1];
|
||||
assert.strictEqual(retryPayload.statusClass, "5xx");
|
||||
assert.strictEqual(retryPayload.attemptsMade, 1);
|
||||
assert.strictEqual(retryPayload.retryCountUsed, 0);
|
||||
assert.strictEqual(retryPayload.remainingRetries, 1);
|
||||
assert.ok(typeof retryPayload.relayRequestId === "string");
|
||||
|
||||
const successEvent = infoLogs.find(
|
||||
(entry) => entry[0] === "relay_request_succeeded"
|
||||
);
|
||||
assert.ok(successEvent);
|
||||
|
||||
const successPayload = successEvent[1];
|
||||
assert.strictEqual(successPayload.statusClass, "none");
|
||||
assert.strictEqual(successPayload.attemptsMade, 2);
|
||||
assert.strictEqual(successPayload.retryCountUsed, 1);
|
||||
assert.ok(typeof successPayload.elapsedMs === "number");
|
||||
});
|
||||
|
||||
test("forwardGetData does not retry non-retryable HTTP status", async () => {
|
||||
let callCount = 0;
|
||||
const infoLogs = [];
|
||||
|
||||
const mod = loadRelayForwardingModule({
|
||||
axios: {
|
||||
@@ -92,7 +128,13 @@ test("forwardGetData does not retry non-retryable HTTP status", async () => {
|
||||
hashAPIPath: () => "&hash=abc",
|
||||
azureHeaders: () => ({ headers: {} }),
|
||||
redactSensitive: (value) => value,
|
||||
consoleLogger: () => {}
|
||||
consoleLogger: () => {},
|
||||
console: {
|
||||
log: () => {},
|
||||
info: (...args) => infoLogs.push(args),
|
||||
warn: () => {},
|
||||
error: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
let thrown = null;
|
||||
@@ -109,6 +151,17 @@ test("forwardGetData does not retry non-retryable HTTP status", async () => {
|
||||
|
||||
assert.ok(thrown);
|
||||
assert.strictEqual(callCount, 1);
|
||||
|
||||
const failedEvent = infoLogs.find(
|
||||
(entry) => entry[0] === "relay_request_failed"
|
||||
);
|
||||
assert.ok(failedEvent);
|
||||
|
||||
const failedPayload = failedEvent[1];
|
||||
assert.strictEqual(failedPayload.statusClass, "4xx");
|
||||
assert.strictEqual(failedPayload.attemptsMade, 1);
|
||||
assert.strictEqual(failedPayload.retryCountUsed, 0);
|
||||
assert.ok(typeof failedPayload.elapsedMs === "number");
|
||||
});
|
||||
|
||||
test("forwardGetData does not retry unauthorized status", async () => {
|
||||
@@ -258,6 +311,64 @@ test("forwardGetData applies timeout and appendHash=false behavior", async () =>
|
||||
assert.strictEqual(capturedOptions.headers.Accept, "application/json");
|
||||
});
|
||||
|
||||
test("forwardGetData telemetry request id is stable across lifecycle events", async () => {
|
||||
const infoLogs = [];
|
||||
let callCount = 0;
|
||||
|
||||
const mod = loadRelayForwardingModule({
|
||||
axios: {
|
||||
get: async () => {
|
||||
callCount += 1;
|
||||
if (callCount === 1) {
|
||||
const error = new Error("service unavailable");
|
||||
error.response = { status: 503 };
|
||||
throw error;
|
||||
}
|
||||
|
||||
return { data: { ok: true }, status: 200 };
|
||||
}
|
||||
},
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=abc",
|
||||
azureHeaders: () => ({ headers: {} }),
|
||||
redactSensitive: (value) => value,
|
||||
consoleLogger: () => {},
|
||||
console: {
|
||||
log: () => {},
|
||||
info: (...args) => infoLogs.push(args),
|
||||
warn: () => {},
|
||||
error: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
await mod.forwardGetData({
|
||||
queryUrl: "incidents?$top=1",
|
||||
maxRetries: 1,
|
||||
retryBaseDelayMs: 0,
|
||||
retryMaxDelayMs: 0
|
||||
});
|
||||
|
||||
const lifecycleEvents = infoLogs
|
||||
.filter((entry) =>
|
||||
[
|
||||
"relay_request_started",
|
||||
"relay_request_retrying",
|
||||
"relay_request_succeeded"
|
||||
].includes(entry[0])
|
||||
)
|
||||
.map((entry) => entry[1]);
|
||||
|
||||
assert.strictEqual(lifecycleEvents.length, 3);
|
||||
|
||||
const relayRequestIds = lifecycleEvents.map(
|
||||
(event) => event.relayRequestId
|
||||
);
|
||||
assert.ok(
|
||||
relayRequestIds.every((id) => typeof id === "string" && id.length > 0)
|
||||
);
|
||||
assert.strictEqual(new Set(relayRequestIds).size, 1);
|
||||
});
|
||||
|
||||
const run = async () => {
|
||||
let passed = 0;
|
||||
for (const currentTest of tests) {
|
||||
|
||||
Reference in New Issue
Block a user