85 lines
3.1 KiB
JavaScript
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]);
|
|
}
|
|
}
|
|
});
|