/** Тесты чистой математики суток (перенос релевантных проверок v1 lightSim). */
import { describe, expect, it } from 'vitest';
import {
DUSK_PEAK, DEFAULT_DUSK_COLOR, dayNightAmbient, dayNightFactor,
dimColor, flickerFactor, lerpAmbient, lerpColor,
} from '../daynight';
describe('lerpColor/lerpAmbient', () => {
it('краи и середина по каналам', () => {
expect(lerpColor(0x000000, 0xffffff, 0)).toBe(0x000000);
expect(lerpColor(0x000000, 0xffffff, 1)).toBe(0xffffff);
expect(lerpAmbient(0x102030, 0x304050, 0.5)).toBe(0x203040);
});
});
describe('dayNightAmbient (золотой час)', () => {
it('краи: k=0 — день, k=1 — ночь', () => {
expect(dayNightAmbient(0xf7f4ea, 0x3e4658, 0)).toBe(0xf7f4ea);
expect(dayNightAmbient(0xf7f4ea, 0x3e4658, 1)).toBe(0x3e4658);
});
it('на пике (k = DUSK_PEAK) ambient равен закатному тону', () => {
expect(dayNightAmbient(0xf7f4ea, 0x3e4658, DUSK_PEAK)).toBe(DEFAULT_DUSK_COLOR);
expect(dayNightAmbient(0xf7f4ea, 0x3e4658, DUSK_PEAK, 0xffcc88)).toBe(0xffcc88);
});
it('до пика — лерп день→закат, после — закат→ночь', () => {
expect(dayNightAmbient(0xf7f4ea, 0x3e4658, DUSK_PEAK / 2)).toBe(
lerpAmbient(0xf7f4ea, DEFAULT_DUSK_COLOR, 0.5),
);
expect(dayNightAmbient(0xf7f4ea, 0x3e4658, DUSK_PEAK + (1 - DUSK_PEAK) / 2)).toBe(
lerpAmbient(DEFAULT_DUSK_COLOR, 0x3e4658, 0.5),
);
});
it('непрерывен и монотонен по k (день светлее ночи по R)', () => {
let prevR = 256;
for (let k = 0; k <= 1.0001; k += 0.01) {
const r = (dayNightAmbient(0xf7f4ea, 0x3e4658, k) >> 16) & 0xff;
expect(r).toBeLessThanOrEqual(prevR);
prevR = r;
}
expect(dayNightAmbient(0xf7f4ea, 0x3e4658, DUSK_PEAK - 0.001)).toBeCloseTo(
dayNightAmbient(0xf7f4ea, 0x3e4658, DUSK_PEAK + 0.001), 1,
);
});
});
describe('dayNightFactor', () => {
it('день (8..18) — 0, ночь (20..6) — 1', () => {
expect(dayNightFactor(12)).toBe(0);
expect(dayNightFactor(0)).toBe(1);
expect(dayNightFactor(23)).toBe(1);
expect(dayNightFactor(5.5)).toBe(1);
});
it('переходы линейны: 7:00 — половина рассвета, 19:00 — половина заката', () => {
expect(dayNightFactor(7)).toBeCloseTo(0.5);
expect(dayNightFactor(19)).toBeCloseTo(0.5);
expect(dayNightFactor(18.5)).toBeCloseTo(0.25);
});
it('непрерывен на стыке 24/0', () => {
expect(dayNightFactor(24)).toBe(dayNightFactor(0));
});
});
describe('dimColor', () => {
it('краи: полная яркость и ноль', () => {
expect(dimColor(0x54586a, 1)).toBe(0x54586a);
expect(dimColor(0x54586a, 0)).toBe(0x000000);
});
});
describe('flickerFactor', () => {
it('детерминирован: одинаковые (time, seed) — одинаковый результат', () => {
expect(flickerFactor(2.5, 0.3, 0.4, 0.9)).toBe(flickerFactor(2.5, 0.3, 0.4, 0.9));
});
it('разные seed дают разные значения', () => {
expect(flickerFactor(1, 0.1, 0.5, 0.9)).not.toBe(flickerFactor(1, 0.9, 0.5, 0.9));
});
it('в диапазоне [1-amount, 1] на сетке времени', () => {
for (let t = 0; t < 10; t += 0.05) {
const f = flickerFactor(t, 0.42, 0.35, 0.9);
expect(f).toBeLessThanOrEqual(1);
expect(f).toBeGreaterThanOrEqual(1 - 0.35 - 1e-9);
}
});
it('amount = 0 — ровный свет', () => {
expect(flickerFactor(3.7, 0.5, 0, 0.9)).toBe(1);
});
});