const assert = require('node:assert/strict') const test = require('node:test') const path = require('node:path') const fs = require('node:fs') const vm = require('node:vm') const {assertFixtureReconciliation} = require('./publish_public_runtime.cjs') const { reportNames, memoryTables, validateReports, readOnlySQL, executeReadOnly, identityQuery, resolveIdentities, reconciliationQuery, reconcile, } = require('./verify_public_fixture_reconciliation.cjs') function fixture() { let nextID = 0 const id = () => `00000000-0000-4000-8000-${String(++nextID).padStart(12, '0')}` const now = Date.parse('2026-09-20T02:00:00.000Z') const manifest = {release: 'public-20260919-223000', url: 'https://agent.pducv.cn', activated_at: '2026-09-20T00:00:00.000Z', api_activated_at: '2026-09-20T00:01:00.000Z', frontend_built_at: '2026-09-20T00:02:00.000Z', frontend_activated_at: '2026-09-20T00:03:00.000Z'} const reports = Object.fromEntries(reportNames.map((name, index) => [name, { started_at: `2026-09-20T01:0${index}:00.000Z`, time: `2026-09-20T01:0${index}:50.000Z`, origin: manifest.url, api_origin: manifest.url, passed: true, checks: [{name: 'fixture_checked', passed: true}], failures: [], }])) const specifications = [ ['public-resource-isolation', 'resource-isolation-1001', {users: 2, roles: 1, agents: 2, skills: 1, kbs: 1}], ['public-resource-files', 'file-isolation-1002', {users: 2, roles: 1, kbs: 2, docs: 1, sessions: 1, messages: 2, resources: 3}], ['public-indexed-retrieval-isolation', 'indexed-isolation-1003', {users: 2, roles: 1, kbs: 1, docs: 1, tags: 2}], ] for (const [name, prefix, counts] of specifications) Object.assign(reports[name], {prefix, fixtures: Object.fromEntries(Object.entries(counts).map(([field, count]) => [field, Array.from({length: count}, id)]))}) const indexed = reports['public-indexed-retrieval-isolation'] indexed.cleanup = indexed.fixtures.kbs.map(knowledgeID => ({name: `knowledge ${knowledgeID}`, passed: true})) const resource = reports['public-resource-isolation'] resource.fixtures.memory_subjects = resource.fixtures.users.map(user => `web_user:${user}`) const file = reports['public-resource-files'] file.fixtures.message_scopes = [{tenant_id: 10059, session_id: file.fixtures.sessions[0], user_id: file.fixtures.users[0], title: file.prefix, username: `${file.prefix}-alice`, email: `${file.prefix}-alice@example.test`, messages: file.fixtures.messages.map((message, index) => ({ id: message, content: ['Disposable attachment fixture.', 'Unrelated message.'][index], }))}] const tenant = reports['public-tenant-isolation'] Object.assign(tenant, {prefix: 'tenant-isolation-1004', second_tenant: 10060}) const identities = [] const addUser = (report, username, userID = id(), email = `${username}@example.test`, tenantID = 10059) => { identities.push({id: userID, username, email, tenant_id: tenantID, created_at: report.started_at}) } for (const [name, labels] of [ ['public-resource-isolation', ['alice', 'bob']], ['public-resource-files', ['alice', 'bob']], ['public-indexed-retrieval-isolation', ['owner', 'reader']], ]) labels.forEach((label, index) => addUser(reports[name], `${reports[name].prefix}-${label}`, reports[name].fixtures.users[index])) for (const label of ['alice', 'bob']) addUser(tenant, `${tenant.prefix}-${label}`) addUser(reports['public-access-control'], 'access-check-1005', id(), 'updated-access-check-1005@example.test') const groups = resolveIdentities(reports, identities) const {countNames} = reconciliationQuery(reports, groups) const database = {counts: Object.fromEntries(countNames.map(name => [name, 0])), retained: {soft_deleted_users: 9, revoked_tokens: 10, soft_deleted_sessions: 2, soft_deleted_documents: 4, soft_deleted_chunks: 2, workbench_revisions: 5}, discovered: {agents: resource.fixtures.agents}} const queries = [], paths = [] const options = {manifest, reports, now, directory: '/fixture-runtime', executeSQL: query => {queries.push(query); return queries.length === 1 ? identities : database}, exists: value => {paths.push(value); return false}} return {options, reports, identities, groups, database, queries, paths, id} } test('reconciles all fresh reports and accepts retained audit and soft-delete history', () => { const value = fixture() const report = reconcile(value.options) assert.equal(report.passed, true) assert.equal(report.read_only, true) assert.equal(report.checks.length, 30) assert.deepEqual(report.failures, []) assert.equal(value.queries.length, 2) assert.deepEqual(report.retained, value.database.retained) assert.equal(report.identities.length, 9) assert.match(report.scope, /Milvus.*not queried again/) assert.deepEqual(value.paths, [path.join('/fixture-runtime', 'acceptance/files/10059/exports/file-isolation-1002')]) }) test('generated reconciliation satisfies the publication gate contract', () => { const {options} = fixture() options.directory = path.resolve(__dirname, '../..') const report = reconcile(options) assert.doesNotThrow(() => assertFixtureReconciliation(options.manifest, report, options.reports, options.now)) }) for (const name of reportNames) { test(`rejects stale ${name} before any database query`, () => { const {options, reports, queries} = fixture() reports[name].started_at = '2026-09-20T00:02:59.999Z' assert.throws(() => reconcile(options), /started before this runtime/) assert.deepEqual(queries, []) }) } for (const [label, mutate] of [ ['missing report', reports => {delete reports['public-navigation']}], ['wrong origin', reports => {reports['public-navigation'].origin = 'http://127.0.0.1:18088'}], ['failed report', reports => {reports['public-navigation'].passed = false}], ['hidden failed check', reports => {reports['public-navigation'].checks.push({name: 'failed', passed: false})}], ['reported failures', reports => {reports['public-navigation'].failures.push('failed cleanup')}], ['failed cleanup result', reports => {reports['public-indexed-retrieval-isolation'].cleanup = [{name: 'knowledge fixture', passed: false}]}], ['invalid cleanup result', reports => {reports['public-indexed-retrieval-isolation'].cleanup = [null]}], ['missing indexed cleanup', reports => {delete reports['public-indexed-retrieval-isolation'].cleanup}], ['mismatched indexed cleanup', reports => {reports['public-indexed-retrieval-isolation'].cleanup[0].name = 'knowledge other'}], ['empty indexed fixtures', reports => {reports['public-indexed-retrieval-isolation'].fixtures.kbs = []}], ['missing start time', reports => {delete reports['public-navigation'].started_at}], ['future completion', reports => {reports['public-navigation'].time = '2026-09-21T01:00:00.000Z'}], ['unsafe file prefix', reports => {reports['public-resource-files'].prefix = '../../uploads'}], ['invalid optional ID', reports => {reports['public-tenant-isolation'].fixtures = {agents: ['bad ID']}}], ['duplicate UUID', reports => {reports['public-resource-isolation'].fixtures.users.fill(reports['public-resource-isolation'].fixtures.users[0])}], ['missing IDs', reports => {delete reports['public-resource-files'].fixtures.resources}], ['changed message ID', reports => {reports['public-resource-files'].fixtures.message_scopes[0].messages[0].id = reports['public-resource-files'].fixtures.docs[0]}], ['changed message content', reports => {reports['public-resource-files'].fixtures.message_scopes[0].messages[0].content = 'Real user message.'}], ['different message tenant', reports => {reports['public-resource-files'].fixtures.message_scopes[0].tenant_id = 10060}], ['different message owner', reports => {reports['public-resource-files'].fixtures.message_scopes[0].user_id = reports['public-resource-files'].fixtures.users[1]}], ['different fixture email', reports => {reports['public-resource-files'].fixtures.message_scopes[0].email = 'real@example.test'}], ['extra synthetic message', reports => {reports['public-resource-files'].fixtures.messages.push(reports['public-resource-files'].fixtures.docs[0])}], ['wrong memory subjects', reports => {reports['public-resource-isolation'].fixtures.memory_subjects = ['web_user:other']}], ['unsafe secondary tenant', reports => {reports['public-tenant-isolation'].second_tenant = 10059}], ['invalid access prefix', reports => {reports['public-access-control'].prefix = 'access-check-1005-extra'}], ]) { test(`rejects ${label} before database access`, () => { const {options, reports, queries} = fixture() mutate(reports) assert.throws(() => reconcile(options)) assert.deepEqual(queries, []) }) } for (const [label, mutate] of [ ['email mismatch', rows => {rows[0].email = 'real@example.test'}], ['tenant mismatch', rows => {rows[0].tenant_id = 1}], ['creation outside the run', rows => {rows[0].created_at = '2026-09-19T01:00:00.000Z'}], ['missing user', rows => {rows.pop()}], ['ambiguous access account', rows => {rows.push({...rows.at(-1), id: 'ffffffff-0000-4000-8000-000000000001'})}], ['unrelated returned account', rows => {rows.push({...rows[0], username: 'unrelated', id: 'ffffffff-0000-4000-8000-000000000002'})}], ]) { test(`rejects discovered ${label} before reconciliation query`, () => { const {options, identities, queries} = fixture() mutate(identities) assert.throws(() => reconcile(options)) assert.equal(queries.length, 1) }) } test('can identify the tenant fixture while its restoration is incomplete', () => { const value = fixture() value.identities.find(row => row.username === 'tenant-isolation-1004-bob').tenant_id = 10060 assert.doesNotThrow(() => resolveIdentities(value.reports, value.identities)) }) test('applies read-only mode, timeout and rollback to both generated SQL queries', () => { const value = fixture() reconcile(value.options) const calls = [] for (const query of value.queries) { const output = executeReadOnly(query, (command, args, options) => { calls.push({command, args, options}) return '{"injected":true}\n' }) assert.deepEqual(output, {injected: true}) } for (const {command, args, options} of calls) { assert.equal(command, 'docker') assert.ok(args.includes('colima-yawan')) assert.ok(args.includes('yawan_native_acceptance_20260915')) assert.ok(args.includes('ON_ERROR_STOP=1')) assert.equal(options.timeout, 45000) assert.match(options.input, /^BEGIN READ ONLY;\nSET LOCAL statement_timeout='15s';\n(?:WITH|SELECT)\b/) assert.match(options.input, /;\nROLLBACK;$/) assert.doesNotMatch(options.input, /(?:^|;)\s*(?:DELETE|UPDATE|INSERT|COMMIT|DROP|ALTER|TRUNCATE)\b/im) assert.doesNotMatch(options.input, /password|access_token|refresh_token/i) assert.deepEqual(options.stdio, ['pipe', 'pipe', 'pipe']) } }) test('rejects a direct mutation query and invalid database output', () => { assert.throws(() => readOnlySQL('DELETE FROM users;')) assert.throws(() => executeReadOnly('SELECT 1;', () => 'not JSON')) }) test('uses recorded IDs and exact bounded discovery for missing fixture IDs', () => { const {reports, groups} = fixture() const discovery = identityQuery(reports) assert.match(discovery, /u\.username ~ '\^access-check-\[0-9\]\+\$'/) assert.match(discovery, /u\.tenant_id=10059/) assert.match(discovery, /u\.email IN \(u\.username \|\| '@example.test','updated-'/) assert.match(discovery, /u\.username IN \('tenant-isolation-1004-alice','tenant-isolation-1004-bob'\)/) reports['public-access-control'].prefix = 'access-check-1005' assert.match(identityQuery(reports), /u\.username='access-check-1005'/) const {query} = reconciliationQuery(reports, groups) for (const group of groups) { assert.ok(query.includes(group.report.started_at)) assert.ok(query.includes(group.report.time)) for (const userID of group.users) assert.ok(query.includes(userID)) } assert.match(query, /UNION SELECT unnest\(ARRAY\[/) assert.match(query, /FROM workbench_events e WHERE e\.tenant_id=10059 AND e\.action='create'/) assert.match(query, /e\.message ~ '\^创建access-check-1005/) assert.doesNotMatch(query, /LIKE|ILIKE/) for (const table of memoryTables) assert.match(query, new RegExp(`FROM ${table} WHERE tenant_id IN \\(10059,10060\\) AND subject_id IN \\('web_user:`)) for (const id of reports['public-resource-files'].fixtures.messages) assert.ok(query.includes(id)) for (const id of reports['public-resource-files'].fixtures.resources) assert.ok(query.includes(id)) assert.match(query, /FROM chunks WHERE tenant_id=10059 AND \(knowledge_base_id IN/) assert.match(query, /FROM workbench_access_publications WHERE tenant_id=10059 AND resource_id IN/) }) for (const name of ['active_users', 'grants', 'unrevoked_tokens', 'synthetic_messages', 'file_resources', 'file_bindings', 'active_chunks', 'publications', ...memoryTables]) { test(`reports leftover ${name} without removing it`, () => { const {options, database} = fixture() database.counts[name] = 1 const report = reconcile(options) assert.equal(report.passed, false) assert.deepEqual(report.failures, [name]) assert.equal(database.counts[name], 1) }) } test('fails when the exact attachment fixture directory remains', () => { const {options} = fixture() options.exists = () => true assert.deepEqual(reconcile(options).failures, ['file_fixture_directory_removed']) }) for (const count of [undefined, null, '0', -1, 0.5, Infinity, NaN, Number.MAX_SAFE_INTEGER + 1]) { test(`fails closed on invalid counts (${String(count)})`, () => { const {options, database} = fixture() database.counts.active_users = count assert.throws(() => reconcile(options), /Invalid reconciliation count: active_users/) }) } test('does not accept an unvalidated release or origin', () => { const {options} = fixture() options.manifest.release = '../old-release' assert.throws(() => validateReports(options.manifest, options.reports, options.now)) options.manifest.release = 'public-20260919-223000' options.manifest.url = 'http://127.0.0.1:18088' assert.throws(() => validateReports(options.manifest, options.reports, options.now)) }) test('CLI invalidates a stale successful artifact before a report read can fail', () => { const writes = [], errors = [] const module = {exports: {}} const process = {argv: ['node', 'script.cjs', 'public-20260919-223000']} const injectedRequire = name => { if (name === 'node:fs') return { writeFileSync: (...args) => writes.push(args), readFileSync: () => {throw new Error('Missing verification report')}, } if (name === 'node:child_process') return {execFileSync: () => assert.fail('No database command is allowed')} return require(name) } injectedRequire.main = module vm.runInNewContext(fs.readFileSync(path.join(__dirname, 'verify_public_fixture_reconciliation.cjs'), 'utf8'), { require: injectedRequire, module, process, __dirname, console: {error: value => errors.push(value)}, }) assert.equal(writes.length, 1) assert.match(writes[0][0], /acceptance\/public-fixture-reconciliation\.json$/) const invalidated = JSON.parse(writes[0][1]) assert.equal(invalidated.passed, false) assert.equal(invalidated.read_only, true) assert.deepEqual(invalidated.failures, ['Reconciliation has not completed.']) assert.equal(writes[0][2].mode, 0o600) assert.deepEqual(errors, ['Missing verification report']) assert.equal(process.exitCode, 1) })