Files
nekonest-cloud/tests/access-boundary.test.mjs

192 lines
7.2 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import { DatabaseSync } from "node:sqlite";
import test from "node:test";
import {
AUTHENTICATE_ACTIVE_DEVICE_SQL,
DEACTIVATE_OWNED_HOST_SQL,
FREE_BETA_ACCESS_BOUNDARY,
REVOKE_ACTIVE_DEVICE_CREDENTIALS_SQL,
} from "../db/access-boundary.ts";
import { CLAIM_HOST_SQL } from "../db/pairing.ts";
import { REQUIRED_PUBLIC_BETA_P0_KEYS } from "../db/launch-gates.ts";
function createClaimCapacityDatabase() {
const db = new DatabaseSync(":memory:");
db.exec(`
CREATE TABLE beta_programs (
id TEXT PRIMARY KEY, state TEXT NOT NULL, capacity_slots INTEGER,
starts_at TEXT NOT NULL, ends_at TEXT, created_at TEXT NOT NULL
);
CREATE TABLE entitlement_grants (
account_id TEXT NOT NULL, state TEXT NOT NULL, capacity_slots INTEGER,
starts_at TEXT NOT NULL, ends_at TEXT, revoked_at TEXT
);
CREATE TABLE launch_gates (
key TEXT PRIMARY KEY, priority TEXT NOT NULL, status TEXT NOT NULL,
owner TEXT, notes TEXT NOT NULL, evidence_url TEXT
);
CREATE TABLE pairing_requests (
id TEXT PRIMARY KEY, account_id TEXT NOT NULL, requested_name TEXT NOT NULL,
os TEXT NOT NULL, code_hash TEXT NOT NULL, status TEXT NOT NULL,
expires_at TEXT NOT NULL, locked_at TEXT, failed_attempts INTEGER NOT NULL DEFAULT 0
);
CREATE TABLE hosts (
id TEXT PRIMARY KEY, account_id TEXT NOT NULL, name TEXT NOT NULL,
os TEXT NOT NULL, lifecycle TEXT NOT NULL, slot_state TEXT NOT NULL,
connection_state TEXT NOT NULL, daemon_version TEXT,
ed25519_public TEXT, x25519_public TEXT,
identity_fingerprint TEXT UNIQUE, claim_request_id TEXT, claimed_at TEXT,
created_at TEXT NOT NULL, deactivated_at TEXT
);
`);
const gate = db.prepare(
`INSERT INTO launch_gates
(key, priority, status, owner, notes, evidence_url)
VALUES (?, 'P0', 'passed', 'test', 'verified', 'https://evidence.example.test/p0')`,
);
for (const key of REQUIRED_PUBLIC_BETA_P0_KEYS) gate.run(key);
return db;
}
function claimHost(db, digit, accountId, now) {
const pairingId = `pair_${digit.repeat(32)}`;
db.prepare(
`INSERT INTO pairing_requests
(id, account_id, requested_name, os, code_hash, status, expires_at)
VALUES (?, ?, ?, 'linux', ?, 'waiting', '2026-08-12T13:00:00.000Z')`,
).run(pairingId, accountId, `Host ${digit}`, `hash-${digit}`);
return Number(db.prepare(CLAIM_HOST_SQL).run(
`host_${digit.repeat(32)}`,
`ed-${digit}`,
`x-${digit}`,
`fingerprint-${digit}`,
now,
now,
pairingId,
`hash-${digit}`,
now,
"linux",
now,
now,
now,
now,
null,
).changes);
}
test("documents the free-beta expiry operation boundary", () => {
assert.deepEqual(
FREE_BETA_ACCESS_BOUNDARY.map(({ operation, requiresCurrentEntitlement }) => [operation, requiresCurrentEntitlement]),
[
["create_pairing", true],
["claim_pairing", true],
["cancel_pairing", false],
["connect_device", false],
["revoke_host", false],
],
);
});
test("keeps an existing active device usable after entitlement expiry until explicit revocation", () => {
const db = new DatabaseSync(":memory:");
db.exec(`
CREATE TABLE hosts (
id TEXT PRIMARY KEY,
account_id TEXT NOT NULL,
lifecycle TEXT NOT NULL,
slot_state TEXT NOT NULL,
connection_state TEXT NOT NULL,
deactivated_at TEXT
);
CREATE TABLE device_credentials (
id TEXT PRIMARY KEY,
host_id TEXT NOT NULL,
token_hash TEXT NOT NULL,
status TEXT NOT NULL,
expires_at TEXT,
last_used_at TEXT,
revoked_at TEXT
);
CREATE TABLE entitlement_grants (
id TEXT PRIMARY KEY,
account_id TEXT NOT NULL,
state TEXT NOT NULL,
ends_at TEXT,
revoked_at TEXT
);
INSERT INTO hosts VALUES ('host_a', 'account_a', 'active', 'active', 'online', NULL);
INSERT INTO device_credentials VALUES ('credential_a', 'host_a', 'token-hash', 'active', NULL, NULL, NULL);
INSERT INTO entitlement_grants VALUES ('grant_expired', 'account_a', 'active', '2026-08-11T00:00:00.000Z', NULL);
`);
const now = "2026-08-12T12:00:00.000Z";
assert.equal(
Number(db.prepare(AUTHENTICATE_ACTIVE_DEVICE_SQL).run(now, "host_a", "token-hash").changes),
1,
);
assert.equal(db.prepare("SELECT last_used_at FROM device_credentials WHERE id = 'credential_a'").get().last_used_at, now);
assert.equal(Number(db.prepare(REVOKE_ACTIVE_DEVICE_CREDENTIALS_SQL).run(now, "host_a").changes), 1);
assert.equal(Number(db.prepare(DEACTIVATE_OWNED_HOST_SQL).run(now, "host_a", "account_a").changes), 1);
assert.equal(
Number(db.prepare(AUTHENTICATE_ACTIVE_DEVICE_SQL).run(now, "host_a", "token-hash").changes),
0,
);
db.close();
});
test("rechecks current per-account capacity atomically when a daemon claims a waiting pairing", () => {
const db = createClaimCapacityDatabase();
const now = "2026-08-12T12:00:00.000Z";
db.prepare(
`INSERT INTO beta_programs
(id, state, capacity_slots, starts_at, ends_at, created_at)
VALUES ('beta', 'active', 2, ?, NULL, ?)`,
).run(now, now);
assert.equal(claimHost(db, "1", "account_public", now), 1);
db.prepare("UPDATE beta_programs SET capacity_slots = 1 WHERE id = 'beta'").run();
assert.equal(claimHost(db, "2", "account_public", now), 0);
assert.equal(
db.prepare(
"SELECT COUNT(*) AS count FROM hosts WHERE account_id = 'account_public' AND lifecycle = 'active'",
).get().count,
1,
);
db.prepare(
`INSERT INTO entitlement_grants
(account_id, state, capacity_slots, starts_at, ends_at, revoked_at)
VALUES ('account_public', 'active', 1, ?, NULL, NULL)`,
).run(now);
assert.equal(claimHost(db, "3", "account_public", now), 1);
assert.equal(claimHost(db, "4", "account_public", now), 0);
assert.equal(
db.prepare(
"SELECT COUNT(*) AS count FROM hosts WHERE account_id = 'account_public' AND lifecycle = 'active'",
).get().count,
2,
);
db.close();
});
test("keeps entitlement checks on new access while security exits stay independent", async () => {
const [pairingSql, repository, relayControl, stateModel, contract] = await Promise.all([
readFile(new URL("../db/pairing.ts", import.meta.url), "utf8"),
readFile(new URL("../db/repository.ts", import.meta.url), "utf8"),
readFile(new URL("../db/relay-control-plane.ts", import.meta.url), "utf8"),
readFile(new URL("../docs/state-model.md", import.meta.url), "utf8"),
readFile(new URL("../docs/commercial-contract.md", import.meta.url), "utf8"),
]);
assert.match(pairingSql, /CURRENT_PAIRING_ACCESS/);
assert.match(pairingSql, /HOST_CLAIM_CAPACITY/);
assert.match(pairingSql, /PUBLIC_BETA_GATE_READY_SQL/);
assert.match(relayControl, /authorizeDeviceForRelay/);
assert.match(repository, /REVOKE_ACTIVE_DEVICE_CREDENTIALS_SQL/);
assert.match(repository, /DEACTIVATE_OWNED_HOST_SQL/);
assert.match(stateModel, /主机撤销.*撤销所有设备凭证.*释放席位/);
assert.match(stateModel, /第 N\+1 台返回 `device_capacity_exceeded`/);
assert.match(contract, /设备离线不释放席位/);
assert.match(contract, /明确撤销才释放/);
});