Anydoc / anydoc parser engine (push) Canceled after 0s
App / Format, vet, test, and build (push) Canceled after 0s
CLI / test (macos-latest / go 1.26) (push) Canceled after 0s
CLI / test (ubuntu-latest / go 1.26) (push) Canceled after 0s
CLI / test (windows-latest / go 1.26) (push) Canceled after 0s
Build and Push Docker Image / build-ui (push) Canceled after 0s
Build and Push Docker Image / build-docreader (push) Canceled after 0s
Build and Push Docker Image / build-sandbox (push) Canceled after 0s
Build and Push Docker Image / build-app (amd64, linux/amd64, ubuntu-latest) (push) Canceled after 0s
Build and Push Docker Image / build-app (arm64, linux/arm64, ubuntu-24.04-arm) (push) Canceled after 0s
DocReader / Python tests and Go client tests (push) Canceled after 0s
Build & Publish (dsh-weknora) / Typecheck, test, pack (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (pinned) (push) Canceled after 0s
Build & Publish (dsh-weknora) / End-to-end inside dsh (latest, advisory) (push) Canceled after 0s
Frontend / Test, type-check, and build (push) Canceled after 0s
Go Lint Cache / warm lint cache (.) (push) Canceled after 0s
Go Lint Cache / warm lint cache (cli) (push) Canceled after 0s
Go Lint Cache / warm lint cache (client) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.10) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.11) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.12) (push) Canceled after 0s
Build & Publish (mcp-server) / Test (Python 3.13) (push) Canceled after 0s
Build and Push Docker Image / merge (push) Canceled after 0s
Build & Publish (dsh-weknora) / Publish to npm (push) Canceled after 0s
Build & Publish (mcp-server) / Build sdist + wheel (push) Canceled after 0s
Build & Publish (mcp-server) / Publish to PyPI (push) Canceled after 0s
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;`)
|
|
}
|