Jason's 2026-11-12 ruling: the only deployed system is the dev environment on dragon-lin, so the system-type plug-in kind goes with no retired kind, fence, conversion or new refusal code. - plugins: system-type leaves KINDS; system-types.mjs and the five modules are deleted. An org file naming the kind refuses unknown-kind. - organization: the plugins.system-type check, systemTypesFence and the system-types.console right are removed. - bus: system-types.console, project.system-types[.set], the system-type half of the tester check, the runtime reads and system-type-not-enabled are removed. Step 3v-3w drops project_system_types, its index, its four guards and its rows; SCHEMA_VERSION is 3w (32 tables, 126 triggers, 5 views). - cli: mosaic org system-types and mosaic project system-types[-set] are removed; stack-release COUNTED and the budget supervisor's closed list move to 3w. - channel: the project.system-types verbs are removed. - webui: the System types page, route, dashboard row, chips and project selection are removed. - docs: REQ-SYS-1/2 now describe starter packs; README system-type text and the requirements titles follow.
74 lines
4.1 KiB
JavaScript
74 lines
4.1 KiB
JavaScript
import test from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import { DatabaseSync } from 'node:sqlite';
|
|
import { chmodSync, mkdirSync, mkdtempSync, readFileSync, rmSync } from 'node:fs';
|
|
import { tmpdir } from 'node:os';
|
|
import { join } from 'node:path';
|
|
import { SCHEMA_VERSION, Store, schemaDigest } from '../src/store.mjs';
|
|
|
|
// #1873 SP9: bus schema step 3v-3w drops project_system_types with its index, its four triggers and
|
|
// its rows (Jason's 2026-11-12 ruling removes the system-type kind outright).
|
|
const TABLE = 'project_system_types';
|
|
const ddl = readFileSync(new URL('../schema.sql', import.meta.url), 'utf8');
|
|
const createdBy3u = readFileSync(new URL('../src/migrations/3t-3u.sql', import.meta.url), 'utf8');
|
|
const named = (path) => {
|
|
const db = new DatabaseSync(path, { readOnly: true });
|
|
try {
|
|
return {
|
|
master: db.prepare('SELECT type,name FROM sqlite_master WHERE name LIKE ? OR tbl_name=? ORDER BY type,name').all(`${TABLE}%`, TABLE).map((r) => ({ ...r })),
|
|
sequence: db.prepare('SELECT name FROM sqlite_sequence WHERE name=?').all(TABLE).map((r) => r.name),
|
|
digest: schemaDigest(db),
|
|
version: db.prepare("SELECT value FROM meta WHERE key='schema_version'").get().value,
|
|
};
|
|
} finally {
|
|
db.close();
|
|
}
|
|
};
|
|
|
|
test('SP9 a store opened at schema 3w has no project_system_types table', (t) => {
|
|
assert.equal(SCHEMA_VERSION, '3w');
|
|
assert.equal(ddl.includes(TABLE), false, 'schema.sql no longer creates the table');
|
|
const root = mkdtempSync(join(tmpdir(), 'sp9-fresh-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
new Store(root).close();
|
|
const fresh = named(join(root, 'bus/bus.sqlite'));
|
|
assert.equal(fresh.version, '3w');
|
|
assert.deepEqual(fresh.master, []);
|
|
});
|
|
|
|
test('SP9 a 3v store holding project_system_types rows migrates to 3w with nothing of the table left', (t) => {
|
|
const root = mkdtempSync(join(tmpdir(), 'sp9-3v-'));
|
|
t.after(() => rmSync(root, { recursive: true, force: true }));
|
|
mkdirSync(join(root, 'bus'), { mode: 0o700 });
|
|
const path = join(root, 'bus/bus.sqlite');
|
|
const db = new DatabaseSync(path);
|
|
// The 3v schema is today's schema.sql plus what 3t-3u created; the store's step check refuses any
|
|
// other shape, so a migration that succeeds proves the fixture is a real 3v store.
|
|
db.exec(ddl + createdBy3u);
|
|
db.prepare('INSERT INTO meta VALUES (?,?)').run('schema_version', '3v');
|
|
db.prepare('INSERT INTO meta VALUES (?,?)').run('schema_digest', schemaDigest(db));
|
|
db.prepare('INSERT INTO meta VALUES (?,?)').run('comment_cursor_epoch', '2026-11-12T00:00:00.000Z');
|
|
db.prepare('INSERT INTO projects(id,at,business,name,repo,created_by) VALUES(?,?,?,?,?,?)').run('p1', '2026-11-12T00:00:00.000Z', 'acme', 'example', null, 'jason');
|
|
const select = db.prepare(`INSERT INTO ${TABLE}(at,business,project,types,by) VALUES(?,?,?,?,?)`);
|
|
select.run('2026-11-12T00:00:01.000Z', 'acme', 'p1', '["coding"]', 'jason');
|
|
select.run('2026-11-12T00:00:02.000Z', 'acme', 'p1', '["coding","montage"]', 'jason');
|
|
db.close();
|
|
chmodSync(path, 0o600);
|
|
const before = named(path);
|
|
assert.deepEqual(before.master.map((r) => r.name).sort(), [TABLE, `${TABLE}_business`, `${TABLE}_no_delete`, `${TABLE}_no_replace`, `${TABLE}_no_update`, `${TABLE}_project`]);
|
|
assert.deepEqual(before.sequence, [TABLE]);
|
|
assert.throws(() => new Store(root), /schema-mismatch/);
|
|
assert.deepEqual(named(path), before, 'the refused open changed nothing');
|
|
const store = new Store(root, { migrateSchema: true });
|
|
t.after(() => store.close());
|
|
assert.equal(store.get('SELECT name FROM projects WHERE id=?', 'p1').name, 'example', 'other rows stay');
|
|
const after = named(path);
|
|
assert.equal(after.version, '3w');
|
|
assert.deepEqual(after.master, [], 'no table, index or trigger of that name is left');
|
|
assert.deepEqual(after.sequence, [], 'no sqlite_sequence row of that name is left');
|
|
const freshRoot = mkdtempSync(join(tmpdir(), 'sp9-ref-'));
|
|
t.after(() => rmSync(freshRoot, { recursive: true, force: true }));
|
|
new Store(freshRoot).close();
|
|
assert.equal(after.digest, named(join(freshRoot, 'bus/bus.sqlite')).digest, 'the migrated schema is the fresh one');
|
|
});
|