Files
jlinc-server/backend/http/dashboardPassthrough.test.js
2026-08-20 15:08:32 +00:00

98 lines
4.9 KiB
JavaScript

import { jest } from "@jest/globals";
import { makePassthrough } from "./dashboardPassthrough.js";
const LOCAL = { core: { url: null, key: null }, audit: { url: null, key: null } };
const FED = { core: { url: "http://core", key: "ck" }, audit: { url: "http://audit", key: "ak" } };
const localService = () => ({
summary: jest.fn(async () => ({ apiUsage: { core: 2, audit: 1, total: 3 }, apiKeys: 4, endpoints: 2, storage: {} })),
transactions: jest.fn(async () => ({ rows: [], total: 0 })),
series: jest.fn(async () => ({ usage: [], storage: [] })),
verify: jest.fn(async () => ({ results: [] })),
event: jest.fn(async () => ({ eventId: "x" })),
});
describe("passthrough — local mode (no remote sources)", () => {
it("serves every endpoint from the local service and never fetches", async () => {
const service = localService();
const fetchImpl = jest.fn();
const p = makePassthrough({ service, config: { dashboard: LOCAL }, fetchImpl });
expect(await p.summary(7, { from: "a", to: "b" })).toMatchObject({ apiKeys: 4 });
await p.transactions(7, { type: "audit" });
await p.series(7, {});
expect(service.summary).toHaveBeenCalled();
expect(service.transactions).toHaveBeenCalled();
expect(fetchImpl).not.toHaveBeenCalled();
});
it("scopes every local call to the caller's own user id (per-tenant isolation)", async () => {
const service = localService();
const p = makePassthrough({ service, config: { dashboard: LOCAL }, fetchImpl: jest.fn() });
await p.summary(7, { from: "a", to: "b" });
await p.transactions(9, { type: "core" });
await p.event(9, "uuid-1");
// The passthrough never substitutes another user's id — each call carries the
// id it was handed (which the route sets from req.user.id), so user 7 can
// never be served user 9's rows and vice versa.
expect(service.summary).toHaveBeenCalledWith(7, { from: "a", to: "b" });
expect(service.transactions.mock.calls[0][0]).toBe(9);
expect(service.event).toHaveBeenCalledWith(9, "uuid-1");
});
});
describe("passthrough — federated mode", () => {
const fetchImpl = jest.fn(async (url) => ({
ok: true,
json: async () =>
url.includes("//core")
? { apiUsage: { core: 10, audit: 0, total: 10 }, apiKeys: 5, endpoints: 2, storage: { onDisk: { core: 100, audit: 0, total: 100 }, logical: { core: 100, audit: 0, total: 100 } } }
: { apiUsage: { core: 0, audit: 7, total: 7 }, apiKeys: 0, endpoints: 0, storage: { onDisk: { core: 0, audit: 50, total: 50 }, logical: { core: 0, audit: 50, total: 50 } } },
}));
beforeEach(() => fetchImpl.mockClear());
it("merges summary: core fields from core source, audit fields from audit source", async () => {
const p = makePassthrough({ service: {}, config: { dashboard: FED }, fetchImpl });
const out = await p.summary(7, { from: "a", to: "b" });
expect(out.apiUsage).toEqual({ core: 10, audit: 7, total: 17 });
expect(out.storage.onDisk).toEqual({ core: 100, audit: 50, total: 150 });
expect(out.apiKeys).toBe(5); // from the core source
expect(fetchImpl).toHaveBeenCalledTimes(2);
});
it("forwards with the source's API key and NO user id in the body (the key is the tenant scope)", async () => {
const p = makePassthrough({ service: {}, config: { dashboard: FED }, fetchImpl });
await p.summary(7, { from: "a", to: "b" });
const coreCall = fetchImpl.mock.calls.find(([u]) => u.includes("//core"));
expect(coreCall[0]).toBe("http://core/api/v1/data/dashboard/summary");
expect(coreCall[1].headers.Authorization).toBe("Bearer ck");
const body = JSON.parse(coreCall[1].body);
expect(body).toEqual({ from: "a", to: "b" }); // only the window — never a userId
expect(body).not.toHaveProperty("userId");
});
it("routes the audit transactions tab to the audit source", async () => {
const p = makePassthrough({ service: {}, config: { dashboard: FED }, fetchImpl });
await p.transactions(7, { type: "audit", limit: 50, offset: 0 });
const [url] = fetchImpl.mock.calls[0];
expect(url).toBe("http://audit/api/v1/data/dashboard/transactions");
});
it("merges the combined usage+storage series by date", async () => {
const seriesFetch = jest.fn(async (url) => ({
ok: true,
json: async () =>
url.includes("//core")
? { usage: [{ date: "2026-06-01", core: 3, audit: 0, total: 3 }], storage: [{ date: "2026-06-01", coreDisk: 100, auditDisk: 0, totalDisk: 100 }] }
: { usage: [{ date: "2026-06-01", core: 0, audit: 5, total: 5 }], storage: [{ date: "2026-06-01", coreDisk: 0, auditDisk: 50, totalDisk: 50 }] },
}));
const p = makePassthrough({ service: {}, config: { dashboard: FED }, fetchImpl: seriesFetch });
const out = await p.series(7, { from: "a", to: "b" });
expect(out.usage).toEqual([{ date: "2026-06-01", core: 3, audit: 5, total: 8 }]);
expect(out.storage).toEqual([{ date: "2026-06-01", coreDisk: 100, auditDisk: 50, totalDisk: 150 }]);
});
});