80 lines
3.2 KiB
TypeScript
80 lines
3.2 KiB
TypeScript
import { describe, it, expect } from 'vitest';
|
|
import { readFileSync } from 'node:fs';
|
|
import { join } from 'node:path';
|
|
import {
|
|
clamp, lerp, curvaCampana, curvaDecrescente, curvaCrescenteConPlateau,
|
|
curvaDirettaX10, curvaDirettaX10Invertita, curvaGradini,
|
|
} from '../../src/lib/longevity/motore/curve';
|
|
|
|
const RIF = JSON.parse(
|
|
readFileSync(join(process.cwd(), 'tests/longevity/riferimento/riferimento.json'), 'utf8')
|
|
) as { casi: { fn: string; args: unknown[]; atteso: number }[] };
|
|
|
|
const casiDi = (fn: string) => RIF.casi.filter((c) => c.fn === fn);
|
|
|
|
describe('curve del questionario, confrontate con l oracolo', () => {
|
|
it('la campana combacia su tutto il dominio', () => {
|
|
const casi = casiDi('score_bell_curve');
|
|
expect(casi.length).toBeGreaterThan(20);
|
|
for (const c of casi) {
|
|
const [v, low, pl, ph, high] = c.args as number[];
|
|
expect(curvaCampana(v, low, pl, ph, high)).toBeCloseTo(c.atteso, 1);
|
|
}
|
|
});
|
|
|
|
it('la decrescente combacia, inclusa la soglia 7-14 dell alcol', () => {
|
|
const casi = casiDi('score_decreasing');
|
|
expect(casi.length).toBeGreaterThan(40);
|
|
for (const c of casi) {
|
|
const [v, best, worst] = c.args as number[];
|
|
expect(curvaDecrescente(v, best, worst)).toBeCloseTo(c.atteso, 1);
|
|
}
|
|
});
|
|
|
|
it('la crescente con plateau combacia', () => {
|
|
for (const c of casiDi('score_increasing_plateau')) {
|
|
const [v, worst, plateau] = c.args as number[];
|
|
expect(curvaCrescenteConPlateau(v, worst, plateau)).toBeCloseTo(c.atteso, 1);
|
|
}
|
|
});
|
|
|
|
it('la diretta per dieci combacia', () => {
|
|
for (const c of casiDi('score_direct_x10')) {
|
|
expect(curvaDirettaX10((c.args as number[])[0])).toBeCloseTo(c.atteso, 1);
|
|
}
|
|
});
|
|
|
|
// Questa curva NON esiste nell'oracolo: è la quinta, che al motore del cliente manca.
|
|
// Serve a q_calo_pomeridiano e nel prototipo HTML è `scale10_inv`: (10 - v) * 10.
|
|
it('la diretta invertita e il complemento della diretta', () => {
|
|
for (const v of [0, 2.5, 5, 7.5, 10]) {
|
|
expect(curvaDirettaX10Invertita(v)).toBeCloseTo(curvaDirettaX10(10 - v), 6);
|
|
}
|
|
expect(curvaDirettaX10Invertita(0)).toBe(100);
|
|
expect(curvaDirettaX10Invertita(10)).toBe(0);
|
|
});
|
|
|
|
it('i gradini decrescenti riproducono il prototipo: q_sigarette', () => {
|
|
const steps: [number, number][] = [[0, 100], [5, 60], [10, 40], [20, 20], [999, 0]];
|
|
expect(curvaGradini(0, steps, undefined, true)).toBe(100);
|
|
expect(curvaGradini(5, steps, undefined, true)).toBe(60);
|
|
expect(curvaGradini(10, steps, undefined, true)).toBe(40);
|
|
expect(curvaGradini(20, steps, undefined, true)).toBe(20);
|
|
expect(curvaGradini(40, steps, undefined, true)).toBeLessThan(20);
|
|
});
|
|
|
|
it('zeroVal ha la precedenza sui gradini: q_schermi a zero vale 100', () => {
|
|
const steps: [number, number][] = [[15, 85], [30, 70], [60, 40], [999, 10]];
|
|
expect(curvaGradini(0, steps, 100, true)).toBe(100);
|
|
expect(curvaGradini(15, steps, 100, true)).toBe(85);
|
|
});
|
|
|
|
it('clamp e lerp si comportano come nell oracolo', () => {
|
|
expect(clamp(150)).toBe(100);
|
|
expect(clamp(-5)).toBe(0);
|
|
expect(lerp(5, 0, 10, 0, 100)).toBe(50);
|
|
expect(lerp(-1, 0, 10, 0, 100)).toBe(0); // t viene limitato a [0,1]
|
|
expect(lerp(11, 0, 10, 0, 100)).toBe(100);
|
|
});
|
|
});
|