'use strict'; const assert=require('node:assert/strict'), Views=require('../lib/wrinkle-views'); const side=label=>({request_id:label,result:{score_info:{wrinkle_score:1},face_maps:{texture_enhanced_lines:label+'-map',red_area:'do-not-forward'},wrinkle_mark:{left_cheek_wrinkle_outline:[{x:1,y:1},{x:2,y:1},{x:2,y:2}]}}}); const photos={left:Buffer.from('left photo').toString('base64'),right:Buffer.from('right photo').toString('base64')}; (async()=>{ const original={result:{score_info:{wrinkle_score:76}},left_side_result:{left_jawline_info:{left_jawline_angle:112}}}; const before=JSON.stringify(original),calls=[]; const options={key:'fixture',secret:'fixture',url:'https://fixture.invalid',sleep:async()=>{},now:()=>0,fetch:async(url,o)=>{ assert.equal(o.method,'POST');const form=o.body; assert.equal(form.get('return_maps'),'texture_enhanced_lines');assert.equal(form.get('return_marks'),'wrinkle_mark'); assert(!form.has('left_side_image_file')&&!form.has('right_side_image_file')); const file=form.get('image_file'),label=file.name.split('.')[0];calls.push(label); assert.equal(await file.text(),label+' photo'); return{ok:true,status:200,json:async()=>side(label)}; }}; const views=await Views.collect(original,photos,options); assert.deepEqual(calls,['left','right']);assert.equal(JSON.stringify(original),before); assert.equal(views.left.status,'ready');assert.equal(views.right.status,'ready'); const report=Views.forReport(views); assert.equal(report.left.result.face_maps.texture_enhanced_lines,'left-map'); assert.equal(report.right.result.face_maps.texture_enhanced_lines,'right-map'); assert(!report.left.result.score_info&&!report.left.result.face_maps.red_area); const mixed=await Views.collect(original,photos,{...options,fetch:async(url,o)=>({ok:true,status:200,json:async()=>o.body.get('image_file').name==='left.jpg'?{error_message:'INVALID_IMAGE_FACE: test'}:side('right')})}); assert.equal(mixed.left.status,'unavailable');assert.equal(mixed.left.error_code,'INVALID_IMAGE_FACE');assert.equal(mixed.right.status,'ready'); assert(!Views.forReport(mixed).left.result); const deadline=await Views.collect(original,photos,{...options,deadline:1,gapMs:4,fetch:()=>{throw Error('must not call');}}); assert.equal(deadline.left.error_code,'WRINKLE_TIME_BUDGET');assert.equal(deadline.right.error_code,'WRINKLE_TIME_BUDGET'); const missing=await Views.collect(original,{},options);assert.equal(missing.left.error_code,'MISSING_SIDE_PHOTO'); const reused=await Views.collect({left_side_result:side('cached').result},photos,{...options,fetch:async()=>({ok:true,json:async()=>side('right')})}); assert.equal(reused.left.source,'primary-side-result');assert.equal(reused.right.source,'independent-side-request'); const failed=await Views.collect(original,photos,{...options,fetch:async()=>{const e=new Error();e.name='TimeoutError';throw e;}}); assert.equal(failed.left.error_code,'WRINKLE_TIMEOUT');assert.equal(failed.right.error_code,'WRINKLE_TIMEOUT'); console.log('PASS wrinkle views: own photo requests, sequential order, separate maps, no score mutation/forwarding, native reuse, missing/API failure/timeout/budget and report-safe payload'); })().catch(e=>{console.error(e);process.exitCode=1;});