mirror of
https://github.com/screentinker/screentinker.git
synced 2026-06-17 11:42:40 -06:00
Each playlist item can carry schedule blocks (active days, start/end time-of-day, optional start/end dates). An item plays when the screen's local "now" matches at least one block; an item with no blocks always plays. #74 covers time-of-day/day-of-week windows including overnight wrap; #75 covers inclusive date ranges (auto-expiry). Evaluation is on-device, so dayparting and expiry work offline. - Shared evaluator contract: shared/schedule-vectors.json (39 vectors — DST US+AU, overnight-wrap anchoring, timezone correctness, date boundaries). Canonical JS evaluator in server/lib/schedule-eval.js; Kotlin and Tizen ports kept in lockstep by drift guards (Tizen byte-diff test, Kotlin JUnit reads the shared JSON, new android-test CI job). - All three players (web, Android, Tizen) filter by schedule against their own clock, idle with a "Nothing scheduled" message + 30s re-check when everything is filtered, and fail open on any evaluator error. - Editor: per-item schedule modal + row badge in the playlist editor; client validation mirrors the server; editing marks the playlist draft. - Part B (behaviour change): device/group schedule overrides now evaluate in each device's effective timezone instead of server-local time. - Device detail shows the reported timezone + a clock-skew warning. - i18n for en/es/fr/de/pt across all new strings (namespaced itemsched.* to avoid colliding with the device-schedule calendar's schedule.*). - CHANGELOG documents the feature, the Part B change, the fail-open guarantee, and the scheduled-single-video re-render tradeoff. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
27 lines
1.5 KiB
JavaScript
27 lines
1.5 KiB
JavaScript
// Drift guard (#74/#75): the Tizen player bundles the evaluator, and per the
|
|
// design directive it must be the BYTE-IDENTICAL canonical UMD (server/lib/
|
|
// schedule-eval.js), not a hand-port. This test (run by `npm test`, i.e. in CI)
|
|
// fails the moment tizen/js/schedule-eval.js diverges from the source, and also
|
|
// re-checks that the bundled copy still passes every shared vector.
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert');
|
|
const path = require('node:path');
|
|
const fs = require('node:fs');
|
|
|
|
const canonical = path.join(__dirname, '..', 'lib', 'schedule-eval.js');
|
|
const tizenCopy = path.join(__dirname, '..', '..', 'tizen', 'js', 'schedule-eval.js');
|
|
|
|
test('tizen evaluator is byte-identical to the canonical evaluator', () => {
|
|
assert.ok(fs.existsSync(tizenCopy), `tizen copy missing: ${tizenCopy}`);
|
|
const a = fs.readFileSync(canonical);
|
|
const b = fs.readFileSync(tizenCopy);
|
|
assert.ok(a.equals(b), 'tizen/js/schedule-eval.js has drifted from server/lib/schedule-eval.js — re-copy it (the .wgt build does this automatically)');
|
|
});
|
|
|
|
test('bundled tizen evaluator passes every shared vector', () => {
|
|
const { isItemActiveNow } = require(tizenCopy);
|
|
const data = JSON.parse(fs.readFileSync(path.join(__dirname, '..', '..', 'shared', 'schedule-vectors.json'), 'utf8'));
|
|
const failures = data.vectors.filter(v => isItemActiveNow(v.blocks, v.utc_now, v.timezone) !== v.expected);
|
|
assert.strictEqual(failures.length, 0, `${failures.length} vector(s) failed in the tizen copy`);
|
|
});
|