2026-08-14 12:57:06 +02:00
import { createServer } from 'node:net'
2026-08-22 16:02:10 +02:00
import { createServer as createHttpServer , type Server as HttpServer } from 'node:http'
2026-08-14 14:09:24 +02:00
import { randomUUID } from 'node:crypto'
2026-08-22 16:02:10 +02:00
import { readFileSync } from 'node:fs'
2026-08-14 12:57:06 +02:00
import path from 'node:path'
import { fileURLToPath } from 'node:url'
import pg from 'pg'
2026-08-15 09:29:08 +02:00
import jwt from 'jsonwebtoken'
2026-08-14 12:57:06 +02:00
import { afterAll , beforeAll , describe , expect , it } from 'vitest'
2026-08-17 09:24:53 +02:00
import type { CaseState , DocumentExhibit , EventExhibit , FolderExhibit , NoteExhibit , PartyExhibit , TimelineView } from '../src/types.js'
2026-08-14 12:57:06 +02:00
import { runMigrations } from './migrations.js'
const { Client } = pg
const baseDatabaseUrl = process . env . TEST_DATABASE_URL
const suite = baseDatabaseUrl ? describe : describe.skip
const databaseName = `osint_api_test_ ${ process . pid } _ ${ Date . now () } `
let adminClient : InstanceType < typeof Client >
let appServer : Awaited < typeof import ( './index.js' ) >[ 'server' ]
let appPool : Awaited < typeof import ( './index.js' ) >[ 'pool' ]
let baseUrl = ''
2026-08-15 09:29:08 +02:00
let adminAuthorization = ''
2026-08-22 16:02:10 +02:00
let judgeServer : HttpServer
let judgeVerdict = { subject : 'target' , supports_claim :true , evidence_excerpt : 'Ada Example patented a pocket telescope' , confidence :.96 }
let judgeHttpStatus = 200
2026-08-15 09:29:08 +02:00
function adminFetch ( url : string , init : RequestInit = {}) {
const headers = new Headers ( init . headers )
headers . set ( 'authorization' , adminAuthorization )
return fetch ( url , { ... init , headers })
}
2026-08-14 12:57:06 +02:00
async function availablePort() {
return new Promise < number >(( resolve , reject ) => {
const probe = createServer ()
probe . once ( 'error' , reject )
probe . listen ( 0 , '127.0.0.1' , () => {
const address = probe . address ()
const port = typeof address === 'object' && address ? address.port : 0
probe . close ( error => error ? reject ( error ) : resolve ( port ))
})
})
}
2026-08-17 09:24:53 +02:00
const placed = ( x : number , y : number , width : number , height : number , zIndex = 1 ) => ({ x , y , width , height , rotation : 0 , zIndex , hidden : false })
suite ( 'normalized level persistence API' , () => {
2026-08-14 12:57:06 +02:00
beforeAll ( async () => {
const adminUrl = new URL ( baseDatabaseUrl ! )
adminUrl . pathname = '/postgres'
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 ()
2026-08-17 09:24:53 +02:00
await runMigrations ( databaseUrl , path . resolve ( path . dirname ( fileURLToPath ( import . meta . url )), '..' , 'migrations' ), () => undefined )
2026-08-14 12:57:06 +02:00
const port = await availablePort ()
process . env . DATABASE_URL = databaseUrl
process . env . LEVEL_EDITING_ENABLED = 'true'
2026-08-15 09:29:08 +02:00
process . env . JWT_SECRET = 'osint-integration-jwt-secret'
2026-08-17 09:24:53 +02:00
process . env . ASSET_STORAGE_DRIVER = 'memory'
2026-08-14 12:57:06 +02:00
process . env . PORT = String ( port )
2026-08-22 16:02:10 +02:00
judgeServer = createHttpServer (( _req , res ) => {
res . statusCode = judgeHttpStatus
res . setHeader ( 'content-type' , 'application/json' )
res . end ( JSON . stringify ({ content : [{ type : 'tool_use' , name : 'record_evidence_verdict' , input :judgeVerdict }] }))
})
await new Promise < void >(( resolve , reject ) => judgeServer . listen ( 0 , '127.0.0.1' , resolve ). once ( 'error' , reject ))
const judgeAddress = judgeServer . address ()
process . env . EVIDENCE_JUDGE_PROVIDER = 'anthropic'
process . env . EVIDENCE_JUDGE_MODEL = 'integration-haiku'
process . env . ANTHROPIC_API_KEY = 'integration-key'
process . env . ANTHROPIC_API_URL = `http://127.0.0.1: ${ typeof judgeAddress === 'object' && judgeAddress ? judgeAddress.port : 0 } `
2026-08-14 12:57:06 +02:00
const serverModule = await import ( './index.js' )
appServer = serverModule . server
appPool = serverModule . pool
baseUrl = `http://127.0.0.1: ${ port } `
2026-08-15 09:29:08 +02:00
adminAuthorization = `Bearer ${ jwt . sign ({ sub : 'integration-admin' , role : 'admin' } , process.env.JWT_SECRET)}`
2026-08-14 12:57:06 +02:00
})
afterAll ( async () => {
if ( appServer ) await new Promise < void >(( resolve , reject ) => appServer . close ( error => error ? reject ( error ) : resolve ()))
2026-08-22 16:02:10 +02:00
if ( judgeServer ) await new Promise < void >(( resolve , reject ) => judgeServer . close ( error => error ? reject ( error ) : resolve ()))
2026-08-14 12:57:06 +02:00
if ( appPool ) await appPool . end ()
if ( ! adminClient ) return
await adminClient . query ( `DROP DATABASE IF EXISTS " ${ databaseName } "` )
await adminClient . end ()
})
2026-08-17 09:24:53 +02:00
it ( 'round-trips exhibits, relations, board views, private objects, and template clones' , async () => {
2026-08-15 09:29:08 +02:00
expect ( await ( await fetch ( ` ${ baseUrl } /api/session` )). json ()). toEqual ({ authenticated : false , isAdmin : false })
const createResponse = await adminFetch ( ` ${ baseUrl } /api/levels` , {
2026-08-17 09:24:53 +02:00
method : 'POST' , headers : { 'content-type' : 'application/json' }, body : JSON.stringify ({ id : 'api-smoke-level' , title : 'API Smoke Level' }),
2026-08-14 12:57:06 +02:00
})
expect ( createResponse . status ). toBe ( 201 )
const state = await createResponse . json () as CaseState
2026-08-17 09:24:53 +02:00
const timeline = state . views . find (( view ) : view is TimelineView => view . type === 'timeline' ) !
timeline . rangeMode = 'fixed'
timeline . range = { start : '2021-04-01' , end : '2021-04-30' }
2026-08-14 12:57:06 +02:00
state . viewport = { x : 91 , y : - 42 , zoom : 0.85 }
2026-08-17 09:24:53 +02:00
const document : DocumentExhibit = { id : randomUUID (), type : 'document' , title : 'Evidence' , publishedAt : '2021-04-17T12:00:00Z' , body : [ 'Extracted body' ], regions : [{ id : 'stamp' , label : 'Date stamp' , excerpt : '17 April 2021' , date : '2021-04-17T09:00:00Z' }], fileType : 'image' , metadata : {}, ... placed ( 1051 , 417 , 174 , 145 , 2 ) }
2026-08-22 14:53:23 +02:00
const gatedDocument : DocumentExhibit = { id : randomUUID (), type : 'document' , title : 'Later tip' , body : [], regions : [], fileType : 'image' , metadata : {}, requiredFlags : [ 'tip.received' ], ... placed ( 1260 , 417 , 174 , 145 , 3 ) }
2026-08-17 09:24:53 +02:00
const folder : FolderExhibit = { id : randomUUID (), type : 'folder' , title : 'Folder' , content : 'Evidence folder' , isOpen : true , ... placed ( 685 , 417 , 260 , 166 ) }
const note : NoteExhibit = { id : randomUUID (), type : 'note' , title : 'Extract' , content : 'Date matters' , ... placed ( 420 , 300 , 108 , 154 ) }
const event : EventExhibit = { id : randomUUID (), type : 'event' , title : 'The meeting occurred' , content : 'The evidence places the meeting on 18 April.' , eventDate : '2021-04-18T14:30:00Z' , ... placed ( 520 , 610 , 270 , 174 ) }
const party : PartyExhibit = { id : randomUUID (), type : 'party' , partyKind : 'person' , title : 'Ada Lovelace' , content : 'Named as correspondent.' , aliases : [ 'A. A. L.' ], ... placed ( 720 , 250 , 280 , 190 ) }
2026-08-22 14:53:23 +02:00
state . exhibits = [ document , gatedDocument , folder , note , event , party ]
2026-08-17 09:24:53 +02:00
state . relations = [
{ id : randomUUID (), fromExhibitId : folder.id , toExhibitId : document.id , type : 'contains' , sortOrder : 0 },
{ id : randomUUID (), fromExhibitId : note.id , toExhibitId : document.id , type : 'source' , sourceRegionId : 'stamp' , sortOrder : 0 },
{ id : randomUUID (), fromExhibitId : event.id , toExhibitId : document.id , type : 'supports' , sortOrder : 0 },
{ id : randomUUID (), fromExhibitId : event.id , toExhibitId : note.id , type : 'supports' , sortOrder : 1 },
{ id : randomUUID (), fromExhibitId : party.id , toExhibitId : document.id , type : 'concerns' , sortOrder : 0 },
2026-08-14 14:17:54 +02:00
]
2026-08-17 09:24:53 +02:00
state . connections = [{ id : randomUUID (), fromExhibitId : folder.id , toExhibitId : document.id , label : 'Primary source' , tightness : 85 , tagStyle : 'compact' , tagPosition : 72 , tagOffset : - 12 }]
state . brief = { body : 'Identify Ada Lovelace.' , concepts : [{ id : randomUUID (), label : 'Ada Lovelace' , context : 'Named in evidence.' , expectedPartyKind : 'person' , resolvedPartyExhibitId : party.id }] }
2026-08-14 12:57:06 +02:00
2026-08-17 09:24:53 +02:00
const save = await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } ?edit=1` , { method : 'PUT' , headers : { 'content-type' : 'application/json' }, body : JSON.stringify ( state ) })
expect ( await save . json ()). toEqual ({ ok : true , mode : 'author' })
2026-08-15 09:29:08 +02:00
const loaded = await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } ?edit=1` )). json () as CaseState
2026-08-17 09:24:53 +02:00
expect ( loaded . views [ 0 ]). toMatchObject ({ type : 'timeline' , rangeMode : 'fixed' , range : timeline.range })
expect ( loaded . exhibits . find ( item => item . id === folder . id )). toMatchObject ({ x : 685 , y : 417 , isOpen : true })
expect ( loaded . relations ). toEqual ( expect . arrayContaining ([ expect . objectContaining ({ type : 'supports' , fromExhibitId : event.id , toExhibitId : note.id })]))
expect ( loaded . connections [ 0 ]). toMatchObject ({ fromExhibitId : folder.id , toExhibitId : document.id , label : 'Primary source' })
2026-08-22 14:53:23 +02:00
expect ( loaded . exhibits . find ( item => item . id === gatedDocument . id )). toMatchObject ({ requiredFlags : [ 'tip.received' ] })
const beforeFlag = await ( await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } ` )). json () as CaseState
expect ( beforeFlag . exhibits . map ( item => item . id )). not . toContain ( gatedDocument . id )
expect ( beforeFlag . newlyVisibleDocumentIds ). toContain ( document . id )
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /flags` )). json ()). toEqual ([
{ key : 'tip.received' , gatedDocumentCount : 1 },
])
expect (( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /flags/tip.received` , { method : 'PUT' })). status ). toBe ( 200 )
const afterFlag = await ( await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } ` )). json () as CaseState
expect ( afterFlag . exhibits . map ( item => item . id )). toContain ( gatedDocument . id )
expect ( afterFlag . newlyVisibleDocumentIds ). toContain ( gatedDocument . id )
expect (( await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /reveals/seen` , { method : 'POST' , headers : { 'content-type' : 'application/json' }, body : JSON.stringify ({ documentIds : afterFlag.newlyVisibleDocumentIds }) })). status ). toBe ( 200 )
expect (( await ( await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } ` )). json () as CaseState ). newlyVisibleDocumentIds ). toEqual ([])
expect (( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /flags/tip.received` , { method : 'DELETE' })). status ). toBe ( 200 )
const matchRuleResponse = await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /evidence-match-rules` , {
method : 'POST' , headers : { 'content-type' : 'application/json' }, body : JSON.stringify ({
2026-08-22 16:02:10 +02:00
name : 'Smoke source passage' , sourceLabel : 'Archive smoke test' , sourceUri : 'https://example.test/archive/smoke' ,
flagKey : 'tip.received' , minimumAnchorMatches : 1 ,
2026-08-22 14:53:23 +02:00
anchors : [{ phrase : 'OSINT smoke evidence from the archive' , minimumSimilarity : 0.72 }],
}),
})
expect ( matchRuleResponse . status ). toBe ( 201 )
2026-08-22 16:02:10 +02:00
expect ( await matchRuleResponse . json ()). toMatchObject ({ name : 'Smoke source passage' , sourceLabel : 'Archive smoke test' ,
sourceUri : 'https://example.test/archive/smoke' , flagKey : 'tip.received' , anchors : [{ phrase : 'OSINT smoke evidence from the archive' }] })
2026-08-22 14:53:23 +02:00
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /evidence-match-rules` )). json ()). toHaveLength ( 1 )
2026-08-22 16:02:10 +02:00
const goalResponse = await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /goals` , {
method : 'POST' , headers : { 'content-type' : 'application/json' }, body : JSON.stringify ({
key : 'smoke.prove-source' , title : 'Prove the source' , instructions : 'Paste a matching archival source.' ,
completionMessage : 'Source verified.' , requiredFlags : [ 'tip.received' ],
}),
})
expect ( goalResponse . status ). toBe ( 201 )
expect ( await goalResponse . json ()). toMatchObject ({ key : 'smoke.prove-source' , status : 'pending' , requiredFlags : [ 'tip.received' ] })
const playGoalBefore = ( await ( await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } ` )). json () as CaseState ). goals [ 0 ]
expect ( playGoalBefore ). toMatchObject ({ key : 'smoke.prove-source' , status : 'pending' , newlyCompleted : false })
expect ( playGoalBefore ). not . toHaveProperty ( 'id' )
expect ( playGoalBefore ). not . toHaveProperty ( 'requiredFlags' )
const semanticGoalResponse = await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /goals` , {
method : 'POST' , headers : { 'content-type' : 'application/json' }, body :JSON.stringify ({ key : 'smoke.semantic-proof' , title : 'Prove the semantic claim' ,
instructions : 'Upload another credible source.' , completionMessage : 'Claim verified.' , requiredFlags : [ 'semantic.proved' ] }),
})
expect ( semanticGoalResponse . status ). toBe ( 201 )
const semanticGoal = await semanticGoalResponse . json () as { id :string }
const semanticRuleResponse = await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /evidence-semantic-rules` , {
method : 'POST' , headers : { 'content-type' : 'application/json' }, body :JSON.stringify ({ goalId :semanticGoal.id , name : 'Ada inventor claim' ,
targetSubject : 'Ada Example' , relatedSubject : 'Ada Example Senior' , assertion : 'Ada Example was an inventor' , successFlagKey : 'semantic.proved' ,
relatedFlagKey : 'semantic.father' , minimumConfidence :.85 }),
})
expect ( semanticRuleResponse . status ). toBe ( 201 )
2026-08-14 13:43:37 +02:00
const upload = new FormData ()
2026-08-22 14:53:23 +02:00
upload . append ( 'file' , new Blob ([ 'OSINT smoke evidence from the archlve' ], { type : 'text/plain' }), 'smoke-evidence.txt' )
upload . append ( 'x' , '812' )
upload . append ( 'y' , '438' )
const uploadResponse = await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /documents` , { method : 'POST' , body : upload })
2026-08-14 13:43:37 +02:00
expect ( uploadResponse . status ). toBe ( 201 )
2026-08-22 16:02:10 +02:00
const uploaded = await uploadResponse . json () as DocumentExhibit & { analysis : { extractionStatus : string ; matchedFlags : string []; awardedFlags : string []; goals : CaseState [ 'goals' ] } }
2026-08-22 14:53:23 +02:00
expect ( uploaded ). toMatchObject ({ type : 'document' , fileName : 'smoke-evidence.txt' , fileType : 'text' , x : 812 , y : 438 ,
2026-08-22 16:02:10 +02:00
body : [ 'OSINT smoke evidence from the archlve' ], analysis : { extractionStatus : 'succeeded' , matchedFlags : [ 'tip.received' ], awardedFlags : [ 'tip.received' ],
goals : expect.arrayContaining ([ expect . objectContaining ({ key : 'smoke.prove-source' , status : 'complete' , newlyCompleted : true })]) } })
2026-08-22 14:53:23 +02:00
expect ( await ( await fetch ( ` ${ baseUrl } /api/assets/ ${ uploaded . assetId } ` )). text ()). toBe ( 'OSINT smoke evidence from the archlve' )
2026-08-17 09:24:53 +02:00
const assetRow = await appPool . query < { storage_provider : string ; content : Buffer | null ; object_key : string | null } > ( 'SELECT storage_provider,content,object_key FROM osint.assets WHERE id=$1' , [ uploaded . assetId ])
expect ( assetRow . rows [ 0 ]). toMatchObject ({ storage_provider : 's3' , content : null , object_key : expect.stringMatching ( /^assets\// ) })
2026-08-22 14:53:23 +02:00
const automaticallyRevealed = await ( await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } ` )). json () as CaseState
expect ( automaticallyRevealed . exhibits . map ( item => item . id )). toContain ( gatedDocument . id )
2026-08-22 16:02:10 +02:00
expect ( automaticallyRevealed . goals ). toEqual ( expect . arrayContaining ([
expect . objectContaining ({ key : 'smoke.prove-source' , status : 'complete' , newlyCompleted : false }),
]))
2026-08-22 14:53:23 +02:00
const evaluationRows = await appPool . query < { matched : boolean ; matched_anchor_count : number } > (
'SELECT matched,matched_anchor_count FROM osint.evidence_match_evaluations WHERE document_exhibit_id=$1' , [ uploaded . id ])
expect ( evaluationRows . rows ). toEqual ([{ matched : true , matched_anchor_count : 1 }])
2026-08-22 16:02:10 +02:00
const semanticUpload = new FormData ()
semanticUpload . append ( 'file' , new Blob ([ 'Archive entry: Ada Example patented a pocket telescope in 1948.' ], { type : 'text/plain' }), 'semantic-evidence.txt' )
const semanticDocumentResponse = await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /documents` , { method : 'POST' , body :semanticUpload })
expect ( semanticDocumentResponse . status ). toBe ( 201 )
const semanticDocument = await semanticDocumentResponse . json () as DocumentExhibit & { analysis : { goals :CaseState [ 'goals' ] } }
expect ( semanticDocument . analysis . goals . find ( goal => goal . key === 'smoke.semantic-proof' ) ? . status ). toBe ( 'pending' )
const judgedResponse = await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /documents/ ${ semanticDocument . id } /judge` , { method : 'POST' })
expect ( judgedResponse . status ). toBe ( 200 )
expect ( await judgedResponse . json ()). toMatchObject ({ status : 'succeeded' , subject : 'target' , supportsClaim :true , confidence :.96 ,
awardedFlags : [ 'semantic.proved' ], goals :expect.arrayContaining ([ expect . objectContaining ({ key : 'smoke.semantic-proof' , status : 'complete' , newlyCompleted :true })]) })
const semanticProvenance = await appPool . query < { flag_key :string ; awarded_by_semantic_evaluation_id :string | null } > (
'SELECT flag_key,awarded_by_semantic_evaluation_id FROM osint.level_flags WHERE level_id=(SELECT id FROM osint.levels WHERE slug=$1) AND flag_key=$2' ,
[ state . id , 'semantic.proved' ])
expect ( semanticProvenance . rows [ 0 ]). toMatchObject ({ flag_key : 'semantic.proved' , awarded_by_semantic_evaluation_id :expect.any ( String ) })
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /documents/ ${ semanticDocument . id } /judge` , { method : 'POST' })). json ())
. toMatchObject ({ status : 'not_needed' , awardedFlags : [] })
2026-08-22 14:53:23 +02:00
const screenshot = new FormData ()
screenshot . append ( 'file' , new Blob ([ Buffer . from ( '89504e470d0a1a0a' , 'hex' )], { type : 'image/png' }), 'Screenshot 2026-08-22.png' )
const screenshotResponse = await fetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /documents` , { method : 'POST' , body : screenshot })
expect ( screenshotResponse . status ). toBe ( 201 )
expect ( await screenshotResponse . json ()). toMatchObject ({ type : 'document' , fileType : 'image' , fileName : 'Screenshot 2026-08-22.png' })
2026-08-14 13:43:37 +02:00
2026-08-17 09:24:53 +02:00
const templateResponse = await adminFetch ( ` ${ baseUrl } /api/levels/ ${ state . id } /templates?edit=1` , { method : 'POST' , headers : { 'content-type' : 'application/json' }, body : JSON.stringify ({ name : 'Smoke Template' }) })
2026-08-14 14:17:54 +02:00
expect ( templateResponse . status ). toBe ( 201 )
2026-08-17 09:24:53 +02:00
const cloneResponse = await adminFetch ( ` ${ baseUrl } /api/templates/smoke-template/levels?edit=1` , { method : 'POST' , headers : { 'content-type' : 'application/json' }, body : JSON.stringify ({ id : 'smoke-template-copy' , title : 'Playable copy' }) })
2026-08-14 14:17:54 +02:00
expect ( cloneResponse . status ). toBe ( 201 )
const clone = await cloneResponse . json () as CaseState
2026-08-17 09:24:53 +02:00
expect ( clone . views [ 0 ]). toMatchObject ({ type : 'timeline' , rangeMode : 'fixed' , range : timeline.range })
expect ( clone . exhibits . map ( item => item . id )). not . toContain ( folder . id )
expect ( clone . exhibits . find ( item => item . type === 'folder' )). toMatchObject ({ x : 685 , y : 417 })
expect ( clone . relations . filter ( relation => relation . type === 'supports' )). toHaveLength ( 2 )
expect ( clone . connections [ 0 ]). toMatchObject ({ label : 'Primary source' , tightness : 85 })
expect ( clone . brief . concepts [ 0 ]. resolvedPartyExhibitId ). not . toBe ( party . id )
2026-08-22 14:53:23 +02:00
const authoredClone = await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ clone . id } ?edit=1` )). json () as CaseState
expect ( authoredClone . exhibits . find ( item => item . type === 'document' && item . title === 'Later tip' )). toMatchObject ({ requiredFlags : [ 'tip.received' ] })
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ clone . id } /evidence-match-rules` )). json ()). toEqual ([
2026-08-22 16:02:10 +02:00
expect . objectContaining ({ name : 'Smoke source passage' , sourceLabel : 'Archive smoke test' , sourceUri : 'https://example.test/archive/smoke' ,
flagKey : 'tip.received' , anchors : [ expect . objectContaining ({ phrase : 'OSINT smoke evidence from the archive' })] }),
])
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ clone . id } /goals` )). json ()). toEqual ( expect . arrayContaining ([
expect . objectContaining ({ key : 'smoke.prove-source' , status : 'pending' , requiredFlags : [ 'tip.received' ] }),
expect . objectContaining ({ key : 'smoke.semantic-proof' , status : 'pending' , requiredFlags : [ 'semantic.proved' ] }),
]))
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ clone . id } /evidence-semantic-rules` )). json ()). toEqual ([
expect . objectContaining ({ name : 'Ada inventor claim' , goalKey : 'smoke.semantic-proof' , successFlagKey : 'semantic.proved' }),
2026-08-22 14:53:23 +02:00
])
2026-08-14 12:57:06 +02:00
})
2026-08-22 16:02:10 +02:00
it ( 'imports the data-defined Scene 7 template with its private recognition rules' , async () => {
const { importMysteryTemplate } = await import ( '../scripts/importMysteryTemplate.js' )
const manifestPath = path . resolve ( path . dirname ( fileURLToPath ( import . meta . url )), '..' , 'mysteries' , 'barricelli-scene-7' , 'mystery.json' )
const imported = await importMysteryTemplate ( manifestPath , baseUrl , adminAuthorization . replace ( /^Bearer / , '' ))
expect ( imported . playableLevel ). toMatchObject ({ title : 'The Barricelli Files' , exhibits : [], goals : [ expect . objectContaining ({
key : 'barricelli.inventor-proof' , status : 'pending' , newlyCompleted :false ,
})] })
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ imported . playableLevel . id } /evidence-match-rules` )). json ()). toEqual ([
expect . objectContaining ({ name : 'Google Patents · GB695913A' , sourceUri : 'https://patents.google.com/patent/GB695913A/en' ,
flagKey : 'scene7.nils_inventor_proved' , minimumAnchorMatches :2 }),
])
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ imported . playableLevel . id } /evidence-semantic-rules` )). json ()). toEqual ([
expect . objectContaining ({ goalKey : 'barricelli.inventor-proof' , targetSubject : 'Nils Aall Barricelli' ,
relatedFlagKey : 'scene7.father_inventor_discovered' }),
])
const fixtureDir = path . join ( path . dirname ( manifestPath ), 'fixtures' )
const fatherUpload = new FormData ()
fatherUpload . append ( 'file' , new Blob ([ readFileSync ( path . join ( fixtureDir , 'father-only-negative-ocr.txt' ))], { type : 'text/plain' }), 'father-source.txt' )
const fatherDocument = await ( await fetch ( ` ${ baseUrl } /api/levels/ ${ imported . playableLevel . id } /documents` , { method : 'POST' , body :fatherUpload })). json () as DocumentExhibit & { analysis : { goals :CaseState [ 'goals' ] } }
expect ( fatherDocument . analysis . goals [ 0 ]. status ). toBe ( 'pending' )
judgeVerdict = { subject : 'related' , supports_claim :true , evidence_excerpt : 'den italienske maler og opfinder Barricelli' , confidence :.96 }
judgeHttpStatus = 429
expect ( await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ imported . playableLevel . id } /documents/ ${ fatherDocument . id } /judge` , { method : 'POST' })). json ())
. toMatchObject ({ status : 'failed' , retryable :true , awardedFlags : [], goals : [ expect . objectContaining ({ status : 'pending' })] })
judgeHttpStatus = 200
const fatherJudgment = await ( await adminFetch ( ` ${ baseUrl } /api/levels/ ${ imported . playableLevel . id } /documents/ ${ fatherDocument . id } /judge` , { method : 'POST' })). json ()
expect ( fatherJudgment ). toMatchObject ({ status : 'succeeded' , subject : 'related' , awardedFlags : [ 'scene7.father_inventor_discovered' ],
goals : [ expect . objectContaining ({ key : 'barricelli.inventor-proof' , status : 'pending' })] })
const targetUpload = new FormData ()
targetUpload . append ( 'file' , new Blob ([ readFileSync ( path . join ( fixtureDir , 'google-patents-target-ocr.txt' ))], { type : 'text/plain' }), 'google-patents-source.txt' )
const targetResponse = await fetch ( ` ${ baseUrl } /api/levels/ ${ imported . playableLevel . id } /documents` , { method : 'POST' , body :targetUpload })
expect ( targetResponse . status ). toBe ( 201 )
expect ( await targetResponse . json ()). toMatchObject ({ analysis : { awardedFlags : [ 'scene7.nils_inventor_proved' ],
goals : [ expect . objectContaining ({ key : 'barricelli.inventor-proof' , status : 'complete' , newlyCompleted :true })] } })
judgeVerdict = { subject : 'target' , supports_claim :true , evidence_excerpt : 'Ada Example patented a pocket telescope' , confidence :.96 }
judgeHttpStatus = 200
})
2026-08-14 12:57:06 +02:00
})