feat(device-core): add device ownership lifecycle
This commit is contained in:
@@ -0,0 +1,60 @@
|
||||
import assert from "node:assert/strict";
|
||||
import { readFile } from "node:fs/promises";
|
||||
import test from "node:test";
|
||||
|
||||
const migrationUrl = new URL(
|
||||
"../migrations/006_device_lifecycle_ownership.sql",
|
||||
import.meta.url,
|
||||
);
|
||||
const repositoryUrl = new URL("../src/postgres-repository.mjs", import.meta.url);
|
||||
|
||||
test("lifecycle migration separates direct ownership from legacy contours", async () => {
|
||||
const sql = await readFile(migrationUrl, "utf8");
|
||||
|
||||
assert.match(sql, /alter column contour_id drop not null/);
|
||||
assert.match(sql, /add column if not exists owner_scope_id uuid/);
|
||||
assert.match(sql, /device_instances_project_owner_fk/);
|
||||
assert.match(sql, /device_instances_ownership_mode_check/);
|
||||
assert.match(sql, /references device_projects\(id, owner_scope_id\)/);
|
||||
assert.match(sql, /\) not valid;/);
|
||||
});
|
||||
|
||||
test("route-bound discovery and enrollment evidence are DB constrained", async () => {
|
||||
const sql = await readFile(migrationUrl, "utf8");
|
||||
|
||||
assert.match(sql, /device_discoveries_route_context_fk/);
|
||||
assert.match(sql, /device_discoveries_enrollment_context_fk/);
|
||||
assert.match(sql, /device_enrollment_observed_discovery_fk/);
|
||||
assert.match(sql, /device_enrollment_intents_active_identity_idx/);
|
||||
assert.match(sql, /where lifecycle_state in \('pending', 'observed', 'claimed'\)/);
|
||||
});
|
||||
|
||||
test("ownership history supports transfer without rewriting session provenance", async () => {
|
||||
const sql = await readFile(migrationUrl, "utf8");
|
||||
|
||||
assert.match(sql, /create table if not exists device_ownership_transitions/);
|
||||
assert.match(sql, /transition_kind in \('claim', 'transfer'\)/);
|
||||
assert.match(sql, /device_ownership_single_claim_idx/);
|
||||
assert.match(sql, /device_assert_session_current_project/);
|
||||
assert.match(sql, /device_assert_enrollment_current_project/);
|
||||
assert.match(sql, /drop constraint if exists device_sessions_device_id_project_id_fkey/);
|
||||
});
|
||||
|
||||
test("lifecycle migration contains no tenant, device, route or credential seed", async () => {
|
||||
const sql = await readFile(migrationUrl, "utf8");
|
||||
|
||||
assert.doesNotMatch(sql, /insert\s+into/i);
|
||||
assert.doesNotMatch(sql, /dcctouch|arusnavi|\bb2\b|imei|gelios/i);
|
||||
assert.doesNotMatch(sql, /155\.212\.|device\.nodedc\.ru|synology/i);
|
||||
assert.doesNotMatch(sql, /password|secret|private_key|credential_ref/i);
|
||||
});
|
||||
|
||||
test("repository applies lifecycle migration after registry commands", async () => {
|
||||
const source = await readFile(repositoryUrl, "utf8");
|
||||
const commandsIndex = source.indexOf("005_device_registry_commands.sql");
|
||||
const lifecycleIndex = source.indexOf("006_device_lifecycle_ownership.sql");
|
||||
|
||||
assert.notEqual(commandsIndex, -1);
|
||||
assert.notEqual(lifecycleIndex, -1);
|
||||
assert.ok(commandsIndex < lifecycleIndex);
|
||||
});
|
||||
Reference in New Issue
Block a user