feat(relay): add policy overrides and idempotency-aware retry gating
This commit is contained in:
@@ -369,6 +369,152 @@ test("forwardGetData telemetry request id is stable across lifecycle events", as
|
||||
assert.strictEqual(new Set(relayRequestIds).size, 1);
|
||||
});
|
||||
|
||||
test("forwardGetData relayPolicy override controls retry behavior", async () => {
|
||||
let callCount = 0;
|
||||
const infoLogs = [];
|
||||
|
||||
const mod = loadRelayForwardingModule({
|
||||
axios: {
|
||||
get: async () => {
|
||||
callCount += 1;
|
||||
if (callCount < 3) {
|
||||
const error = new Error("temporary outage");
|
||||
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: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
const result = await mod.forwardGetData({
|
||||
queryUrl: "incidents?$top=1",
|
||||
maxRetries: 0,
|
||||
relayPolicy: {
|
||||
maxRetries: 2,
|
||||
retryBaseDelayMs: 0,
|
||||
retryMaxDelayMs: 0,
|
||||
method: "GET"
|
||||
}
|
||||
});
|
||||
|
||||
assert.strictEqual(callCount, 3);
|
||||
assert.deepStrictEqual(result.data, { ok: true });
|
||||
|
||||
const startEvent = infoLogs.find(
|
||||
(entry) => entry[0] === "relay_request_started"
|
||||
);
|
||||
assert.ok(startEvent);
|
||||
assert.strictEqual(startEvent[1].maxRetries, 2);
|
||||
assert.strictEqual(startEvent[1].method, "GET");
|
||||
});
|
||||
|
||||
test("forwardGetData blocks retries for non-idempotent methods by default", async () => {
|
||||
let callCount = 0;
|
||||
const infoLogs = [];
|
||||
|
||||
const mod = loadRelayForwardingModule({
|
||||
axios: {
|
||||
get: async () => {
|
||||
callCount += 1;
|
||||
const error = new Error("temporary outage");
|
||||
error.response = { status: 503 };
|
||||
throw error;
|
||||
}
|
||||
},
|
||||
getToken: async () => ({ access_token: "token" }),
|
||||
hashAPIPath: () => "&hash=abc",
|
||||
azureHeaders: () => ({ headers: {} }),
|
||||
redactSensitive: (value) => value,
|
||||
consoleLogger: () => {},
|
||||
console: {
|
||||
log: () => {},
|
||||
info: (...args) => infoLogs.push(args),
|
||||
warn: () => {},
|
||||
error: () => {}
|
||||
}
|
||||
});
|
||||
|
||||
let thrown = null;
|
||||
try {
|
||||
await mod.forwardGetData({
|
||||
queryUrl: "incidents?$top=1",
|
||||
relayPolicy: {
|
||||
method: "POST",
|
||||
maxRetries: 3,
|
||||
retryBaseDelayMs: 0,
|
||||
retryMaxDelayMs: 0
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
thrown = error;
|
||||
}
|
||||
|
||||
assert.ok(thrown);
|
||||
assert.strictEqual(callCount, 1);
|
||||
|
||||
const retryEvents = infoLogs.filter(
|
||||
(entry) => entry[0] === "relay_request_retrying"
|
||||
);
|
||||
assert.strictEqual(retryEvents.length, 0);
|
||||
|
||||
const failedEvent = infoLogs.find(
|
||||
(entry) => entry[0] === "relay_request_failed"
|
||||
);
|
||||
assert.ok(failedEvent);
|
||||
assert.strictEqual(failedEvent[1].method, "POST");
|
||||
});
|
||||
|
||||
test("forwardGetData can allow retries for non-idempotent methods when explicitly enabled", async () => {
|
||||
let callCount = 0;
|
||||
|
||||
const mod = loadRelayForwardingModule({
|
||||
axios: {
|
||||
get: async () => {
|
||||
callCount += 1;
|
||||
if (callCount === 1) {
|
||||
const error = new Error("temporary outage");
|
||||
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: () => {}
|
||||
});
|
||||
|
||||
const result = await mod.forwardGetData({
|
||||
queryUrl: "incidents?$top=1",
|
||||
relayPolicy: {
|
||||
method: "PATCH",
|
||||
maxRetries: 1,
|
||||
retryBaseDelayMs: 0,
|
||||
retryMaxDelayMs: 0,
|
||||
allowNonIdempotentRetries: true
|
||||
}
|
||||
});
|
||||
|
||||
assert.strictEqual(callCount, 2);
|
||||
assert.deepStrictEqual(result.data, { ok: true });
|
||||
});
|
||||
|
||||
const run = async () => {
|
||||
let passed = 0;
|
||||
for (const currentTest of tests) {
|
||||
|
||||
Reference in New Issue
Block a user