'use strict'; const assert=require('node:assert/strict');const SS=require('../js/skin-score'),UI=require('../js/report-ui'); const near=(a,b)=>assert(Math.abs(a-b)<1e-9, a+' !== '+b); function run(dc,options={}){ const r=structuredClone(SS.MOCK_RESULT);Object.assign(r.score_info,{melanin_score:90,water_score:80,dark_circle_score:dc}); r.melasma={value:1};r.freckle={value:1};r.water={water_severity:20}; if(options.missingPigment)delete r.score_info.melanin_score; if(options.missingWater)delete r.score_info.water_score; const bytes=JSON.stringify(r),vm=SS.buildViewModel(r,SS.MOCK_SIDE,{});assert.equal(JSON.stringify(r),bytes); return{vm,r,v:vm.cats.tone.vars}; } const a=run(50);near(a.v.color_score,75*.8+50*.2);near(a.vm.cats.tone.P,75); assert.equal(a.vm.cats.tone.S,9.25);assert.equal(a.vm.dims.vita.S,9.1); assert(!UI.build(a.vm,a.r).views.find(v=>v.key==='color').text.includes('占肤色项20%')); for(const dc of [0,100]){const x=run(dc);near(x.v.color_score,75*.8+dc*.2);near(x.vm.cats.tone.P,(x.v.color_score+80)/2);} for(const dc of [null,undefined,-1,101,NaN,Infinity]){const x=run(dc);assert.equal(x.v.dark_circle_score,null);near(x.v.color_score,75);near(x.vm.cats.tone.P,77.5);} near(run(50,{missingPigment:true}).vm.cats.tone.P,65); near(run(50,{missingWater:true}).vm.cats.tone.P,70); assert.equal(a.v.tone_scoring.dark_circle_weight,.2);assert.equal(a.v.tone_scoring.pigment_weight,.8); console.log('PASS tone: pigment80/dark-circle20, color/water50:50, spot deductions before weighting, zero/missing/invalid scores, audit, copy and immutable source');