Files
2026-08-02 20:14:53 +01:00

101 lines
3.1 KiB
TypeScript

import { describe, it, expect, beforeEach } from "vitest";
import {
DEFAULT_CAPACITY,
MAX_ENTRY_BYTES,
RingBuffer,
_resetForTesting,
clearLogs,
getLogBuffer,
snapshotLogs,
} from "./buffer";
describe("frontend log buffer", () => {
beforeEach(() => {
_resetForTesting();
});
it("appends entries in order", () => {
const buf = getLogBuffer();
buf.push({ timestamp: "t1", level: "info", source: "console", message: "a" });
buf.push({ timestamp: "t2", level: "warn", source: "console", message: "b" });
expect(snapshotLogs().map((e) => e.message)).toEqual(["a", "b"]);
});
it("evicts oldest when capacity is exceeded", () => {
const buf = getLogBuffer();
for (let i = 0; i < DEFAULT_CAPACITY + 5; i++) {
buf.push({ timestamp: String(i), level: "info", source: "console", message: `m${i}` });
}
const snap = snapshotLogs();
expect(snap).toHaveLength(DEFAULT_CAPACITY);
expect(snap[0].message).toBe(`m5`);
expect(snap[snap.length - 1].message).toBe(`m${DEFAULT_CAPACITY + 4}`);
});
it("snapshot is isolated from the live buffer", () => {
const buf = getLogBuffer();
buf.push({ timestamp: "t", level: "info", source: "console", message: "x" });
const snap = snapshotLogs();
snap[0].message = "mutated";
expect(snapshotLogs()[0].message).toBe("x");
});
it("snapshot deep-copies the args array", () => {
const buf = getLogBuffer();
buf.push({
timestamp: "t",
level: "info",
source: "console",
message: "x",
args: ["a", "b"],
});
const snap = snapshotLogs();
snap[0].args!.push("mutated");
expect(snapshotLogs()[0].args).toEqual(["a", "b"]);
});
it("clearLogs empties the buffer", () => {
const buf = getLogBuffer();
buf.push({ timestamp: "t", level: "info", source: "console", message: "x" });
clearLogs();
expect(snapshotLogs()).toHaveLength(0);
});
it("partitions snapshots by authenticated identity", () => {
const buf = getLogBuffer();
buf.push({
timestamp: "t1",
level: "info",
source: "console",
message: "alice",
identity_scope: "alice",
});
buf.push({
timestamp: "t2",
level: "info",
source: "console",
message: "bob",
identity_scope: "bob",
});
expect(snapshotLogs("alice").map((entry) => entry.message)).toEqual(["alice"]);
});
it("rejects an entry over 64 KiB and sheds low-priority entries first", () => {
const buf = new RingBuffer(2, MAX_ENTRY_BYTES * 2);
expect(
buf.push({
timestamp: "t",
level: "info",
source: "console",
message: "x".repeat(MAX_ENTRY_BYTES),
}),
).toBe(false);
buf.push({ timestamp: "1", level: "warn", source: "console", message: "warn" });
buf.push({ timestamp: "2", level: "debug", source: "console", message: "debug" });
buf.push({ timestamp: "3", level: "error", source: "console", message: "error" });
expect(buf.snapshot().map((entry) => entry.message)).toEqual(["warn", "error"]);
expect(buf.statistics()).toEqual({ capacity: 1, entry_too_large: 1 });
});
});