78 lines
3.3 KiB
TypeScript
78 lines
3.3 KiB
TypeScript
import { once } from 'node:events'
|
|||
|
|
import path from 'node:path'
|
||
|
|
import { fileURLToPath } from 'node:url'
|
||
|
|
import pg from 'pg'
|
||
|
|
import type { CaseState } from '../src/types.js'
|
||
|
|
import { runMigrations } from './migrations.js'
|
||
|
|
|
||
|
|
const { Client } = pg
|
||
|
|
const baseDatabaseUrl = process.env.TEST_DATABASE_URL
|
||
|
|
if (!baseDatabaseUrl) throw new Error('TEST_DATABASE_URL is required for the browser harness')
|
||
|
|
|
||
|
|
const databaseName = `osint_e2e_${process.pid}_${Date.now()}`
|
||
|
|
const adminUrl = new URL(baseDatabaseUrl)
|
||
|
|
adminUrl.pathname = '/postgres'
|
||
|
|
const adminClient = new Client({ connectionString: adminUrl.toString() })
|
||
|
|
await adminClient.connect()
|
||
|
|
await adminClient.query(`CREATE DATABASE "${databaseName}"`)
|
||
|
|
|
||
|
|
const testUrl = new URL(baseDatabaseUrl)
|
||
|
|
testUrl.pathname = `/${databaseName}`
|
||
|
|
const databaseUrl = testUrl.toString()
|
||
|
|
const migrationsDir = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..', 'migrations')
|
||
|
|
await runMigrations(databaseUrl, migrationsDir, () => undefined)
|
||
|
|
|
||
|
|
const port = Number(process.env.E2E_PORT || 18788)
|
||
|
|
process.env.DATABASE_URL = databaseUrl
|
||
|
|
process.env.LEVEL_EDITING_ENABLED = 'true'
|
||
|
|
process.env.PORT = String(port)
|
||
|
|
process.env.OSINT_MANAGED_SERVER = 'true'
|
||
|
|
const { server, pool } = await import('./index.js')
|
||
|
|
if (!server.listening) await once(server, 'listening')
|
||
|
|
const baseUrl = `http://127.0.0.1:${port}`
|
||
|
|
|
||
|
|
const created = await fetch(`${baseUrl}/api/levels`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: { 'content-type': 'application/json' },
|
||
|
|
body: JSON.stringify({ id: 'e2e-level', title: 'Browser Safety Test', subtitle: 'Disposable test level' }),
|
||
|
|
})
|
||
|
|
if (!created.ok) throw new Error(`Could not create browser test level: ${created.status}`)
|
||
|
|
const state = await created.json() as CaseState
|
||
|
|
state.documents = [{
|
||
|
|
id: 'e2e-document', title: 'Dated source image', kind: 'IMAGE', date: '2021-04-17', publishedAt: '2021-04-17T12:00:00.000Z',
|
||
|
|
body: [], regions: [], fileType: 'image', metadata: {},
|
||
|
|
}]
|
||
|
|
state.evidence = [{
|
||
|
|
id: 'e2e-folder', type: 'folder', title: 'BROWSER TEST FOLDER', content: 'Disposable evidence',
|
||
|
|
x: 600, y: 360, width: 260, config: { open: false }, containedDocumentIds: ['e2e-document'],
|
||
|
|
}]
|
||
|
|
state.relations = [{
|
||
|
|
id: 'e2e-membership', fromWidgetId: 'e2e-folder', toWidgetId: 'e2e-document', type: 'contains', sortOrder: 0,
|
||
|
|
config: { x: 980, y: 360 },
|
||
|
|
}]
|
||
|
|
state.connections = []
|
||
|
|
state.viewport = { x: 0, y: 28, zoom: 0.7 }
|
||
|
|
const saved = await fetch(`${baseUrl}/api/levels/${state.id}?edit=1`, {
|
||
|
|
method: 'PUT',
|
||
|
|
headers: { 'content-type': 'application/json' },
|
||
|
|
body: JSON.stringify(state),
|
||
|
|
})
|
||
|
|
if (!saved.ok) throw new Error(`Could not seed browser test level: ${saved.status}`)
|
||
|
|
|
||
|
|
let shuttingDown = false
|
||
|
|
async function shutdown(exitCode: number) {
|
||
|
|
if (shuttingDown) return
|
||
|
|
shuttingDown = true
|
||
|
|
if (server.listening) await new Promise<void>((resolve, reject) => server.close(error => error ? reject(error) : resolve()))
|
||
|
|
await pool.end()
|
||
|
|
await adminClient.query(`DROP DATABASE IF EXISTS "${databaseName}"`)
|
||
|
|
await adminClient.end()
|
||
|
|
process.exit(exitCode)
|
||
|
|
}
|
||
|
|
|
||
|
|
process.on('SIGTERM', () => void shutdown(0))
|
||
|
|
process.on('SIGINT', () => void shutdown(0))
|
||
|
|
process.on('uncaughtException', error => { console.error(error); void shutdown(1) })
|
||
|
|
process.on('unhandledRejection', error => { console.error(error); void shutdown(1) })
|
||
|
|
console.log(`Browser safety harness ready on ${baseUrl}`)
|