const assert = require('node:assert/strict'); const { readFileSync } = require('node:fs'); const { resolve } = require('node:path'); const { spawnSync } = require('node:child_process'); const container = `lms-ai-provider-migration-${process.pid}`; const run = (command, args, input) => { const result = spawnSync(command, args, { input, encoding: 'utf8', timeout: 30000 }); assert.equal(result.status, 0, result.stderr || result.error?.message); return result.stdout; }; const migration = name => readFileSync(resolve(__dirname, '../prisma/migrations', name, 'migration.sql'), 'utf8'); try { run('docker', ['run', '-d', '--rm', '--name', container, '--network', 'none', '-e', 'POSTGRES_HOST_AUTH_METHOD=trust', 'postgres:16-alpine']); let ready = false; for (let attempt = 0; attempt < 100; attempt++) { if (spawnSync('docker', ['exec', container, 'pg_isready', '-h', '127.0.0.1', '-U', 'postgres'], { stdio: 'ignore' }).status === 0) { ready = true; break; } Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, 100); } assert(ready, 'Isolated test PostgreSQL did not start'); const sql = ` CREATE TABLE courses (id uuid PRIMARY KEY); ${migration('20261014160000_add_course_ai_settings')} ${migration('20261014170000_add_course_ai_endpoint')} INSERT INTO courses VALUES ('00000000-0000-0000-0000-000000000001'); INSERT INTO course_ai_settings (id, course_id, enabled, provider, model, encrypted_api_key, endpoint_url, updated_at) VALUES ('00000000-0000-0000-0000-000000000002', '00000000-0000-0000-0000-000000000001', true, 'QWEN', 'qwen3.8-flash', 'preserved-key', 'https://example.test/v1', '2026-10-19'); CREATE TEMP TABLE original AS TABLE course_ai_settings; ${migration('20261020160000_allow_course_ai_providers')} DO $$ BEGIN IF EXISTS ((SELECT * FROM original EXCEPT SELECT * FROM course_ai_settings) UNION ALL (SELECT * FROM course_ai_settings EXCEPT SELECT * FROM original)) THEN RAISE EXCEPTION 'Existing provider setting was changed'; END IF; END $$; INSERT INTO course_ai_settings (id, course_id, provider, model, updated_at) VALUES ('00000000-0000-0000-0000-000000000003', '00000000-0000-0000-0000-000000000001', 'OPENAI', 'gpt-6.1-sol', now()); DO $$ BEGIN IF (SELECT count(*) FROM course_ai_settings) <> 2 THEN RAISE EXCEPTION 'Provider settings are not independent'; END IF; IF (SELECT enabled FROM course_ai_settings WHERE provider = 'OPENAI') THEN RAISE EXCEPTION 'New providers must default to disabled'; END IF; BEGIN INSERT INTO course_ai_settings (id, course_id, provider, model, updated_at) VALUES ('00000000-0000-0000-0000-000000000004', '00000000-0000-0000-0000-000000000001', 'OPENAI', 'duplicate', now()); RAISE EXCEPTION 'Duplicate provider was accepted'; EXCEPTION WHEN unique_violation THEN NULL; END; END $$; DELETE FROM courses; DO $$ BEGIN IF EXISTS (SELECT FROM course_ai_settings) THEN RAISE EXCEPTION 'Cascade relation was lost'; END IF; END $$; `; run('docker', ['exec', '-i', container, 'psql', '-h', '127.0.0.1', '-U', 'postgres', '-v', 'ON_ERROR_STOP=1'], sql); console.log('PASS: migration preserves settings and keys, allows multiple providers, defaults new providers to disabled, rejects duplicates, and retains cascade deletion'); } finally { spawnSync('docker', ['rm', '-f', container], { stdio: 'ignore' }); }