rename web/ to admin/, along with the web-named build and cli identifiers
This commit is contained in:
@@ -0,0 +1,101 @@
|
||||
import type { LiveQueryEvent, QueryRow } from "@/lib/types";
|
||||
import { RING_CAPACITY, mergeGap, pushRow, type LiveRow } from "./ringBuffer";
|
||||
|
||||
function event(ts: number, domain: string, overrides: Partial<LiveQueryEvent> = {}): LiveQueryEvent {
|
||||
return {
|
||||
ts,
|
||||
domain,
|
||||
client_ip: "192.0.2.10",
|
||||
qtype: 1,
|
||||
blocked: false,
|
||||
block_reason: "",
|
||||
response_time_us: 500,
|
||||
cache_hit: false,
|
||||
upstream: "udp://9.9.9.9:53",
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
function liveRow(key: number, ts: number, domain: string, overrides: Partial<LiveQueryEvent> = {}): LiveRow {
|
||||
return { ...event(ts, domain, overrides), key };
|
||||
}
|
||||
|
||||
function fetchedRow(id: number, ts: number, domain: string, overrides: Partial<LiveQueryEvent> = {}): QueryRow {
|
||||
return { id, ...event(ts, domain, overrides) };
|
||||
}
|
||||
|
||||
function counter(start = 100): () => number {
|
||||
let n = start;
|
||||
return () => ++n;
|
||||
}
|
||||
|
||||
describe("pushRow", () => {
|
||||
test("prepends newest-first", () => {
|
||||
let rows: LiveRow[] = [];
|
||||
rows = pushRow(rows, liveRow(1, 10, "a.example"));
|
||||
rows = pushRow(rows, liveRow(2, 11, "b.example"));
|
||||
expect(rows.map((r) => r.domain)).toEqual(["b.example", "a.example"]);
|
||||
});
|
||||
|
||||
test("drops the oldest beyond capacity", () => {
|
||||
let rows: LiveRow[] = [];
|
||||
for (let i = 0; i < 5; i++) rows = pushRow(rows, liveRow(i, i, `d${i}.example`), 3);
|
||||
expect(rows).toHaveLength(3);
|
||||
expect(rows.map((r) => r.key)).toEqual([4, 3, 2]);
|
||||
});
|
||||
|
||||
test("default capacity is 500", () => {
|
||||
let rows: LiveRow[] = [];
|
||||
for (let i = 0; i < RING_CAPACITY + 10; i++) rows = pushRow(rows, liveRow(i, i, "x.example"));
|
||||
expect(rows).toHaveLength(RING_CAPACITY);
|
||||
});
|
||||
});
|
||||
|
||||
describe("mergeGap", () => {
|
||||
test("skips rows already in the buffer and counts only new ones", () => {
|
||||
const buffer = [liveRow(2, 100, "seen.example"), liveRow(1, 99, "old.example")];
|
||||
const fetched = [
|
||||
fetchedRow(30, 102, "gap2.example"),
|
||||
fetchedRow(29, 101, "gap1.example"),
|
||||
fetchedRow(28, 100, "seen.example"),
|
||||
];
|
||||
const { rows, missed } = mergeGap(buffer, fetched, counter());
|
||||
expect(missed).toBe(2);
|
||||
expect(rows.map((r) => r.domain)).toEqual(["gap2.example", "gap1.example", "seen.example", "old.example"]);
|
||||
});
|
||||
|
||||
test("no additions returns the buffer unchanged with missed 0", () => {
|
||||
const buffer = [liveRow(1, 100, "seen.example")];
|
||||
const { rows, missed } = mergeGap(buffer, [fetchedRow(5, 100, "seen.example")], counter());
|
||||
expect(missed).toBe(0);
|
||||
expect(rows).toBe(buffer);
|
||||
});
|
||||
|
||||
test("rows differing only in qtype are not deduplicated", () => {
|
||||
const buffer = [liveRow(1, 100, "dual.example", { qtype: 1 })];
|
||||
const fetched = [fetchedRow(5, 100, "dual.example", { qtype: 28 })];
|
||||
const { missed } = mergeGap(buffer, fetched, counter());
|
||||
expect(missed).toBe(1);
|
||||
});
|
||||
|
||||
test("assigns fresh keys from the counter and drops the id", () => {
|
||||
const { rows } = mergeGap([], [fetchedRow(77, 100, "gap.example")], counter(200));
|
||||
expect(rows[0]?.key).toBe(201);
|
||||
expect("id" in (rows[0] ?? {})).toBe(false);
|
||||
});
|
||||
|
||||
test("result is capped at capacity, keeping the newest", () => {
|
||||
const buffer = [liveRow(3, 300, "live.example")];
|
||||
const fetched = [fetchedRow(2, 302, "g2.example"), fetchedRow(1, 301, "g1.example")];
|
||||
const { rows, missed } = mergeGap(buffer, fetched, counter(), 2);
|
||||
expect(missed).toBe(2);
|
||||
expect(rows.map((r) => r.domain)).toEqual(["g2.example", "g1.example"]);
|
||||
});
|
||||
|
||||
test("merged rows stay sorted newest-first by ts", () => {
|
||||
const buffer = [liveRow(4, 105, "after-reopen.example"), liveRow(3, 100, "before.example")];
|
||||
const fetched = [fetchedRow(9, 103, "gap.example")];
|
||||
const { rows } = mergeGap(buffer, fetched, counter());
|
||||
expect(rows.map((r) => r.ts)).toEqual([105, 103, 100]);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user