Refine board notes, clipping evidence, and case reports
This commit is contained in:
@@ -104,7 +104,7 @@ suite('normalized level persistence API', () => {
|
||||
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', captureKind:'full_page',metadata: {}, ...placed(1051, 417, 205, 282, 2) }
|
||||
const gatedDocument: DocumentExhibit = { id: randomUUID(), type: 'document', title: 'Later tip', body: [], regions: [], fileType: 'image', captureKind:'clipping',metadata: {}, requiredFlags: ['tip.received'], ...placed(1260, 417, 210, 178, 3) }
|
||||
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 note: NoteExhibit = { id:randomUUID(),type:'note',title:'Extract',content:'Date matters',presentation:'lined_sheet',...placed(420,300,220,270) }
|
||||
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) }
|
||||
state.exhibits = [document, gatedDocument, folder, note, event, party]
|
||||
@@ -123,6 +123,7 @@ suite('normalized level persistence API', () => {
|
||||
const loaded = await (await adminFetch(`${baseUrl}/api/levels/${state.id}?edit=1`)).json() as CaseState
|
||||
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.exhibits.find(item => item.id === note.id)).toMatchObject({ presentation:'lined_sheet',width:220,height:270 })
|
||||
expect(loaded.exhibits.find(item => item.id === document.id)).toMatchObject({ captureKind:'full_page',width:205,height:282 })
|
||||
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' })
|
||||
@@ -234,6 +235,7 @@ suite('normalized level persistence API', () => {
|
||||
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.exhibits.find(item => item.type === 'note')).toMatchObject({ presentation:'lined_sheet',width:220,height:270 })
|
||||
expect(clone.brief.concepts[0].resolvedPartyExhibitId).not.toBe(party.id)
|
||||
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({ captureKind:'clipping',requiredFlags: ['tip.received'] })
|
||||
@@ -330,7 +332,7 @@ suite('normalized level persistence API', () => {
|
||||
relatedState.connections.push({ id:relatedConnectionId,fromExhibitId:relatedClaim.id,toExhibitId:fatherDocument.id,label:'Proof that the Barricelli family included an inventor.',tightness:65,tagStyle:'luggage',tagPosition:50,tagOffset:0 })
|
||||
expect((await fetch(`${baseUrl}/api/levels/${relatedState.id}`,{ method:'PUT',headers:{'content-type':'application/json'},body:JSON.stringify(relatedState) })).status).toBe(200)
|
||||
const relatedSubmission=await (await fetch(`${baseUrl}/api/levels/${relatedState.id}/report/submissions`,{ method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({
|
||||
investigatorName:'Test Player',evidence:[{ connectionId:relatedConnectionId,documentExhibitId:fatherDocument.id,relationText:'Proof that the Barricelli family included an inventor.' }],
|
||||
investigatorName:'Test Player',
|
||||
}) })).json()
|
||||
expect(relatedSubmission).toMatchObject({ status:'evidence_insufficient',issues:expect.arrayContaining(['missing_accepted_evidence','connected_evidence_unverified']),
|
||||
feedback:expect.stringContaining('related person rather than the claim subject'),claims:[expect.objectContaining({ evidence:[expect.objectContaining({
|
||||
@@ -350,14 +352,24 @@ suite('normalized level persistence API', () => {
|
||||
reportState.connections.push({ id:connectionId,fromExhibitId:claim.id,toExhibitId:targetDocument.id,label:'Proof that…',tightness:65,tagStyle:'luggage',tagPosition:50,tagOffset:0 })
|
||||
expect((await fetch(`${baseUrl}/api/levels/${reportState.id}`,{ method:'PUT',headers:{'content-type':'application/json'},body:JSON.stringify(reportState) })).status).toBe(200)
|
||||
const incomplete=await fetch(`${baseUrl}/api/levels/${reportState.id}/report/submissions`,{ method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({
|
||||
investigatorName:'Test Player',evidence:[{ connectionId,documentExhibitId:targetDocument.id,relationText:'Proof that…' }],
|
||||
investigatorName:'Test Player',
|
||||
// Legacy/forged report fields must not mutate board-owned provenance.
|
||||
evidence:[{ connectionId,documentExhibitId:targetDocument.id,relationText:'Forged complete statement',publishedAt:'1953-08-19',sourceCitation:'Forged source' }],
|
||||
}) })
|
||||
expect(incomplete.status).toBe(201)
|
||||
expect(await incomplete.json()).toMatchObject({ status:'evidence_accepted_report_incomplete',issues:expect.arrayContaining(['unfinished_relation','missing_date','missing_source']),
|
||||
feedback:"The evidence is good enough, but the report itself won't hold up in court. Add the date, cite the source, and provide the link if you can. Then we can accept it." })
|
||||
const targetOnBoard=reportState.exhibits.find(exhibit => exhibit.type === 'document' && exhibit.id === targetDocument.id)
|
||||
if (!targetOnBoard || targetOnBoard.type !== 'document') throw new Error('Target document missing from board')
|
||||
targetOnBoard.publishedAt='1953-08-19T00:00:00.000Z'
|
||||
targetOnBoard.sourceCitation='Google Patents · GB695913A'
|
||||
targetOnBoard.sourceUri='https://patents.google.com/patent/GB695913A/en'
|
||||
const targetConnection=reportState.connections.find(connection => connection.id === connectionId)
|
||||
if (!targetConnection) throw new Error('Target connection missing from board')
|
||||
targetConnection.label='Proof that Barricelli is named as the inventor on patent GB695913A.'
|
||||
expect((await fetch(`${baseUrl}/api/levels/${reportState.id}`,{ method:'PUT',headers:{'content-type':'application/json'},body:JSON.stringify(reportState) })).status).toBe(200)
|
||||
const accepted=await fetch(`${baseUrl}/api/levels/${reportState.id}/report/submissions`,{ method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({
|
||||
investigatorName:'Test Player',evidence:[{ connectionId,documentExhibitId:targetDocument.id,relationText:'Proof that Barricelli is named as the inventor on patent GB695913A.',
|
||||
publishedAt:'1953-08-19',sourceCitation:'Google Patents · GB695913A',sourceUri:'https://patents.google.com/patent/GB695913A/en' }],
|
||||
investigatorName:'Test Player',
|
||||
}) })
|
||||
expect(accepted.status).toBe(201)
|
||||
const acceptedBody=await accepted.json()
|
||||
@@ -376,10 +388,14 @@ suite('normalized level persistence API', () => {
|
||||
const repeatedState=await (await fetch(`${baseUrl}/api/levels/${reportState.id}`)).json() as CaseState
|
||||
const repeatedConnectionId=randomUUID()
|
||||
repeatedState.connections.push({ id:repeatedConnectionId,fromExhibitId:claim.id,toExhibitId:repeatedDocument.id,label:'Proof that Barricelli is named as the inventor on patent GB695913A.',tightness:65,tagStyle:'luggage',tagPosition:50,tagOffset:0 })
|
||||
const repeatedOnBoard=repeatedState.exhibits.find(exhibit => exhibit.type === 'document' && exhibit.id === repeatedDocument.id)
|
||||
if (!repeatedOnBoard || repeatedOnBoard.type !== 'document') throw new Error('Repeated document missing from board')
|
||||
repeatedOnBoard.publishedAt='1953-08-19T00:00:00.000Z'
|
||||
repeatedOnBoard.sourceCitation='Google Patents · GB695913A'
|
||||
repeatedOnBoard.sourceUri='https://patents.google.com/patent/GB695913A/en'
|
||||
expect((await fetch(`${baseUrl}/api/levels/${repeatedState.id}`,{ method:'PUT',headers:{'content-type':'application/json'},body:JSON.stringify(repeatedState) })).status).toBe(200)
|
||||
const repeatedReport=await (await fetch(`${baseUrl}/api/levels/${repeatedState.id}/report/submissions`,{ method:'POST',headers:{'content-type':'application/json'},body:JSON.stringify({
|
||||
investigatorName:'Test Player',evidence:[{ connectionId:repeatedConnectionId,documentExhibitId:repeatedDocument.id,relationText:'Proof that Barricelli is named as the inventor on patent GB695913A.',
|
||||
publishedAt:'1953-08-19',sourceCitation:'Google Patents · GB695913A',sourceUri:'https://patents.google.com/patent/GB695913A/en' }],
|
||||
investigatorName:'Test Player',
|
||||
}) })).json()
|
||||
expect(repeatedReport).toMatchObject({ status:'accepted',claims:[expect.objectContaining({ evidence:expect.arrayContaining([expect.objectContaining({
|
||||
documentExhibitId:repeatedDocument.id,evidenceAccepted:true,verification:expect.objectContaining({ status:'accepted' }),
|
||||
|
||||
Reference in New Issue
Block a user