Files
opencode-kimi-full/test/headers.test.ts
T
lemon07r 64550fb057 Release v1.0.2: broaden offline test coverage
Adds one test file per source file plus a shared fetch mock helper:

- test/constants.test.ts: OAuth scope/client_id/PROVIDER_ID/MODEL_ID pins,
  rules 1, 6, 8.
- test/headers.test.ts: seven fingerprint header keys, UA/X-Msh-Version
  track KIMI_CLI_VERSION, ASCII-only values, device-id format and
  stability across calls (rules 1, 2).
- test/oauth.test.ts: form-encoded bodies for device-start/refresh,
  authorization_pending/expired_token/unknown-error branches, non-OK and
  non-JSON error shapes.
- test/plugin.test.ts: chat.params provider+model gating, full effort
  matrix (rule 4), prompt_cache_key gating (rule 5), camelCase effort
  input, loader Authorization ownership (rule 3), refresh-on-expiry with
  persistAuth, 401 single-retry no-loop, device-flow authorize wiring.
- test/_util/fetchMock.ts: zero-dep global-fetch swap returning canned
  Responses with call capture.

Notes:

- Tests use the real ~/.kimi/device_id; getDeviceId is idempotent and the
  file is shared with kimi-cli by design (AGENTS.md rule 2), so no HOME
  redirect is needed. Attempted monkey-patching os.homedir instead is
  fragile under Bun's eager import-binding resolution.
- pollDeviceToken tests use interval=1 because `device.interval || 5`
  treats 0 as the default 5s and would push each iteration past the
  default bun-test timeout.

Total: 35 tests, ~5s to run.
2026-04-17 03:18:23 -04:00

61 lines
2.1 KiB
TypeScript

import { test, expect } from "bun:test"
import fs from "node:fs"
import os from "node:os"
import path from "node:path"
import { KIMI_CLI_VERSION, USER_AGENT } from "../src/constants.ts"
import { getDeviceId, kimiHeaders } from "../src/headers.ts"
// Note: getDeviceId() reads/writes ~/.kimi/device_id. That file is shared
// with kimi-cli on purpose (AGENTS.md rule 2) and the function is
// idempotent — if the file exists we reuse it, otherwise we create it. The
// tests therefore use the real HOME: they cannot clobber anything, and
// mocking `os.homedir` for Node's built-in `os` is fragile (Bun resolves
// the import binding eagerly).
test("kimiHeaders emits exactly the 7 fingerprint keys kimi-cli sends", () => {
const h = kimiHeaders()
expect(Object.keys(h).sort()).toEqual(
[
"User-Agent",
"X-Msh-Device-Id",
"X-Msh-Device-Model",
"X-Msh-Device-Name",
"X-Msh-Os-Version",
"X-Msh-Platform",
"X-Msh-Version",
].sort(),
)
})
test("User-Agent and X-Msh-Version track KIMI_CLI_VERSION (AGENTS.md rule 1)", () => {
const h = kimiHeaders()
expect(h["User-Agent"]).toBe(USER_AGENT)
expect(h["User-Agent"]).toContain(KIMI_CLI_VERSION)
expect(h["X-Msh-Version"]).toBe(KIMI_CLI_VERSION)
expect(h["X-Msh-Platform"]).toBe("kimi_cli")
})
test("All header values are ASCII-only (undici rejects non-ASCII)", () => {
for (const [k, v] of Object.entries(kimiHeaders())) {
expect(v, `header ${k}`).toMatch(/^[\x20-\x7e]+$/)
}
})
test("Device id is a 32-char lowercase hex string (kimi-cli UUIDv4 no-dashes format)", () => {
expect(getDeviceId()).toMatch(/^[0-9a-f]{32}$/)
})
test("Device id is stable across calls and matches ~/.kimi/device_id on disk (AGENTS.md rule 2)", () => {
const first = getDeviceId()
const second = getDeviceId()
expect(second).toBe(first)
// Mirrors kimi-cli's path; shared by design.
const onDisk = fs.readFileSync(path.join(os.homedir(), ".kimi", "device_id"), "utf8").trim()
expect(onDisk).toBe(first)
})
test("Device id is also present and matches in the headers map", () => {
const h = kimiHeaders()
expect(h["X-Msh-Device-Id"]).toBe(getDeviceId())
})