Files
juzi/h5/test/recommendation.js
T

68 lines
3.0 KiB
JavaScript

'use strict';
const assert = require('node:assert/strict');
const SS = require('../js/skin-score.js');
const UI = require('../js/report-ui.js');
let count = 0;
function check(age, firm, vita, stab, tier, adjustment = 'none') {
const r = SS.recommendProduct(age, { firm, vita, stab });
assert.equal(r.tier, tier, `age=${age}; scores=${firm}/${vita}/${stab}`);
assert.equal(r.adjustment, adjustment);
assert.equal(r.version, '2026-10-12-skin-age-v1');
if (tier) assert(Math.abs(r.tier - r.baseTier) <= 1);
count++;
}
// Explicit decision-table expectations, including fractional ages and inclusive endpoints.
for (const [age, normal, low, high] of [
[24, 1, 1, 1], [31.99, 1, 1, 1], [32, 1, 2, 1], [34.99, 1, 2, 1],
[35, 2, 2, 1], [36, 2, 2, 1], [37, 2, 2, 1], [38, 2, 2, 1], [38.01, 2, 2, 2],
[46.99, 2, 2, 2], [47, 2, 3, 2], [49.99, 2, 3, 2],
[50, 3, 3, 2], [51, 3, 3, 2], [52, 3, 3, 2], [53, 3, 3, 2], [53.01, 3, 3, 3], [80, 3, 3, 3]
]) {
check(age, 8, 8, 8, normal);
check(age, 7.99, 10, 10, low, low > normal ? 'up' : 'none');
check(age, 9, 9, 7, high, high < normal ? 'down' : 'none');
check(age, 9, 7, 9, high, high < normal ? 'down' : 'none');
}
// Thresholds are compared before display rounding, not to 1- or 2-decimal scores.
for (const age of [32, 34.99, 47, 49.99]) {
const base = age < 35 ? 1 : 2;
check(age, 7.99999, 10, 10, base + 1, 'up');
check(age, 8, 10, 10, base);
check(age, null, 10, 10, base);
}
for (const age of [35, 38, 50, 53]) {
const base = age < 50 ? 2 : 3;
check(age, 8.99999, 10, 10, base);
check(age, 9, 8.99999, 8.99999, base);
check(age, 9, null, null, base);
check(age, null, 10, 10, base);
check(age, 9, null, 9, base - 1, 'down');
check(age, 9, 9, null, base - 1, 'down');
}
check(37, 9, 7, 9, 1, 'down'); // Only dullness: the third dimension may be arbitrarily low.
check(35, 8.99, 10, 10, 2); // Firmness is necessary, other two alone do not downgrade.
check(24, null, null, null, 1);
check(40, null, null, null, 2);
for (const invalid of [null, undefined, NaN, Infinity, -Infinity, 0, -5, '37', {}]) {
check(invalid, 10, 10, 10, null, 'unavailable');
}
const raw = JSON.parse(JSON.stringify(SS.MOCK_RESULT));
raw.skin_age = { value: 24 };
for (const realAge of [24, 40, 60, undefined]) {
const vm = SS.buildViewModel(raw, SS.MOCK_SIDE, { realAge });
assert.equal(vm.essence.tier, 1); // Weak wrinkles never upgrade under32.
assert.equal(vm.recommendation.skinAge, 24);
assert(Math.abs(vm.recommendation.scores.firm - 7.69) < 1e-12);
assert.equal(UI.build(vm, raw).product.tier, 1);
assert.equal(vm.skinAgeDisplay, 24); // Display and recommendation both use raw skin age.
count++;
}
delete raw.skin_age;
const missing = SS.buildViewModel(raw, SS.MOCK_SIDE, { realAge: 60 });
assert.equal(missing.group, null);
assert.equal(missing.essence.tier, null);
assert.equal(UI.build(missing, raw).product, null);
assert(missing.recommendation.reason.includes('未获取有效原始肌龄'));
count++;
console.log(`PASS: ${count} recommendation cases; boundary/threshold/missing/raw-age/actual-age isolation`);