36 lines
5.1 KiB
JavaScript
36 lines
5.1 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import fs from 'node:fs/promises'
|
|
import {execFileSync} from 'node:child_process'
|
|
import crypto from 'node:crypto'
|
|
import pg from 'pg'
|
|
const name=process.env.MMAPP_OWN_SQL_DB,url=process.env.DATABASE_URL,base='/app/backend/acceptance'
|
|
assert(/^mm_accept_166_\d+$/.test(name));assert.equal(new URL(url).pathname,'/'+name);assert.equal(new URL(url).username,name)
|
|
const report={date:'2026-10-13',block:'12.5.187',database:name,productionDatabaseTouched:false,productionEnvCopied:false,cases:[],scriptSha256:crypto.createHash('sha256').update(await fs.readFile(new URL(import.meta.url))).digest('hex')}
|
|
const client=new pg.Client({connectionString:url});await client.connect()
|
|
const check=async()=>{assert.equal((await client.query('SELECT current_database() AS n')).rows[0].n,name)}
|
|
const init=()=>execFileSync(process.execPath,['--input-type=module','-e',"const {initDatabase}=await import('/app/backend/dist/db/bootstrap.js');const {closePool}=await import('/app/backend/dist/db/index.js');await initDatabase();await closePool();"],{env:{...process.env,DATABASE_PATH:'/tmp/'+name+'/missing.db'},stdio:'pipe'})
|
|
const directory='/tmp/'+name;await fs.mkdir(directory);let passed=false
|
|
try{
|
|
await check();const role=(await client.query('SELECT rolsuper,rolcreatedb,rolcreaterole,rolbypassrls FROM pg_roles WHERE rolname=current_user')).rows[0];assert(Object.values(role).every(v=>v===false))
|
|
init();const at=new Date().toISOString(),earlier=new Date(Date.parse(at)-30_000).toISOString(),identity=JSON.stringify([at,'*1'])
|
|
const id=(await client.query("INSERT INTO servers(name,host,enabled,username,password,updated_at) VALUES('Own restore187','127.0.0.1',false,'fixture','fixture',$1) RETURNING id",[at])).rows[0].id
|
|
await client.query("INSERT INTO traffic_samples(server_id,interface_name,sampled_at,rx_bytes,tx_bytes,flags) VALUES($1,'ether1',$2,0,0,13),($1,'ether1',$3,1024,1024,13)",[id,earlier,at])
|
|
// An archive with the exact pre-0012 column/marker shape, constructed only in this owned database.
|
|
await check();await client.query('ALTER TABLE traffic_samples DROP COLUMN native_counter_identity');await client.query("DELETE FROM schema_migrations WHERE id='0012_traffic_counter_identity'")
|
|
const old=directory+'/legacy.dump',native=directory+'/native.dump'
|
|
const dump=file=>execFileSync('pg_dump',['--format=custom','--no-owner','--no-acl','--file',file,'--dbname',url],{stdio:'pipe'})
|
|
dump(old);init();await client.query("UPDATE traffic_samples SET native_counter_identity='mutated' WHERE server_id=$1",[id])
|
|
const {restorePartitionedDatabase}=await import('/app/backend/dist/services/system-database-partition-restore.js')
|
|
const restore=async file=>{await check();await restorePartitionedDatabase(file,url,{prepareSql:async p=>{assert(p.startsWith(directory+'/'));await fs.writeFile(p,'',{flag:'wx',mode:0o600})},command:async(cmd,args)=>{assert(['pg_restore','psql'].includes(cmd));if(cmd==='psql')assert.equal(args[args.indexOf('-d')+1],url);return execFileSync(cmd,args,{stdio:'pipe'})}})}
|
|
await restore(old);assert.equal((await client.query("SELECT count(*)::int AS n FROM information_schema.columns WHERE table_schema='public' AND table_name='traffic_samples' AND column_name='native_counter_identity'")).rows[0].n,0);report.cases.push({case:'legacy archive restores original schema',passed:true})
|
|
init();let rows=(await client.query('SELECT native_counter_identity,flags,rx_bytes FROM traffic_samples WHERE server_id=$1 ORDER BY sampled_at',[id])).rows
|
|
assert.equal(rows.length,2);assert(rows.every(r=>r.native_counter_identity===null&&r.flags===13));assert.deepEqual(rows.map(r=>Number(r.rx_bytes)),[0,1024]);report.cases.push({case:'bootstrap migrates legacy archive without guessed identities or flag rewrite',passed:true})
|
|
await client.query('UPDATE traffic_samples SET native_counter_identity=$1,flags=CASE WHEN rx_bytes=0 THEN 45 ELSE 61 END WHERE server_id=$2',[identity,id]);dump(native)
|
|
await client.query('DELETE FROM traffic_samples WHERE server_id=$1',[id]);assert.equal((await client.query('SELECT count(*)::int AS n FROM traffic_samples WHERE server_id=$1',[id])).rows[0].n,0)
|
|
await restore(native);rows=(await client.query('SELECT native_counter_identity,flags,rx_bytes FROM traffic_samples WHERE server_id=$1 ORDER BY sampled_at',[id])).rows
|
|
assert.deepEqual(rows.map(r=>r.flags),[45,61]);assert(rows.every(r=>r.native_counter_identity===identity));assert.deepEqual(rows.map(r=>Number(r.rx_bytes)),[0,1024]);report.cases.push({case:'native archive preserves identities, continuity markers and counters',passed:true})
|
|
assert.equal((await client.query("SELECT count(*)::int AS n FROM schema_migrations WHERE id='0012_traffic_counter_identity'")).rows[0].n,1)
|
|
report.cases.push({case:'partitioned archive restore and current migration remain valid',passed:true});report.clientVersion=execFileSync('pg_dump',['--version'],{encoding:'utf8'}).trim();report.serverVersion=(await client.query('SHOW server_version')).rows[0].server_version;passed=true
|
|
}catch(e){report.errorName=e.name;process.exitCode=1}
|
|
finally{await client.end();report.passed=passed;await fs.writeFile(base+'/restore187-result.json',JSON.stringify(report,null,2));console.log(JSON.stringify(report))}
|