summaryrefslogtreecommitdiffhomepage
path: root/packages/core/tests/credentials/wake-probe.test.ts
blob: a97a00c7274376e1fbfea2c439d06d11fa8b88a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import { describe, expect, it, vi } from "vitest";

// `claude.ts` transitively imports `db/index.js`, whose top-level
// `import { Database } from "bun:sqlite"` can't resolve under vitest's Node
// runtime. Stub the db module — `buildWakeProbeBody` never touches it.
vi.mock("../../src/db/index.js", () => ({
	getDatabase: vi.fn(() => {
		throw new Error("db not available in this test");
	}),
}));

const { buildWakeProbeBody, selectHaikuModel } = await import("../../src/credentials/claude.js");

const IDENTITY = "You are Claude Code, Anthropic's official CLI for Claude.";

describe("buildWakeProbeBody", () => {
	it("targets the requested model with a tiny token budget", () => {
		const body = buildWakeProbeBody("claude-3-5-haiku-20241022");
		expect(body.model).toBe("claude-3-5-haiku-20241022");
		expect(body.max_tokens).toBe(16);
	});

	it("emits a Claude-Code-shaped system[]: billing first, identity second", () => {
		const body = buildWakeProbeBody("claude-3-5-haiku-20241022");
		expect(body.system).toHaveLength(2);

		// system[0] is the billing header line (no cache_control on a probe).
		expect(body.system[0]).toEqual({
			type: "text",
			text: expect.stringMatching(/^x-anthropic-billing-header: /),
		});
		expect(body.system[0]).not.toHaveProperty("cache_control");

		// system[1] is the VERBATIM Claude Code identity string. Anthropic
		// rejects OAuth (Pro/Max) requests whose system[] lacks this.
		expect(body.system[1]).toEqual({ type: "text", text: IDENTITY });
	});

	it("carries a single short user message", () => {
		const body = buildWakeProbeBody("claude-3-5-haiku-20241022");
		expect(body.messages).toEqual([{ role: "user", content: "hi" }]);
	});

	it("is deterministic for a given model (pure)", () => {
		const a = buildWakeProbeBody("claude-3-5-haiku-20241022");
		const b = buildWakeProbeBody("claude-3-5-haiku-20241022");
		expect(a).toEqual(b);
	});
});
describe("selectHaikuModel", () => {
	it("returns the id whose name contains 'haiku'", () => {
		const models = ["claude-sonnet-4-20250514", "claude-haiku-4-5-20251001"];
		expect(selectHaikuModel(models)).toBe("claude-haiku-4-5-20251001");
	});

	it("matches case-insensitively", () => {
		expect(selectHaikuModel(["Claude-HAIKU-Latest"])).toBe("Claude-HAIKU-Latest");
	});

	it("returns the FIRST match when several models contain 'haiku'", () => {
		// `/v1/models` returns newest-first, so first-match prefers the newest.
		const models = ["claude-haiku-4-5-20251001", "claude-3-5-haiku-20241022"];
		expect(selectHaikuModel(models)).toBe("claude-haiku-4-5-20251001");
	});

	it("returns null when no model contains 'haiku'", () => {
		expect(selectHaikuModel(["claude-sonnet-4-20250514", "claude-opus-4-20250514"])).toBeNull();
	});

	it("returns null for an empty list", () => {
		expect(selectHaikuModel([])).toBeNull();
	});
});