import { describe, expect, it } from "vitest"; import type { JobView, PollState } from "@/lib/jobs/job"; import { authOpenUrl, connectPhase, formatExpiresAt, jobConnected, KIMI_CODE_MODEL, KIMI_CODE_PROVIDER, toConnectionStatus, toDeviceDisplay, } from "./kimiOauth"; const poll = (over: Partial): PollState => ({ status: "polling", progress: 0, job: null, error: null, ...over, }); const job = (over: Partial): JobView => ({ id: "j1", kind: "kimi_oauth", status: "done", progress: 100, result: null, error: null, ...over, }); describe("KIMI_CODE constants", () => { it("uses the OAuth provider + coding model names", () => { expect(KIMI_CODE_PROVIDER).toBe("kimi-code"); expect(KIMI_CODE_MODEL).toBe("kimi-for-coding"); }); }); describe("toDeviceDisplay", () => { it("maps start response fields", () => { const d = toDeviceDisplay({ job_id: "abc", user_code: "WXYZ-1234", verification_uri: "https://auth.kimi.com/device", verification_uri_complete: "https://auth.kimi.com/device?code=WXYZ-1234", expires_in: 600, interval: 5, }); expect(d).toEqual({ userCode: "WXYZ-1234", verificationUri: "https://auth.kimi.com/device", verificationUriComplete: "https://auth.kimi.com/device?code=WXYZ-1234", expiresIn: 600, interval: 5, }); }); it("defaults missing complete uri to null", () => { const d = toDeviceDisplay({ job_id: "abc", user_code: "CODE", verification_uri: "https://auth.kimi.com/device", verification_uri_complete: null, expires_in: 600, interval: 5, }); expect(d.verificationUriComplete).toBeNull(); }); }); describe("authOpenUrl", () => { it("prefers the complete uri when present", () => { expect( authOpenUrl({ userCode: "C", verificationUri: "https://auth.kimi.com/device", verificationUriComplete: "https://auth.kimi.com/device?code=C", expiresIn: 600, interval: 5, }), ).toBe("https://auth.kimi.com/device?code=C"); }); it("falls back to the plain uri", () => { expect( authOpenUrl({ userCode: "C", verificationUri: "https://auth.kimi.com/device", verificationUriComplete: null, expiresIn: 600, interval: 5, }), ).toBe("https://auth.kimi.com/device"); }); }); describe("toConnectionStatus", () => { it("narrows connected + expires_at", () => { expect( toConnectionStatus({ connected: true, expires_at: "2026-07-01T00:00:00Z" }), ).toEqual({ connected: true, expiresAt: "2026-07-01T00:00:00Z" }); }); it("defaults non-string expires_at to null", () => { expect(toConnectionStatus({ connected: false })).toEqual({ connected: false, expiresAt: null, }); }); }); describe("jobConnected", () => { it("true only when result.connected === true (no token expected)", () => { expect(jobConnected(job({ result: { connected: true, provider: "kimi-code" } }))).toBe( true, ); expect(jobConnected(job({ result: { connected: false } }))).toBe(false); expect(jobConnected(job({ result: null }))).toBe(false); }); }); describe("connectPhase", () => { it("not started + not connected → idle", () => { expect( connectPhase({ started: false, connected: false, poll: poll({}) }), ).toBe("idle"); }); it("not started + connected (status query) → connected", () => { expect( connectPhase({ started: false, connected: true, poll: poll({}) }), ).toBe("connected"); }); it("started + polling → awaiting", () => { expect( connectPhase({ started: true, connected: false, poll: poll({ status: "polling" }), }), ).toBe("awaiting"); }); it("started + done + job.connected → connected", () => { expect( connectPhase({ started: true, connected: false, poll: poll({ status: "done", job: job({ result: { connected: true, provider: "kimi-code" } }), }), }), ).toBe("connected"); }); it("started + done but job not connected → error", () => { expect( connectPhase({ started: true, connected: false, poll: poll({ status: "done", job: job({ result: { connected: false } }) }), }), ).toBe("error"); }); it("started + poll error (expired/denied/network) → error", () => { expect( connectPhase({ started: true, connected: false, poll: poll({ status: "error", error: "授权已过期" }), }), ).toBe("error"); }); }); describe("formatExpiresAt", () => { it("returns null for null/invalid input", () => { expect(formatExpiresAt(null)).toBeNull(); expect(formatExpiresAt("not-a-date")).toBeNull(); }); it("formats a valid ISO timestamp to a non-empty string", () => { const out = formatExpiresAt("2026-07-01T08:30:00Z"); expect(typeof out).toBe("string"); expect(out).not.toBe(""); }); });