160 lines
5.2 KiB
TypeScript
160 lines
5.2 KiB
TypeScript
import { describe, expect, it, beforeAll, afterAll, vi } from "vitest";
|
|
import IORedis from "ioredis";
|
|
import { pushOpsSummaryToEoc } from "../src/processors/eoc-push.js";
|
|
|
|
const redis = new IORedis(process.env.REDIS_URL ?? "redis://localhost:6379", {
|
|
maxRetriesPerRequest: null,
|
|
});
|
|
|
|
describe("pushOpsSummaryToEoc", () => {
|
|
beforeAll(async () => {
|
|
await redis.set(
|
|
"ops:summary:cache",
|
|
JSON.stringify({ app: { app: "cibello", generated_at: new Date().toISOString() }, as_of: new Date().toISOString() }),
|
|
"EX",
|
|
60,
|
|
);
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await redis.del("ops:summary:cache");
|
|
await redis.quit();
|
|
});
|
|
|
|
it("maps feedback.created_at to skapad in the POST body", async () => {
|
|
process.env.EOC_URL = "https://eoc.example.com";
|
|
process.env.EOC_PUSH_TOKEN = "tok";
|
|
const ts = "2026-08-11T10:00:00.000Z";
|
|
await redis.set(
|
|
"ops:summary:cache",
|
|
JSON.stringify({
|
|
as_of: ts,
|
|
app: { app: "cibello" },
|
|
feedback: {
|
|
oppna: 1,
|
|
senaste: [{ typ: "bug", rubrik: "exempel", status: "oppen", created_at: ts }],
|
|
},
|
|
}),
|
|
"EX",
|
|
60,
|
|
);
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
const fetchMock = vi.fn().mockResolvedValue(new Response(null, { status: 204 }));
|
|
globalThis.fetch = fetchMock as unknown as typeof fetch;
|
|
|
|
try {
|
|
const result = await pushOpsSummaryToEoc(redis);
|
|
expect(result.ok).toBe(true);
|
|
expect(fetchMock).toHaveBeenCalledTimes(1);
|
|
|
|
const init = fetchMock.mock.calls[0][1] as RequestInit;
|
|
const body = JSON.parse(init.body as string);
|
|
expect(body.feedback.senaste[0].skapad).toBe(ts);
|
|
expect(body.feedback.senaste[0]).not.toHaveProperty("created_at");
|
|
expect(body.as_of).toBe(ts);
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
it("returns disabled result when EOC_URL is missing", async () => {
|
|
delete process.env.EOC_URL;
|
|
process.env.EOC_PUSH_TOKEN = "tok";
|
|
const result = await pushOpsSummaryToEoc(redis);
|
|
expect(result.ok).toBe(false);
|
|
expect(result.error).toMatch(/EOC_URL or EOC_PUSH_TOKEN not configured/);
|
|
});
|
|
|
|
it("returns disabled result when EOC_PUSH_TOKEN is missing", async () => {
|
|
process.env.EOC_URL = "https://eoc.example.com";
|
|
delete process.env.EOC_PUSH_TOKEN;
|
|
const result = await pushOpsSummaryToEoc(redis);
|
|
expect(result.ok).toBe(false);
|
|
expect(result.error).toMatch(/EOC_URL or EOC_PUSH_TOKEN not configured/);
|
|
});
|
|
|
|
it("returns cache-miss when cache is empty", async () => {
|
|
process.env.EOC_URL = "https://eoc.example.com";
|
|
process.env.EOC_PUSH_TOKEN = "tok";
|
|
await redis.del("ops:summary:cache");
|
|
const result = await pushOpsSummaryToEoc(redis);
|
|
expect(result.ok).toBe(false);
|
|
expect(result.error).toMatch(/cache miss/);
|
|
});
|
|
|
|
it("transforms wall titles and tones for EOC", async () => {
|
|
process.env.EOC_URL = "https://eoc.example.com";
|
|
process.env.EOC_PUSH_TOKEN = "tok";
|
|
await redis.set(
|
|
"ops:summary:cache",
|
|
JSON.stringify({
|
|
as_of: new Date().toISOString(),
|
|
app: { app: "cibello" },
|
|
jobb: { workers_ok: false },
|
|
sakerhet: { allergen_invariant_brott: 1 },
|
|
wall: {
|
|
boards: [
|
|
{
|
|
title: "{brand} Overview",
|
|
tiles: [
|
|
{ label: "MRR", value: "0 kr", tone: "ok" },
|
|
{ label: "Workers", value: "NERE", tone: "warn" },
|
|
{ label: "Allergen-brott", value: "1", tone: "warn" },
|
|
],
|
|
},
|
|
],
|
|
},
|
|
}),
|
|
"EX",
|
|
60,
|
|
);
|
|
|
|
const originalFetch = globalThis.fetch;
|
|
const fetchMock = vi.fn().mockResolvedValue(new Response(null, { status: 204 }));
|
|
globalThis.fetch = fetchMock as unknown as typeof fetch;
|
|
|
|
try {
|
|
const result = await pushOpsSummaryToEoc(redis);
|
|
expect(result.ok).toBe(true);
|
|
|
|
const init = fetchMock.mock.calls[0][1] as RequestInit;
|
|
const body = JSON.parse(init.body as string);
|
|
const board = body.wall.boards[0];
|
|
expect(board.title).toBe("Cibello Overview");
|
|
expect(board.title).not.toMatch(/\{brand\}/);
|
|
|
|
const tones = board.tiles.map((t: { tone?: string }) => t.tone);
|
|
for (const tone of tones) {
|
|
expect(["good", "warn", "crit", ""]).toContain(tone);
|
|
}
|
|
expect(board.tiles.find((t: { label: string }) => t.label === "MRR")?.tone).toBe("good");
|
|
expect(board.tiles.find((t: { label: string }) => t.label === "Workers")?.tone).toBe("crit");
|
|
expect(board.tiles.find((t: { label: string }) => t.label === "Allergen-brott")?.tone).toBe("crit");
|
|
} finally {
|
|
globalThis.fetch = originalFetch;
|
|
}
|
|
});
|
|
|
|
it("does not throw when payload mapping fails", async () => {
|
|
process.env.EOC_URL = "https://eoc.example.com";
|
|
process.env.EOC_PUSH_TOKEN = "tok";
|
|
await redis.set(
|
|
"ops:summary:cache",
|
|
JSON.stringify({
|
|
as_of: new Date().toISOString(),
|
|
app: { app: "cibello" },
|
|
wall: { boards: [{ title: 123, tiles: "nej" }] },
|
|
}),
|
|
"EX",
|
|
60,
|
|
);
|
|
|
|
await expect(pushOpsSummaryToEoc(redis)).resolves.toEqual({
|
|
ok: false,
|
|
status: null,
|
|
error: expect.stringMatching(/payload mapping failed/),
|
|
});
|
|
});
|
|
});
|