test: add shared helper coverage

This commit is contained in:
Peter Steinberger 2026-03-13 20:21:43 +00:00
parent 35cf3d0ce5
commit 341d3e3493
4 changed files with 197 additions and 0 deletions

View File

@ -0,0 +1,16 @@
import { describe, expect, it } from "vitest";
import { normalizeDeviceAuthRole, normalizeDeviceAuthScopes } from "./device-auth.js";
describe("shared/device-auth", () => {
it("trims device auth roles without further rewriting", () => {
expect(normalizeDeviceAuthRole(" operator ")).toBe("operator");
expect(normalizeDeviceAuthRole("")).toBe("");
});
it("dedupes, trims, sorts, and filters auth scopes", () => {
expect(
normalizeDeviceAuthScopes([" node.invoke ", "operator.read", "", "node.invoke", "a.scope"]),
).toEqual(["a.scope", "node.invoke", "operator.read"]);
expect(normalizeDeviceAuthScopes(undefined)).toEqual([]);
});
});

View File

@ -0,0 +1,94 @@
import { describe, expect, it, vi } from "vitest";
import { resolveGatewayBindUrl } from "./gateway-bind-url.js";
describe("shared/gateway-bind-url", () => {
it("returns null for loopback/default binds", () => {
expect(
resolveGatewayBindUrl({
scheme: "ws",
port: 18789,
pickTailnetHost: () => "100.64.0.1",
pickLanHost: () => "192.168.1.2",
}),
).toBeNull();
});
it("resolves custom binds only when custom host is present after trimming", () => {
expect(
resolveGatewayBindUrl({
bind: "custom",
customBindHost: " gateway.local ",
scheme: "wss",
port: 443,
pickTailnetHost: vi.fn(),
pickLanHost: vi.fn(),
}),
).toEqual({
url: "wss://gateway.local:443",
source: "gateway.bind=custom",
});
expect(
resolveGatewayBindUrl({
bind: "custom",
customBindHost: " ",
scheme: "ws",
port: 18789,
pickTailnetHost: vi.fn(),
pickLanHost: vi.fn(),
}),
).toEqual({
error: "gateway.bind=custom requires gateway.customBindHost.",
});
});
it("resolves tailnet and lan binds or returns clear errors", () => {
expect(
resolveGatewayBindUrl({
bind: "tailnet",
scheme: "ws",
port: 18789,
pickTailnetHost: () => "100.64.0.1",
pickLanHost: vi.fn(),
}),
).toEqual({
url: "ws://100.64.0.1:18789",
source: "gateway.bind=tailnet",
});
expect(
resolveGatewayBindUrl({
bind: "tailnet",
scheme: "ws",
port: 18789,
pickTailnetHost: () => null,
pickLanHost: vi.fn(),
}),
).toEqual({
error: "gateway.bind=tailnet set, but no tailnet IP was found.",
});
expect(
resolveGatewayBindUrl({
bind: "lan",
scheme: "wss",
port: 8443,
pickTailnetHost: vi.fn(),
pickLanHost: () => "192.168.1.2",
}),
).toEqual({
url: "wss://192.168.1.2:8443",
source: "gateway.bind=lan",
});
expect(
resolveGatewayBindUrl({
bind: "lan",
scheme: "ws",
port: 18789,
pickTailnetHost: vi.fn(),
pickLanHost: () => null,
}),
).toEqual({
error: "gateway.bind=lan set, but no private LAN IP was found.",
});
});
});

View File

@ -0,0 +1,29 @@
import { afterEach, describe, expect, it } from "vitest";
import { resolveProcessScopedMap } from "./process-scoped-map.js";
const MAP_KEY = Symbol("process-scoped-map:test");
const OTHER_MAP_KEY = Symbol("process-scoped-map:other");
afterEach(() => {
delete (process as Record<PropertyKey, unknown>)[MAP_KEY];
delete (process as Record<PropertyKey, unknown>)[OTHER_MAP_KEY];
});
describe("shared/process-scoped-map", () => {
it("reuses the same map for the same symbol", () => {
const first = resolveProcessScopedMap<number>(MAP_KEY);
first.set("a", 1);
const second = resolveProcessScopedMap<number>(MAP_KEY);
expect(second).toBe(first);
expect(second.get("a")).toBe(1);
});
it("keeps distinct maps for distinct symbols", () => {
const first = resolveProcessScopedMap<number>(MAP_KEY);
const second = resolveProcessScopedMap<number>(OTHER_MAP_KEY);
expect(second).not.toBe(first);
});
});

View File

@ -0,0 +1,58 @@
import { describe, expect, it } from "vitest";
import {
formatDurationCompact,
formatTokenShort,
formatTokenUsageDisplay,
resolveIoTokens,
resolveTotalTokens,
truncateLine,
} from "./subagents-format.js";
describe("shared/subagents-format", () => {
it("formats compact durations across minute, hour, and day buckets", () => {
expect(formatDurationCompact()).toBe("n/a");
expect(formatDurationCompact(30_000)).toBe("1m");
expect(formatDurationCompact(61 * 60_000)).toBe("1h1m");
expect(formatDurationCompact(25 * 60 * 60_000)).toBe("1d1h");
});
it("formats token counts with integer, kilo, and million branches", () => {
expect(formatTokenShort()).toBeUndefined();
expect(formatTokenShort(999.9)).toBe("999");
expect(formatTokenShort(1_500)).toBe("1.5k");
expect(formatTokenShort(15_400)).toBe("15k");
expect(formatTokenShort(1_250_000)).toBe("1.3m");
});
it("truncates lines only when needed", () => {
expect(truncateLine("short", 10)).toBe("short");
expect(truncateLine("trim me ", 7)).toBe("trim me...");
});
it("resolves token totals and io breakdowns from valid numeric fields only", () => {
expect(resolveTotalTokens()).toBeUndefined();
expect(resolveTotalTokens({ totalTokens: 42 })).toBe(42);
expect(resolveTotalTokens({ inputTokens: 10, outputTokens: 5 })).toBe(15);
expect(resolveTotalTokens({ inputTokens: Number.NaN, outputTokens: 5 })).toBeUndefined();
expect(resolveIoTokens({ inputTokens: 10, outputTokens: 5 })).toEqual({
input: 10,
output: 5,
total: 15,
});
expect(resolveIoTokens({ inputTokens: Number.NaN, outputTokens: 0 })).toBeUndefined();
});
it("formats io and prompt-cache usage displays with fallback branches", () => {
expect(
formatTokenUsageDisplay({
inputTokens: 1_200,
outputTokens: 300,
totalTokens: 2_100,
}),
).toBe("tokens 1.5k (in 1.2k / out 300), prompt/cache 2.1k");
expect(formatTokenUsageDisplay({ totalTokens: 500 })).toBe("tokens 500 prompt/cache");
expect(formatTokenUsageDisplay({ inputTokens: 0, outputTokens: 0, totalTokens: 0 })).toBe("");
});
});