mirror of
https://github.com/screentinker/screentinker.git
synced 2026-08-14 06:16:20 -06:00
2 commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4f7b4e3989 |
Judge capability baselines against the SHIPPED source, not the working tree
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 |
||
|
|
3e37d33b80 |
QA: close four ways a control or an asset lied about itself
Found by driving the real server and a real browser, not by reading. Each fix has a
test that fails without it.
1. A missing upload answered 200 with the DASHBOARD. express.static falls through on a
miss and the SPA catch-all caught it, so GET /uploads/content/<gone>.mp4 returned
15KB of index.html as text/html — under the `immutable, max-age=30d` header the mount
sets before it knows the file exists. Every player downloader treats 200 as success,
so a panel stores the HTML page AS the video and caches it for a month, rendering a
black frame with nothing in any log. Reachable exactly when it hurts: a content
replace writes a new random filename and unlinks the old one. The mount now
terminates a miss with a 404 and drops the cache header.
2. Four dashboard->device socket handlers had no capability gate. dashboard:device-command
has always refused a command the panel cannot honour, and the comment above it is right
about why ("hiding the button is not enforcement — this socket is reachable directly").
Every word applied to the four handlers immediately above it, which had none: a display
declaring [] still received screenshot-request, remote-touch, remote-key and
remote-start. Measured, not inferred. They now refuse on remote.screenshot /
remote.input / remote.stream and name the capability in the ack; remote-stop stays
ungated for the same reason set_debug does. The undeclared fleet is unaffected — an
absent declaration still resolves to its platform baseline and keeps everything.
The wall panel list (#235) made this visible: it offered a Screenshot button for every
panel, including a BrightSign, which has no screenshot capability at all, and popped a
toast promising an image that was never coming. GET /api/devices now ships the RESOLVED
capability array rather than the raw column ('[]' as a STRING, which Array.isArray reads
as "pre-capability server, show everything" — wrong in the one case that matters), so
the wall list and the fleet cards can hide what a panel cannot do. The remote pad's
Scrn Off / Scrn On were gated on remote.input while the Info tab gated the same two
commands on display.power; both now agree.
3. A register with no `platform` ERASED the stored one. captureIdentity coerces a missing
field to the literal 'unknown' and persistIdentity wrote it straight over. That column
is load-bearing: platformFamily() reads it, so one reconnect from an older build turned
a Tizen panel into a browser tab and handed it a volume slider the .wgt has no handler
for — the exact control BASELINE.tizen exists to hide — while a BrightSign lost screen
power and reboot and gained screenshots it cannot take. platform and client_type are
now preserved (physical facts); client_version and contract_version still decay, because
there "we no longer know" is the truthful answer. client_type 'wgt' is also read as a
second signal for a Tizen TV.
4. PUT /api/content/:id/replace carried its own shorter copy of the ingest logic. Replacing
a video left duration_sec at the OLD clip's length and nulled width/height, so #237's
brand-new "default an item to the clip's own length" then handed out the wrong number
for every later add — 32s scheduled for a 5s video is 27s of frozen frame. Replacing an
image measured it with raw sharp metadata and thumbnailed without .rotate(),
re-introducing the EXIF-orientation bug #172 had just fixed at ingest. Both paths now
share lib/content-ingest.deriveMediaMetadata.
Verified working and NOT changed: all six item-duration insert paths (a 31.7s clip stores
32 everywhere, an explicit value always wins, and no path can store a 0); the content
revision bump + filepath refresh reaching a real device socket; a landscape wall producing
byte-identical geometry to the pre-#236 expression; a portrait wall reaching the player as
side-by-side halves; cross-workspace isolation across 29 probes.
Full suite green (1319).
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Uaeo9MvzKoyXuN6ZsbhtkL
|