Add deployment reading windows and progress-aware quiz unlock
This commit is contained in:
1 parent
edbc9c6056
commit
d8eba9169f
15 files changed
+471
-5
No files matched your search
@@ -0,0 +1,60 @@
|
||||
'use strict';
|
||||
const test = require('node:test');
|
||||
const assert = require('node:assert/strict');
|
||||
const { parseSchedule, scheduled, answerMatches, createReadingAccess } = require('../reading-access');
|
||||
|
||||
const windows = parseSchedule(JSON.stringify({
|
||||
0: [['08:00', '01:00']], 1: [['07:00', '12:00'], ['13:00', '24:00']],
|
||||
2: [['07:00', '24:00']], 3: [['07:00', '24:00']], 4: [['07:00', '24:00']],
|
||||
5: [['07:00', '24:00']], 6: [['08:00', '01:00']],
|
||||
}), 'Europe/Berlin');
|
||||
const at = iso => new Date(iso);
|
||||
|
||||
test('multiple daily windows and weekend overnight carry in local time', () => {
|
||||
assert.equal(scheduled(windows, at('2026-10-12T08:00:00Z')), true); // Monday 10:00
|
||||
assert.equal(scheduled(windows, at('2026-10-12T10:30:00Z')), false); // lunch gap 12:30
|
||||
assert.equal(scheduled(windows, at('2026-10-12T11:30:00Z')), true);
|
||||
assert.equal(scheduled(windows, at('2026-10-10T22:30:00Z')), true); // Sunday 00:30 from Saturday
|
||||
assert.equal(scheduled(windows, at('2026-10-11T22:30:00Z')), true); // Monday 00:30 from Sunday
|
||||
assert.equal(scheduled(windows, at('2026-10-11T23:30:00Z')), false); // Monday 01:30
|
||||
});
|
||||
|
||||
test('schedule config rejects malformed and zero-length windows', () => {
|
||||
assert.throws(() => parseSchedule('{"1":[["12:00","12:00"]]}', 'Europe/Berlin'));
|
||||
assert.throws(() => parseSchedule('{"7":[]}', 'Europe/Berlin'));
|
||||
assert.throws(() => parseSchedule('{"1":[["25:00","26:00"]]}', 'Europe/Berlin'));
|
||||
});
|
||||
|
||||
test('answers accept aliases and small typos but not unrelated words', () => {
|
||||
assert.equal(answerMatches(' luffy ', ['Monkey D. Luffy', 'Luffy']), true);
|
||||
assert.equal(answerMatches('Lufy', ['Luffy']), true);
|
||||
assert.equal(answerMatches('pirate', ['Luffy']), false);
|
||||
});
|
||||
|
||||
test('five ordered answers grant time, allow retries and reset on a new visit', () => {
|
||||
let clock = Date.parse('2026-10-14T01:00:00Z'), saves = 0;
|
||||
const grants = {}, bank = { moon: Array.from({ length: 5 }, (_, i) => ({ id: `q${i}`, prompt: `Question ${i}`, answers: [`answer${i}`], requires: ['v01/c0001'] })) };
|
||||
let finished = false;
|
||||
const gate = createReadingAccess({ schedule: parseSchedule('{"0":[],"1":[],"2":[],"3":[],"4":[],"5":[],"6":[]}', 'UTC'),
|
||||
bank, grants, save: () => saves++, now: () => clock,
|
||||
getProgress: () => ({ 'v01/c0001': { done: finished } }), getMangaTitle: () => 'Moon Garden' });
|
||||
assert.equal(gate.status('reader', true).quizAvailable, false);
|
||||
assert.throws(() => gate.start('reader'));
|
||||
finished = true;
|
||||
assert.equal(gate.status('reader', true).quizAvailable, true);
|
||||
const first = gate.start('reader');
|
||||
assert.equal(gate.answer('reader', first.challenge, 'wrong').correct, false);
|
||||
assert.equal(gate.start('reader').number, 1);
|
||||
assert.throws(() => gate.answer('reader', first.challenge, 'answer0'));
|
||||
let current = gate.start('reader');
|
||||
for (let i = 0; i < 5; i++) {
|
||||
const answer = bank.moon.find(q => q.prompt === current.prompt).answers[0];
|
||||
current = gate.answer('reader', current.challenge, answer);
|
||||
}
|
||||
assert.equal(current.complete, true);
|
||||
assert.equal(grants.reader, clock + 3600000);
|
||||
assert.equal(saves, 1);
|
||||
assert.equal(gate.status('reader').allowed, true);
|
||||
clock += 3600000;
|
||||
assert.equal(gate.status('reader').allowed, false);
|
||||
});
|
||||
Reference in new issue
Block a user