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<string, unknown>(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");