import { describe, expect, test } from "bun:test"; import { JobMasks, splitLogChunk } from "./job-masks"; import type { MaskState } from "./job-masks"; function storage(initial: string[] | null) { const data = new Map(initial ? [["masks", initial]] : []); const fake = { get: async (key: string) => data.get(key), put: async (key: string, value: unknown) => void data.set(key, value), }; return Object.assign(Object.create(null), fake); } describe("JobMasks", () => { test("hides every line until the list loads, and keeps add-mask values seen meanwhile", async () => { const masks = new JobMasks(storage(null)); const state: MaskState = {}; await masks.ensure(async () => null, state); await masks.add(state, "runtime-token"); expect(masks.apply("value runtime-token")).toBe("[masking unavailable; line hidden]"); expect(state.pendingMasks).toEqual(["runtime-token"]); await masks.ensure(async () => ["job-secret"], state); expect(masks.apply("job-secret and runtime-token")).toBe("*** and ***"); expect(state.pendingMasks).toEqual([]); }); test("added masks extend the stored list instead of replacing it", async () => { const masks = new JobMasks(storage(["stored-secret"])); const state: MaskState = {}; await masks.ensure(async () => null, state); await masks.add(state, "added-value"); expect(masks.apply("stored-secret added-value")).toBe("*** ***"); }); test("a line split across self-hosted log chunks is masked as one", async () => { const masks = new JobMasks(storage(["chunked-secret"])); await masks.ensure(async () => null, {}); const first = splitLogChunk("", "ok\nvalue chunked-"); expect(first.lines).toEqual(["ok"]); const second = splitLogChunk(first.carry, "secret done\n"); expect(second.lines.map((line) => masks.apply(line))).toEqual(["value *** done"]); expect(second.carry).toBe(""); }); test("base64 and percent-encoded forms of a secret are masked at any offset", async () => { const secret = "s3cr3t/value+with=chars"; const masks = new JobMasks(storage(null)); await masks.ensure(async () => [secret], {}); for (const prefix of ["", "a", "ab", "abc"]) { const encoded = btoa(`${prefix}${secret}trailing`); expect(masks.apply(encoded)).toContain("***"); } expect(masks.apply(`token=${encodeURIComponent(secret)}`)).toBe("token=***"); }); });