import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import { DatabaseSync } from "node:sqlite"; import test from "node:test"; import { derivePairingProgress, OWNED_PAIRING_PROGRESS_SQL, } from "../db/pairing.ts"; const NOW = "2026-08-12T12:00:00.000Z"; function row(overrides = {}) { return { id: "pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", status: "waiting", expires_at: "2026-08-12T12:10:00.000Z", claimed_host_id: null, claimed_at: null, last_claim_attempt_at: null, ...overrides, }; } test("derives honest waiting, terminal, and claimed pairing progress", () => { assert.equal(derivePairingProgress(row(), NOW).status, "waiting"); assert.equal( derivePairingProgress( row({ expires_at: "2026-08-12T12:00:00.000Z" }), NOW, ).status, "expired", ); assert.equal(derivePairingProgress(row({ status: "locked" }), NOW).status, "locked"); assert.equal(derivePairingProgress(row({ status: "cancelled" }), NOW).status, "cancelled"); assert.deepEqual( derivePairingProgress( row({ status: "claimed", claimed_host_id: "host_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", claimed_at: "2026-08-12T12:01:00.000Z", }), NOW, ), { id: "pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", status: "claimed", expiresAt: "2026-08-12T12:10:00.000Z", claimedHostId: "host_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", claimedAt: "2026-08-12T12:01:00.000Z", claimAttemptState: "not_seen", lastClaimAttemptAt: null, }, ); assert.throws( () => derivePairingProgress(row({ status: "claimed" }), NOW), /incomplete_claimed_pairing_progress/, ); assert.throws( () => derivePairingProgress(row({ status: "unexpected" }), NOW), /unknown_pairing_progress_status/, ); }); test("distinguishes an unseen request from a safe owner-visible claim attempt signal", () => { assert.deepEqual( derivePairingProgress(row(), NOW), { id: "pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", status: "waiting", expiresAt: "2026-08-12T12:10:00.000Z", claimedHostId: null, claimedAt: null, claimAttemptState: "not_seen", lastClaimAttemptAt: null, }, ); assert.deepEqual( derivePairingProgress( row({ last_claim_attempt_at: "2026-08-12T11:59:00.000Z" }), NOW, ), { id: "pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", status: "waiting", expiresAt: "2026-08-12T12:10:00.000Z", claimedHostId: null, claimedAt: null, claimAttemptState: "seen", lastClaimAttemptAt: "2026-08-12T11:59:00.000Z", }, ); assert.equal( derivePairingProgress(row({ last_claim_attempt_at: "bad-time" }), NOW) .claimAttemptState, "invalid", ); assert.equal( derivePairingProgress( row({ last_claim_attempt_at: "2026-08-12T12:06:00.000Z" }), NOW, ).claimAttemptState, "invalid", ); }); test("queries pairing progress only through the owning account", () => { const db = new DatabaseSync(":memory:"); db.exec(` CREATE TABLE pairing_requests ( id TEXT PRIMARY KEY, account_id TEXT NOT NULL, status TEXT NOT NULL, expires_at TEXT NOT NULL, claimed_host_id TEXT, claimed_at TEXT ); CREATE TABLE pairing_claim_attempts ( pairing_request_id TEXT NOT NULL, created_at TEXT NOT NULL ); INSERT INTO pairing_requests VALUES ('pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', 'acct_a', 'waiting', '2026-08-12T12:10:00.000Z', NULL, NULL); INSERT INTO pairing_claim_attempts VALUES ('pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa', '2026-08-12T11:59:00.000Z'); `); const query = db.prepare(OWNED_PAIRING_PROGRESS_SQL); assert.equal( query.get("pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "acct_a").status, "waiting", ); assert.equal( query.get("pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "acct_a") .last_claim_attempt_at, "2026-08-12T11:59:00.000Z", ); assert.equal( query.get("pair_aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "acct_b"), undefined, ); db.close(); }); test("polls the owner-only no-store endpoint and clears one-time state at a terminal result", async () => { const [route, repository, form, stateModel, plan] = await Promise.all([ readFile(new URL("../app/api/hosts/pairing/route.ts", import.meta.url), "utf8"), readFile(new URL("../db/repository.ts", import.meta.url), "utf8"), readFile(new URL("../app/dashboard/hosts/new/PairingForm.tsx", import.meta.url), "utf8"), readFile(new URL("../docs/state-model.md", import.meta.url), "utf8"), readFile(new URL("../docs/implementation-plan.md", import.meta.url), "utf8"), ]); assert.match(route, /export async function GET/); assert.match(route, /getCloudViewer/); assert.match(route, /getOwnedPairingProgress/); assert.match(route, /cache-control": "no-store/); assert.match(repository, /OWNED_PAIRING_PROGRESS_SQL/); assert.match(form, /pairing_id=\$\{encodeURIComponent\(pairingId!\)\}/); assert.match(form, /\{ cache: "no-store" \}/); assert.match(form, /window\.setTimeout\(poll, 2_000\)/); assert.match(form, /Cloud 已收到请求,但尚未认领/); assert.match(form, /尚未收到注册请求/); assert.match(form, /当前码有效时无需反复生成/); assert.match(form, /setCompletion[\s\S]*setPairing\(null\)/); assert.match(form, /一次性配对码已经从页面状态中清除/); assert.match(stateModel, /账户所有者可以轮询自己创建的配对状态/); assert.match(plan, /自动确认认领、过期、锁定或取消/); });