Files
root/tests/artwork-schedule.test.cjs

85 lines
3.1 KiB
JavaScript

const test = require('node:test');
const assert = require('node:assert/strict');
const fs = require('node:fs');
const path = require('node:path');
const vm = require('node:vm');
const sandbox = {};
vm.runInNewContext(fs.readFileSync(path.join(__dirname, '../artwork-schedule.js'), 'utf8'), sandbox);
const schedule = sandbox.NNCArtworkSchedule;
const order = loop => Array.from(schedule.orderForLoop(loop));
test('137-minute slots have a fixed selection up to the exact boundary', () => {
const duration = 137 * 60 * 1000;
assert.equal(schedule.SLOT_DURATION, duration);
const now = Date.parse('2026-10-07T09:00:00+09:00');
const start = Math.floor(now / duration) * duration;
const first = schedule.selectionAt(start);
assert.equal(schedule.selectionAt(start + 1).variant, first.variant);
assert.equal(schedule.selectionAt(start + duration - 1).variant, first.variant);
const next = schedule.selectionAt(start + duration);
assert.equal(next.slot, first.slot + 1);
assert.notEqual(next.variant, first.variant);
});
test('PRNG streams and permutations reproduce across fresh evaluations', () => {
const fresh = {};
vm.runInNewContext(fs.readFileSync(path.join(__dirname, '../artwork-schedule.js'), 'utf8'), fresh);
for (const seed of [0, 1, 100, 101, 102, 2147483647]) {
const a = schedule.createRandom(seed);
const b = fresh.NNCArtworkSchedule.createRandom(seed);
for (let index = 0; index < 100; index++) {
const value = a();
assert(value >= 0 && value < 1);
assert.equal(value, b());
}
assert.deepEqual(order(seed), Array.from(fresh.NNCArtworkSchedule.orderForLoop(seed)));
}
});
test('10,000 loops each contain all 30 artworks with no repeat across boundaries', () => {
let previous;
for (let loop = 0; loop < 10000; loop++) {
const current = order(loop);
assert.deepEqual([...current].sort((a, b) => a - b), Array.from({ length: 30 }, (_, i) => i));
if (previous) {
assert.notEqual(previous[29], current[0]);
assert.notDeepEqual(previous, current);
}
previous = current;
}
});
test('a boundary collision swaps exactly the first two items', () => {
function raw(loop) {
const random = schedule.createRandom(loop);
const result = Array.from({ length: 30 }, (_, i) => i);
for (let i = 29; i > 0; i--) {
const j = Math.floor(random() * (i + 1));
[result[i], result[j]] = [result[j], result[i]];
}
return result;
}
let collisions = 0;
for (let loop = 1; loop < 500; loop++) {
const expected = raw(loop);
if (raw(loop - 1)[29] === expected[0]) {
[expected[0], expected[1]] = [expected[1], expected[0]];
collisions++;
}
assert.deepEqual(order(loop), expected);
}
assert(collisions > 0);
});
test('all slots of a loop map to its permutation, including loop transitions', () => {
for (const loop of [0, 100, 101, 102, 2102]) {
for (let position = 0; position < 30; position++) {
const value = schedule.selectionAt((loop * 30 + position) * schedule.SLOT_DURATION);
assert.equal(value.loop, loop);
assert.equal(value.position, position);
assert.equal(value.variant, order(loop)[position]);
}
}
});