import test from 'node:test'; import assert from 'node:assert/strict'; import {ingestResourceBody, resourceParents, attachmentRows} from '../bubble-resource-utils.mjs'; const id='1700000000000x1', other='1700000000000x2'; const output=()=>({records:{},empty:{}}); test('only explicit affected parents are visited; descriptors are not article targets',()=>{ assert.deepEqual(resourceParents({[id]:{_list_custom_scrap:'LOOKUP__'+other}, [other]:{hasattachments_boolean:true}, '1700000000000x3':{_list_custom_scrap:[]}, invalid:{hasattachments_boolean:true}}),[id,other]); }); test('typed search and initialization resource observations preserve versions',()=>{ const result=output(); ingestResourceBody(JSON.stringify({responses:[{hits:{hits:[{_type:'custom.scrap',_id:id, _source:{content_text:'Synthetic first'}}]}}]}),result); const envelope={type:'custom.scrap',id,data:{_type:'custom.scrap',_id:id, content_text:'Synthetic second'}}; ingestResourceBody(JSON.stringify(envelope),result); ingestResourceBody(JSON.stringify(envelope),result); assert.equal(result.records['custom.scrap:'+id].length,2); assert.equal(result.records['custom.scrap:'+id][0].content_text,'Synthetic first'); }); test('empty envelopes remain unresolved; mixed ids/types and nested users are excluded',()=>{ const result=output(); const resource={_type:'custom.scrap',_id:id,_source:{content_text:'Synthetic'}}; ingestResourceBody(JSON.stringify([ {type:'custom.scrap',id,data:{}}, {type:'custom.scrap',id,data:{_type:'custom.scrap',_id:other}}, {type:'user',id,data:resource}, {_type:'user',_id:id,_source:resource}, {...resource,_source:{_type:'custom.article'}}, {...resource,_source:{_id:other}}, {_type:'custom.article',_id:id,_source:{content_text:'Synthetic article'}}, ]),result); assert.deepEqual(result.records,{}); assert.deepEqual(result.empty,{'custom.scrap:1700000000000x1':{type:'custom.scrap',id}}); }); test('NDJSON selects only content-resource types',()=>{ const result=output(); ingestResourceBody('unrelated\n'+JSON.stringify({_type:'custom.relattachment',_id:id, _source:{url_text:'https://example.invalid/synthetic'}})+'\n'+ JSON.stringify({_type:'session',_id:other,_source:{cookie:'synthetic'}}),result); assert.deepEqual(Object.keys(result.records),['custom.relattachment:'+id]); }); function attachmentFixture() { const relation={_type:'custom.relattachment',_id:'1700000000000x3', to_custom_article:id,_chunk_custom_article:other}; const resources={records:{'custom.relattachment:1700000000000x3':[relation]}}; const articles={[other]:{context_custom_article:id,nomer_text:'Synthetic file', logo_image:'//0123456789abcdef0123456789abcdef.cdn.bubble.io/synthetic.pdf', 'Created Date':1700000000000,'Modified Date':1700000000001}}; return {resources,articles}; } test('attachment relation binds parent, original file and exact source dates',()=>{ const {resources,articles}=attachmentFixture(); const [row]=attachmentRows(id,resources,articles); assert.equal(row.article_id,other); assert.equal(row.url,'https:'+articles[other].logo_image); assert.equal(row.created_at,1700000000000); assert.equal(row.modified_at,1700000000001); assert.deepEqual(attachmentRows('1700000000000x9',resources,articles),[]); }); test('incomplete, cross-parent and drifting attachment relations are refused',()=>{ for(const change of [f=>delete f.articles[other], f=>{f.articles[other].context_custom_article='1700000000000x9';}, f=>{f.resources.records['custom.relattachment:1700000000000x3'].push( {...f.resources.records['custom.relattachment:1700000000000x3'][0],_version:2});}]) { const f=attachmentFixture();change(f); assert.throws(()=>attachmentRows(id,f.resources,f.articles),/attachment_/); } }); test('file retrieval requires the observed HTTPS Bubble CDN, without credentials',()=>{ for(const url of ['http://0123456789abcdef0123456789abcdef.cdn.bubble.io/file', 'https://example.invalid/file','https://localhost/file', 'https://secret@0123456789abcdef0123456789abcdef.cdn.bubble.io/file', 'https://0123456789abcdef0123456789abcdef.cdn.bubble.io:444/file']) { const f=attachmentFixture();f.articles[other].logo_image=url; assert.throws(()=>attachmentRows(id,f.resources,f.articles),/attachment_file_origin/); } });