feat: establish NekoNest Cloud control and relay
This commit is contained in:
@@ -0,0 +1,162 @@
|
||||
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, /自动确认认领、过期、锁定或取消/);
|
||||
});
|
||||
Reference in New Issue
Block a user