53 lines
3.8 KiB
JavaScript
53 lines
3.8 KiB
JavaScript
const assert = require('node:assert/strict')
|
|||
|
|
const fs = require('node:fs')
|
||
|
|
const crypto = require('node:crypto')
|
||
|
|
const { execFileSync } = require('node:child_process')
|
||
|
|
|
||
|
|
const schema = `fusion_migration_test_${crypto.randomBytes(6).toString('hex')}`
|
||
|
|
const migration = fs.readFileSync(`${__dirname}/../migrations/versioned/000208_agent_fusions.up.sql`, 'utf8')
|
||
|
|
const checks = []
|
||
|
|
const sql = query => execFileSync('docker', ['--context', 'colima-yawan', 'exec', '-i', 'WeKnora-postgres',
|
||
|
|
'psql', '-X', '-U', 'postgres', '-d', 'yawan_native_acceptance_20260915', '-At', '-v', 'ON_ERROR_STOP=1'],
|
||
|
|
{ input: `SET search_path TO ${schema}, public;\n${query}`, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'] }).trim()
|
||
|
|
const check = (name, condition) => { assert.ok(condition, name); checks.push(name) }
|
||
|
|
const value = text => `'${JSON.stringify(text).replaceAll("'", "''")}'::jsonb`
|
||
|
|
function fixture(id, fusion, role = 'production', native = { role: 'production', sentinel: 'preserved' }) {
|
||
|
|
sql(`INSERT INTO custom_agents VALUES ('agent-${id}',10059,${value(native)},NULL);
|
||
|
|
INSERT INTO yawan_legacy_asset_mappings VALUES ('yawan-workbench',1,'agent',${id},10059,'agent-${id}',${value({ role, fusion_agent_ids: fusion })},'{}');`)
|
||
|
|
}
|
||
|
|
const run = () => sql(`BEGIN; ${migration} COMMIT;`)
|
||
|
|
try {
|
||
|
|
sql(`CREATE SCHEMA ${schema};
|
||
|
|
CREATE TABLE custom_agents (id TEXT PRIMARY KEY,tenant_id BIGINT,config JSONB,deleted_at TIMESTAMPTZ);
|
||
|
|
CREATE TABLE yawan_legacy_asset_mappings (source_system TEXT,source_organization_id BIGINT,source_kind TEXT,source_id BIGINT,target_tenant_id BIGINT,target_id TEXT,source_snapshot JSONB,native_snapshot JSONB);`)
|
||
|
|
fixture(1, [3, 2]); fixture(2, [1]); fixture(3, [])
|
||
|
|
fixture(4, [2], 'production', { role: 'production', fusion_agent_ids: [], sentinel: 'explicit-empty' })
|
||
|
|
fixture(5, [2], 'production', { role: 'qc', sentinel: 'native-duty-edited' })
|
||
|
|
run()
|
||
|
|
const data = JSON.parse(sql('SELECT jsonb_object_agg(id,config) FROM custom_agents').split('\n').at(-1))
|
||
|
|
check('ordered_direct_ids_preserved_without_recursive_cycle_expansion', JSON.stringify(data['agent-1'].fusion_agent_ids) === JSON.stringify(['agent-3', 'agent-2']) && JSON.stringify(data['agent-2'].fusion_agent_ids) === JSON.stringify(['agent-1']))
|
||
|
|
check('empty_selection_and_native_duty_edits_preserved', data['agent-4'].fusion_agent_ids.length === 0 && !('fusion_agent_ids' in data['agent-5']))
|
||
|
|
check('unrelated_configuration_preserved', data['agent-1'].sentinel === 'preserved')
|
||
|
|
sql(`UPDATE custom_agents SET config=config || '{"fusion_agent_ids":[],"sentinel":"owner-edit"}' WHERE id='agent-1';`)
|
||
|
|
const before = sql('SELECT jsonb_agg(to_jsonb(a) ORDER BY id) FROM custom_agents a')
|
||
|
|
run()
|
||
|
|
check('rerun_does_not_restore_user_removed_bindings', before === sql('SELECT jsonb_agg(to_jsonb(a) ORDER BY id) FROM custom_agents a'))
|
||
|
|
const cases = [[6, [999], 'missing-target'], [7, [2, 2], 'duplicate'], [8, [8], 'self'], [9, ['2'], 'invalid-id'], [10, [2], 'primary-wrong-role']]
|
||
|
|
for (const [id, fusions, label] of cases) {
|
||
|
|
fixture(id, fusions, label === 'primary-wrong-role' ? 'qc' : 'production')
|
||
|
|
const baseline = sql('SELECT jsonb_agg(to_jsonb(a) ORDER BY id) FROM custom_agents a')
|
||
|
|
let rejected = false
|
||
|
|
try { run() } catch { rejected = true }
|
||
|
|
check(`${label}_rejected_atomically`, rejected && baseline === sql('SELECT jsonb_agg(to_jsonb(a) ORDER BY id) FROM custom_agents a'))
|
||
|
|
sql(`DELETE FROM custom_agents WHERE id='agent-${id}'; DELETE FROM yawan_legacy_asset_mappings WHERE source_id=${id};`)
|
||
|
|
}
|
||
|
|
fixture(11, [2])
|
||
|
|
sql(`UPDATE yawan_legacy_asset_mappings SET source_snapshot=jsonb_set(source_snapshot,'{role}','"qc"') WHERE source_id=2;`)
|
||
|
|
let rejected = false
|
||
|
|
try { run() } catch { rejected = true }
|
||
|
|
check('nonproduction_target_rejected', rejected)
|
||
|
|
console.log(JSON.stringify({ passed: true, checks }, null, 2))
|
||
|
|
} finally {
|
||
|
|
sql(`DROP SCHEMA IF EXISTS ${schema} CASCADE;`)
|
||
|
|
}
|