Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions src/__tests__/apiKeyScopes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ describe("API-key scopes", () => {
expect(res.status).toBe(400);
expectCanonicalError(
res.body,
res.headers["x-request-id"],
res.headers["x-request-id"] ?? "",
"invalid_request",
);
expect((res.body.message as string).toLowerCase()).toMatch(
Expand All @@ -179,7 +179,7 @@ describe("API-key scopes", () => {
expect(res.status).toBe(400);
expectCanonicalError(
res.body,
res.headers["x-request-id"],
res.headers["x-request-id"] ?? "",
"invalid_request",
);
});
Expand All @@ -189,7 +189,7 @@ describe("API-key scopes", () => {
expect(res.status).toBe(400);
expectCanonicalError(
res.body,
res.headers["x-request-id"],
res.headers["x-request-id"] ?? "",
"invalid_request",
);
});
Expand All @@ -202,7 +202,7 @@ describe("API-key scopes", () => {
expect(res.status).toBe(400);
expectCanonicalError(
res.body,
res.headers["x-request-id"],
res.headers["x-request-id"] ?? "",
"invalid_request",
);
});
Expand Down
12 changes: 6 additions & 6 deletions src/__tests__/apiKeys.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ describe("requireAdmin middleware — constant-time admin token check", () => {
process.env.ADMIN_TOKEN = "admin-secret-token-123";
const res = await request(app).get("/api/v1/admin/status");
expect(res.status).toBe(401);
expectCanonicalError(res.body, res.headers["x-request-id"], "unauthorized");
expectCanonicalError(res.body, res.headers["x-request-id"] ?? "", "unauthorized");
});

it("rejects requests with wrong admin token", async () => {
Expand All @@ -76,7 +76,7 @@ describe("requireAdmin middleware — constant-time admin token check", () => {
.get("/api/v1/admin/status")
.set("Authorization", "Bearer wrong-admin-token");
expect(res.status).toBe(401);
expectCanonicalError(res.body, res.headers["x-request-id"], "unauthorized");
expectCanonicalError(res.body, res.headers["x-request-id"] ?? "", "unauthorized");
});

it("accepts requests with correct admin token", async () => {
Expand All @@ -93,7 +93,7 @@ describe("requireAdmin middleware — constant-time admin token check", () => {
.get("/api/v1/admin/status")
.set("Authorization", "Bearer shortextra");
expect(res.status).toBe(401);
expectCanonicalError(res.body, res.headers["x-request-id"], "unauthorized");
expectCanonicalError(res.body, res.headers["x-request-id"] ?? "", "unauthorized");
});

it("rejects empty bearer token", async () => {
Expand Down Expand Up @@ -176,7 +176,7 @@ describe("api-keys lifecycle", () => {
expect(res.status).toBe(400);
expectCanonicalError(
res.body,
res.headers["x-request-id"],
res.headers["x-request-id"] ?? "",
"invalid_request",
);
});
Expand All @@ -188,7 +188,7 @@ describe("api-keys lifecycle", () => {
expect(res.status).toBe(400);
expectCanonicalError(
res.body,
res.headers["x-request-id"],
res.headers["x-request-id"] ?? "",
"invalid_request",
);
});
Expand Down Expand Up @@ -257,7 +257,7 @@ describe("api-keys lifecycle", () => {
it("returns 404 not_found for an unknown prefix", async () => {
const res = await request(app).delete("/api/v1/api-keys/deadbeef");
expect(res.status).toBe(404);
expectCanonicalError(res.body, res.headers["x-request-id"], "not_found");
expectCanonicalError(res.body, res.headers["x-request-id"] ?? "", "not_found");
});
});
});
Expand Down
28 changes: 17 additions & 11 deletions src/__tests__/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,9 @@ describe("config GET/PATCH", () => {
// ────────────────────────────────────────────────────────────────

it("PATCH bulkMaxItems persists on the next GET", async () => {
const next = original.bulkMaxItems + 7;
const bulkMaxItems = original.bulkMaxItems;
if (bulkMaxItems === undefined) throw new Error("bulkMaxItems missing from original config");
const next = bulkMaxItems + 7;
const patch = await request(app)
.patch("/api/v1/config")
.send({ bulkMaxItems: next });
Expand All @@ -78,7 +80,9 @@ describe("config GET/PATCH", () => {
});

it("PATCH rateLimitPerWindow persists on the next GET", async () => {
const next = original.rateLimitPerWindow + 13;
const rateLimitPerWindow = original.rateLimitPerWindow;
if (rateLimitPerWindow === undefined) throw new Error("rateLimitPerWindow missing from original config");
const next = rateLimitPerWindow + 13;
const patch = await request(app)
.patch("/api/v1/config")
.send({ rateLimitPerWindow: next });
Expand All @@ -90,7 +94,7 @@ describe("config GET/PATCH", () => {
});

it("PATCH rateLimitWindowMs persists on the next GET", async () => {
const next = original.rateLimitWindowMs + 5_000;
const next = (original.rateLimitWindowMs ?? 0) + 5_000;
const patch = await request(app)
.patch("/api/v1/config")
.send({ rateLimitWindowMs: next });
Expand Down Expand Up @@ -147,10 +151,10 @@ describe("config GET/PATCH", () => {
expect(patch.status).toBe(200);
expect(patch.body.config).toEqual(
expect.objectContaining({
rateLimitPerWindow: original.rateLimitPerWindow,
rateLimitWindowMs: original.rateLimitWindowMs,
bulkMaxItems: original.bulkMaxItems,
eventLogCap: original.eventLogCap,
rateLimitPerWindow: original.rateLimitPerWindow ?? 0,
rateLimitWindowMs: original.rateLimitWindowMs ?? 0,
bulkMaxItems: original.bulkMaxItems ?? 0,
eventLogCap: original.eventLogCap ?? 0,
}),
);
});
Expand Down Expand Up @@ -385,8 +389,8 @@ describe("config GET/PATCH", () => {
expect(res.status).toBe(200);
expect(eventLog.length).toBe(5);
// oldest-first eviction: remaining entries are the 5 newest
expect(eventLog[0].payload).toEqual({ i: 15 });
expect(eventLog[4].payload).toEqual({ i: 19 });
expect(eventLog.at(0)?.payload).toEqual({ i: 15 });
expect(eventLog.at(4)?.payload).toEqual({ i: 19 });
});

it("GET /api/v1/events limit clamp respects configured eventLogCap", async () => {
Expand Down Expand Up @@ -421,7 +425,9 @@ describe("config GET/PATCH", () => {
expect(res.status).toBe(200);
expect(eventLog.length).toBe(1);
// Only the newest entry survives
expect(eventLog[0].payload).toEqual({ i: 4 });
const firstEntry = eventLog.at(0);
expect(firstEntry).toBeDefined();
expect(firstEntry?.payload).toEqual({ i: 4 });
});

it("PATCH eventLogCap does not trim when buffer is already within new cap", async () => {
Expand Down Expand Up @@ -453,7 +459,7 @@ describe("config GET/PATCH", () => {
}
expect(eventLog.length).toBe(5);
// No trim needed; buffer is at cap
expect(eventLog[0].payload).toEqual({ i: 0 });
expect(eventLog.at(0)?.payload).toEqual({ i: 0 });
});

it("PATCH eventLogCap accepts EVENT_LOG_CAP_MAX (boundary)", async () => {
Expand Down
7 changes: 4 additions & 3 deletions src/__tests__/etag.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,14 +28,15 @@ describe("ETag / conditional GET on /api/v1/pairs", () => {

const second = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", etag);
.set("If-None-Match", etag ?? "");
expect(second.status).toBe(304);
expect(second.text).toBe("");
});

it("mutating the set changes the ETag so a stale If-None-Match yields 200", async () => {
const before = await request(app).get("/api/v1/pairs");
const staleEtag = before.headers.etag;
const staleEtagStr = typeof staleEtag === "string" ? staleEtag : "";

// Mutate the registry with a fresh, unique pair.
const reg = await request(app)
Expand All @@ -45,7 +46,7 @@ describe("ETag / conditional GET on /api/v1/pairs", () => {

const after = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", staleEtag);
.set("If-None-Match", staleEtagStr);
expect(after.status).toBe(200);
expect(after.headers.etag).toBeDefined();
expect(after.headers.etag).not.toBe(staleEtag);
Expand Down Expand Up @@ -107,7 +108,7 @@ describe("HEAD /api/v1/pairs — ETag without body", () => {

const res = await request(app)
.head("/api/v1/pairs")
.set("If-None-Match", etag);
.set("If-None-Match", etag ?? "");
expect(res.status).toBe(304);
// HEAD responses carry no body — supertest returns undefined for res.text
expect(res.body).toEqual({});
Expand Down
10 changes: 5 additions & 5 deletions src/__tests__/events.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,14 +211,14 @@ describe("GET /api/v1/events — filtering, limit, and capacity", () => {
});
}
expect(eventLog.length).toBe(EVENT_LOG_CAP);
expect(eventLog[0].id).toBe(sentinel);
expect(eventLog.at(0)?.id).toBe(sentinel);

// Push one more via recordEvent — should evict the sentinel
recordEvent("pair.unregistered", { source: "X", destination: "Y" });

expect(eventLog.length).toBe(EVENT_LOG_CAP);
expect(eventLog[0].id).not.toBe(sentinel);
expect(eventLog[eventLog.length - 1].type).toBe("pair.unregistered");
expect(eventLog.at(0)?.id).not.toBe(sentinel);
expect(eventLog.at(eventLog.length - 1)?.type).toBe("pair.unregistered");
});

it("does not evict entries when log is below EVENT_LOG_CAP", () => {
Expand All @@ -227,8 +227,8 @@ describe("GET /api/v1/events — filtering, limit, and capacity", () => {
recordEvent("pair.registered", { source: "C", destination: "D" });
expect(eventLog.length).toBe(2);
// Both entries still present
expect(eventLog[0].payload.source).toBe("A");
expect(eventLog[1].payload.source).toBe("C");
expect(eventLog.at(0)?.payload.source).toBe("A");
expect(eventLog.at(1)?.payload.source).toBe("C");
});

// ─── security: no sensitive payload fields ───────────────────────────────
Expand Down
7 changes: 4 additions & 3 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -920,7 +920,7 @@ describe("StableRoute Backend", () => {
expect(Number(match1![1])).toBe(config.rateLimitPerWindow);

// Change the config and verify the gauge updates
const previous = config.rateLimitPerWindow;
const previous = config.rateLimitPerWindow ?? 60;
config.rateLimitPerWindow = 120;

const res2 = await request(app).get("/api/v1/metrics");
Expand Down Expand Up @@ -2063,10 +2063,11 @@ describe("StableRoute Backend", () => {
const first = await request(app).get("/api/v1/pairs");
const etag = first.headers["etag"];
expect(etag).toBeTruthy();
const etagStr = typeof etag === "string" ? etag : "";

const second = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", etag);
.set("If-None-Match", etagStr);
expect(second.status).toBe(304);
});
});
Expand Down Expand Up @@ -2618,7 +2619,7 @@ describe("StableRoute Backend", () => {
const res304 = await request(app)
.get("/api/v1/pairs")
.query({ limit: 1 })
.set("If-None-Match", etag);
.set("If-None-Match", etag ?? "");
expect(res304.status).toBe(304);
});
});
Expand Down
12 changes: 6 additions & 6 deletions src/__tests__/pairsBulk.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -425,8 +425,8 @@ describe("POST /api/v1/pairs/bulk", () => {

const newEvents = eventLog.slice(eventsBefore);
expect(newEvents).toHaveLength(1);
expect(newEvents[0].type).toBe("pair.refreshed");
expect(newEvents[0].payload).toMatchObject({
expect(newEvents.at(0)?.type).toBe("pair.refreshed");
expect(newEvents.at(0)?.payload).toMatchObject({
source: "USDC",
destination: "EURC",
});
Expand All @@ -449,13 +449,13 @@ describe("POST /api/v1/pairs/bulk", () => {

const newEvents = eventLog.slice(eventsBefore);
expect(newEvents).toHaveLength(2);
expect(newEvents[0].type).toBe("pair.registered");
expect(newEvents[0].payload).toMatchObject({
expect(newEvents.at(0)?.type).toBe("pair.registered");
expect(newEvents.at(0)?.payload).toMatchObject({
source: "USDC",
destination: "EURC",
});
expect(newEvents[1].type).toBe("pair.registered");
expect(newEvents[1].payload).toMatchObject({
expect(newEvents.at(1)?.type).toBe("pair.registered");
expect(newEvents.at(1)?.payload).toMatchObject({
source: "XLM",
destination: "USDC",
});
Expand Down
12 changes: 6 additions & 6 deletions src/__tests__/pairsEtag.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ describe("GET /api/v1/pairs — ETag and 304 conditional request coverage", () =

const second = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", etag);
.set("If-None-Match", etag ?? "");
expect(second.status).toBe(304);
});

Expand All @@ -69,7 +69,7 @@ describe("GET /api/v1/pairs — ETag and 304 conditional request coverage", () =

const second = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", etag);
.set("If-None-Match", etag ?? "");
expect(second.status).toBe(304);
expect(second.text).toBe("");
});
Expand All @@ -80,7 +80,7 @@ describe("GET /api/v1/pairs — ETag and 304 conditional request coverage", () =

const second = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", etag);
.set("If-None-Match", etag ?? "");
expect(second.status).toBe(304);
expect(Object.keys(second.body).length).toBe(0);
});
Expand Down Expand Up @@ -121,7 +121,7 @@ describe("GET /api/v1/pairs — ETag and 304 conditional request coverage", () =
for (let i = 0; i < 3; i++) {
const cached = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", cachedEtag);
.set("If-None-Match", cachedEtag ?? "");
expect(cached.status).toBe(304);
}
});
Expand Down Expand Up @@ -155,7 +155,7 @@ describe("GET /api/v1/pairs — ETag and 304 conditional request coverage", () =

const after = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", staleEtag);
.set("If-None-Match", staleEtag ?? "");
expect(after.status).toBe(200);
expect(after.headers.etag).not.toBe(staleEtag);

Expand All @@ -180,7 +180,7 @@ describe("GET /api/v1/pairs — ETag and 304 conditional request coverage", () =

const res = await request(app)
.get("/api/v1/pairs")
.set("If-None-Match", oldEtag);
.set("If-None-Match", oldEtag ?? "");

// The status is either 304 (if ETag coincidentally matches) or 200 with a new ETag.
if (res.status === 200) {
Expand Down
Loading
Loading