mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-14 06:16:20 -06:00
Two tests disagreed after the QA merges, and both were right about their own half — which is what made the disagreement worth resolving rather than silencing. A baseline describes what an UN-UPDATED display can do. The baselines were justified against `git show v1.9.28:<source>` and then asserted against the working tree, so the moment a player's payload bug was fixed the biconditional demanded a baseline change for displays that cannot possibly have the fix yet. A baseline entry moves when a fix SHIPS. It now reads the newest release tag, and falls back to the tree when tags are unavailable (a shallow CI clone), because a missing tag is a worse reason to fail a build than a slightly-early assertion. While fixing it the helper threw a ReferenceError — the require was missing — and its own broad catch swallowed it and quietly compared against the working tree anyway. The catch now rethrows ReferenceError and TypeError. A fallback that hides a programming error is the same failure shape as everything else this QA pass found. The BrightSign assertion encoded the older, more generous baseline: reboot needs the BrightScript host bridge, and an undeclared unit is precisely the one we cannot know has it. What that test is really pinning is that the row still classifies as brightsign rather than decaying to `web` — so it now asserts that, plus the video playback that is genuinely safe to assume. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
114 lines
6.8 KiB
JavaScript
114 lines
6.8 KiB
JavaScript
'use strict';
|
|
|
|
/*
|
|
* A register that does not mention `platform` must not erase the one we have.
|
|
*
|
|
* liveness.captureIdentity() coerces a missing platform to the literal string 'unknown', and
|
|
* persistIdentity() wrote that straight over the stored value. So a single register from any
|
|
* client that doesn't send the field — an older build, a downgrade, anything pre-v4 — permanently
|
|
* turned a known Tizen panel into 'unknown'.
|
|
*
|
|
* That column is load-bearing: player-capabilities.platformFamily() reads it to pick a baseline.
|
|
* A cleared Tizen panel falls through to the WEB baseline, which hands it audio.volume (the .wgt
|
|
* has no set_volume handler — that is precisely why BASELINE.tizen omits it) and offline.cache
|
|
* (Tizen caches the playlist JSON, not the media). The same clobber costs a BrightSign its screen
|
|
* power and reboot and gives it screenshots it cannot take.
|
|
*
|
|
* The rule is the one applyCapabilities() already documents one screen up: an ABSENT declaration
|
|
* is not a statement about the device.
|
|
*/
|
|
|
|
const { test } = require('node:test');
|
|
const assert = require('node:assert/strict');
|
|
const liveness = require('../lib/liveness');
|
|
const caps = require('../lib/player-capabilities');
|
|
|
|
// What persistIdentity does with a register payload, minus the DB round trip.
|
|
const resolve = (stored, data) => liveness.preserveKnownIdentity(stored, liveness.captureIdentity(data));
|
|
|
|
test('a register with no platform leaves a known platform alone', () => {
|
|
const tizen = { client_type: 'wgt', client_version: '1.9.29', platform: 'Tizen 6.0', contract_version: 'v4' };
|
|
assert.equal(resolve(tizen, { device_id: 'x' }).platform, 'Tizen 6.0');
|
|
|
|
const bs = { client_type: 'player', client_version: '1.9.29', platform: 'brightsign', contract_version: 'v4' };
|
|
assert.equal(resolve(bs, { device_id: 'x' }).platform, 'brightsign');
|
|
});
|
|
|
|
test('the capability baseline survives that register — which is the whole point', () => {
|
|
const stored = { client_type: 'wgt', client_version: '1.9.29', platform: 'Tizen 6.0', contract_version: 'v4' };
|
|
const after = resolve(stored, { device_id: 'x' }); // an old client reconnects
|
|
const row = { platform: after.platform, client_type: after.client_type, android_version: null };
|
|
|
|
assert.equal(caps.platformFamily(row), 'tizen');
|
|
assert.equal(caps.supports(row, 'audio.volume'), false,
|
|
'a fielded .wgt has no set_volume handler — the web baseline would have offered the slider anyway');
|
|
assert.equal(caps.supports(row, 'offline.cache'), false,
|
|
'Tizen caches the playlist JSON, not the media, so content does NOT survive an outage');
|
|
|
|
const bsAfter = resolve({ client_type: 'player', client_version: '1.9.29', platform: 'brightsign', contract_version: 'v4' }, {});
|
|
const bsRow = { platform: bsAfter.platform, client_type: bsAfter.client_type, android_version: null };
|
|
assert.equal(caps.platformFamily(bsRow), 'brightsign');
|
|
// Reboot needs the BrightScript host bridge, and an UNDECLARED unit is exactly the one we cannot
|
|
// know has it — a widget pointed at /player by someone else's tooling has no bridge at all. The
|
|
// point being preserved here is that the row still classifies as brightsign rather than decaying
|
|
// to `web`, which would have handed it the web baseline's screenshot and volume instead.
|
|
assert.equal(caps.supports(bsRow, 'system.reboot'), false, 'the baseline cannot assume a host bridge');
|
|
assert.equal(caps.supports(bsRow, 'remote.screenshot'), false, 'and a canvas cannot read the video plane');
|
|
assert.equal(caps.supports(bsRow, 'playback.video'), true, 'but it certainly plays video');
|
|
});
|
|
|
|
test('a register that DOES declare a platform still updates it', () => {
|
|
// Preserving must not become "the first value wins forever": a genuine change (a panel
|
|
// re-flashed, a row reused for different hardware) has to land.
|
|
const stored = { client_type: 'wgt', client_version: '1.9.29', platform: 'Tizen 6.0', contract_version: 'v4' };
|
|
const i = resolve(stored, { platform: 'Tizen 7.0', client_type: 'wgt', client_version: '2.0', contract_version: 'v4' });
|
|
assert.equal(i.platform, 'Tizen 7.0');
|
|
assert.equal(liveness.identityChanged(stored, i), true, 'and the change is still detected, so it is written');
|
|
});
|
|
|
|
test('a device that never had a platform is not invented one', () => {
|
|
assert.equal(resolve(null, {}).platform, 'unknown', 'still honest about not knowing');
|
|
assert.equal(resolve({ platform: 'unknown' }, {}).platform, 'unknown');
|
|
});
|
|
|
|
test('an unchanged identity still short-circuits the write', () => {
|
|
// The A1 optimisation this function sits inside: a plain reconnect must stay a read with no
|
|
// UPDATE. Preserving the platform must not make every register look like a change.
|
|
const stored = { client_type: 'wgt', client_version: '1.9.29', platform: 'Tizen 6.0', contract_version: 'v4' };
|
|
assert.equal(liveness.identityChanged(stored, resolve(stored, {
|
|
platform: 'Tizen 6.0', client_type: 'wgt', client_version: '1.9.29', contract_version: 'v4',
|
|
})), false);
|
|
});
|
|
|
|
test('client_type wgt is a second, independent signal that this is a Tizen TV', () => {
|
|
// Belt and braces for a row whose platform was already cleared by the old behaviour: the .wgt
|
|
// player sends client_type 'wgt' (tizen/js/app.js), so one lost column is not the end of it.
|
|
const cleared = { platform: 'unknown', client_type: 'wgt', android_version: null };
|
|
assert.equal(caps.platformFamily(cleared), 'tizen');
|
|
assert.equal(caps.supports(cleared, 'audio.volume'), false);
|
|
});
|
|
|
|
test('client_type is preserved too — otherwise the second signal decays with the first', () => {
|
|
// captureIdentity coerces a missing client_type to 'legacy'. Preserving `platform` alone would
|
|
// still leave a panel that reconnects from an older build with NOTHING identifying it.
|
|
const stored = { client_type: 'wgt', client_version: '1.9.29', platform: 'Tizen 6.0', contract_version: 'v4' };
|
|
assert.equal(resolve(stored, {}).client_type, 'wgt');
|
|
assert.equal(resolve({ client_type: 'apk', platform: 'Android 14' }, {}).client_type, 'apk');
|
|
});
|
|
|
|
test('the version fields still decay — a stale build number is not an improvement', () => {
|
|
// The other half of the split: platform/client_type are physical facts, client_version and
|
|
// contract_version are properties of the build currently installed and change with every OTA.
|
|
const stored = { client_type: 'wgt', client_version: '1.9.29', platform: 'Tizen 6.0', contract_version: 'v4' };
|
|
const i = resolve(stored, {});
|
|
assert.equal(i.client_version, 'unknown');
|
|
assert.equal(i.contract_version, 'legacy');
|
|
});
|
|
|
|
test('the Android fleet is untouched by the wgt rule', () => {
|
|
assert.equal(caps.platformFamily({ client_type: 'apk', android_version: '14' }), 'android');
|
|
assert.equal(caps.platformFamily({ android_version: '14' }), 'android');
|
|
assert.equal(caps.platformFamily({ android_version: 'Web/Chrome 120' }), 'web');
|
|
assert.equal(caps.platformFamily({}), 'web');
|
|
});
|