Closed
opened 2026-07-22 19:12:07 +02:00 by timothy
·
3 comments
No Branch/Tag Specified
main
renovate/meziantou.analyzer-3.x
renovate/nuget-sixlabors.imagesharp-vulnerability
913-scripted-playout-401
929-vuln-pkcs-imagesharp-v4
renovate/dotnet-monorepo
release/v26.15.0-notes
renovate/lucene.net
renovate/cliwrap-3.x
issue-806-guard-populations
scratch/767b-poisoned
scratch/767b-control
release/v26.14.0-notes
release/v26.14.0
renovate/sqlitepclraw.bundle_e_sqlite3-3.x
docs/510-skill-logo-bug-policy
fix/510-watermark-resolution-policy
fix/629-verdict-classifier-falseopens
fix/609-decisions-edit-token-scope
issue-135-clear-to-none
release/v26.12.0-notes
fix/409b-lastscan-api-parity
fix/401-updatechannel-mirror-422
fix/327-playlist-rename-validation
fix/410-scancancel-log-level
fix/409-447-librariesscreen-neverscanned
fix/338-zap-exit-code
fix/367-plex-budget-message
fix/310-debom-legacy-cs
ci/604-lane-rebalance
feat/388-design-mirror
feat/247-test-ownership
feat/247-primary-action
feat/357-player-owned-playback
feat/357-jellyfin-plugin-poc
fix/289-mcp-hardening
issue58-mcp
feat/244-channels-extract
ci/auto-bump-prod-compose
feat/multi-rerun-collections-api
feat/collections-api
feat/quick-wins
feat/185-docs-part2
feat/140-collections-screen
feat/146-channel-edit
feat/147-classic-ui-link
issue22-renovate-dashboard
feat/91-cutover
feat/63-composite-create
feat/65-library-browse
feat/85-epg
feat/86-schedule-editor
feat/109-dashboard-data
feat/99-session-tracking
fix/dockerfile-node-tag
feat/59-spa-foundation
docs/59-ui-redesign-brief
feat/102-json-guide
feat/111-schedule-durations
feat/104-artwork-upload
feat/103-media-sources-api
feat/playouts-read-api
feat/108-health-api
feat/105-picker-list-endpoints
issue-97-channel-state-api
issue42-jellyfin-musicvideos
issue46-rest-api-error-contract
dependabot/nuget/ErsatzTV.FFmpeg.Tests/multi-d307a2e06f
qsv-improvements
hdr-vulkan-cuda-test
v26.16.0
v26.15.0
v26.14.0
v26.13.0
v26.12.0
v26.11.0
v26.10.0
v26.9.0
v26.8.0
v26.7.0
blazor-final
v26.6.0
v26.5.0
v26.4.0
v26.3.1
v26.3.0
v26.2.0
v26.1.1
v26.1.0
v25.9.0
v25.8.0
v25.7.1
v25.7.0
v25.6.0
v25.5.0
v25.4.0
v25.3.1
v25.3.0
v25.2.0
v25.1.0
v0.8.8-beta
v0.8.7-beta
v0.8.6-beta
v0.8.5-beta
v0.8.4-beta
v0.8.3-beta
v0.8.2-beta
v0.8.1-beta
v0.8.0-beta
v0.7.9-beta
v0.7.8-beta
v0.7.7-beta
v0.7.6-beta
v0.7.5-beta
v0.7.4-beta
v0.7.3-beta
v0.7.2-beta
v0.7.1-beta
v0.7.0-beta
v0.6.9-beta
v0.6.8-beta
v0.6.7-beta
v0.6.6-beta
v0.6.5-beta
v0.6.4-beta
v0.6.3-beta
v0.6.2-beta
v0.6.1-beta
v0.6.0-beta
v0.5.8-beta
v0.5.7-beta
v0.5.6-beta
v0.5.5-beta
v0.5.4-beta
v0.5.3-beta
v0.5.2-beta
v0.5.1-beta
v0.5.0-beta
v0.4.5-alpha
v0.4.4-alpha
v0.4.3-alpha
v0.4.2-alpha
v0.4.1-alpha
v0.4.0-alpha
v0.3.8-alpha
v0.3.7-alpha
develop
v0.3.6-alpha
v0.3.5-alpha
v0.3.4-alpha
v0.3.3-alpha
v0.3.2-alpha
v0.3.1-alpha
v0.3.0-alpha
v0.2.5-alpha
v0.2.4-alpha
v0.2.3-alpha
v0.2.2-alpha
v0.2.1-alpha
v0.2.0-alpha
v0.1.5-alpha
v0.1.4-alpha
v0.1.3-alpha
v0.1.2-alpha
v0.1.1-alpha
v0.1.0-alpha
v0.0.62-alpha
v0.0.61-alpha
v0.0.60-alpha
v0.0.59-alpha
v0.0.58-alpha
v0.0.57-alpha
v0.0.56-alpha
v0.0.55-alpha
v0.0.54-alpha
v0.0.53-alpha
v0.0.52-alpha
v0.0.51-alpha
v0.0.50-alpha
v0.0.49-prealpha
v0.0.48-prealpha
v0.0.47-prealpha
v0.0.46-prealpha
v0.0.45-prealpha
v0.0.44-prealpha
v0.0.43-prealpha
v0.0.42-prealpha
v0.0.41-prealpha
v0.0.40-prealpha
v0.0.39-prealpha
v0.0.38-prealpha
v0.0.37-prealpha
v0.0.36-prealpha
v0.0.35-prealpha
v0.0.34-prealpha
v0.0.33-prealpha
v0.0.32-prealpha
v0.0.31-prealpha
v0.0.30-prealpha
v0.0.29-prealpha
v0.0.28-prealpha
v0.0.27-prealpha
v0.0.26-prealpha
v0.0.25-prealpha
v0.0.24-prealpha
v0.0.23-prealpha
v0.0.22-prealpha
v0.0.21-prealpha
v0.0.20-prealpha
v0.0.19-prealpha
v0.0.18-prealpha
v0.0.17-prealpha
v0.0.16-prealpha
v0.0.15-prealpha
v0.0.14-prealpha
v0.0.13-prealpha
v0.0.12-prealpha
v0.0.11-prealpha
v0.0.10-prealpha
v0.0.9-prealpha
v0.0.8-prealpha
v0.0.7-prealpha
v0.0.6-prealpha
v0.0.5-prealpha
v0.0.4-prealpha
v0.0.3-prealpha
v0.0.2-prealpha
v0.0.1-prealpha
Labels
Clear labels
ad-hoc
api
bug
ci-cd
content
dependencies
enhancement
frontend
in-progress
jellyfin
parked
priority: high
priority: low
priority: medium
review
security
One-off / ad-hoc work not tracked by a dedicated issue
REST API / HTTP endpoints
Something isn't working
Build, test, deploy pipeline
Channel content / schedules / playlists
Dependency updates (Renovate)
New feature or improvement
ChicoryTV React SPA frontend
Claimed by an active session — do not pick up
Jellyfin tuner / IPTV integration
Excluded from automatic queue pickup; work only when explicitly selected
Adversarial review finding
Security / vulnerability fix
Milestone
No items
No Milestone
Projects
Clear projects
No projects
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: timothy/ersatztv#563
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Follow-up to #381, which added the Sequential (YAML) golden and deferred Scripted from the in-memory
PlayoutBuildGoldenTestsnet.Why scripted can't be an in-memory golden
ScriptedPlayoutBuilder(ErsatzTV.Core/Scheduling/ScriptedScheduling/ScriptedPlayoutBuilder.cs) does not build items in-process: it shells out viaCli.Wrap(scriptFile).ExecuteBufferedAsync(...)to an external script process (the JS interpreter drivingErsatzTV/Resources/Scripts/*.js), which talks back overhttp://localhost:{Settings.UiPort}toSchedulingEngine(scriptedPlayoutBuilderService.StartSession(engine)). Golden characterization (shared in-memory SQLite, no I/O, pinned clock) fundamentally does not fit an external-process + HTTP-callback architecture.The deterministic core the scripts drive —
SchedulingEngine— is in-process and unit-testable (there is a seedErsatzTV.Core.Tests/Scheduling/Engine/SchedulingEngineTests.cs, currently minimal), but the scripted builder itself is process orchestration + HTTP transport, which is integration-test territory.Scope
Stand up an integration-test harness that:
SchedulingEngineHTTP endpoint on an ephemeral port,Cli.Wrappath (or a documented in-process stand-in), andPlayoutItems over a pinned window (golden-style snapshot once the harness is deterministic).Alternatively, expand
SchedulingEngineTeststo characterize the engine's build API directly (the part scripts call) and explicitly scope theCli.Wrap/HTTP shell out as out-of-test.Done-when
SchedulingEngineTestscovering the engine build API with the process/HTTP shell-out explicitly scoped out (documented reasoning).docs/testing.mdupdated.Decision context recorded in
docs/decisions.md(testing.scripted-playout-golden-deferred) anddocs/testing.md.🔗 Session bundle — E2E / test-harness infra: #445, #533, #563
Test-harness infrastructure: #445 (UI-interactive Playwright headless flows for functional-E2E), #533 (e2e-local.sh readiness probe hangs on a reused config dir), #563 (scripted-playout integration harness deferred from #381). Sibling to the just-landed #381 golden work; all live in the E2E/test tooling.
When you claim any member, check this cluster for co-workable siblings and sweep the disjoint set in one session (per docs/handoffs/chicorytv-issue-queue.md → bundles).
Claiming #563 (process.parallel-session-claim).
563-scripted-playout-integration-harness/Users/timothy/orca/workspaces/ersatztv/wt-563origin/main@4a56d54283ef9be1f1b8ca50f8553b09d65720e8Four-way check before the label: no open PR body references #563 (5 open PRs, all Renovate),
git ls-remote --heads origin '*563*'is empty, the only pre-existing comment is the 2026-07-22 bundle note (#445 and #533 both closed), andorigin/mainwas re-fetched.Closing record
Outcome: PR http://192.168.1.95:3000/timothy/ersatztv/pulls/915 (
563-scripted-playout-integration-harness@4454f1adfcdc5a4d94a848b3896a1ff3f739c73c). Takes the second arm from this issue:SchedulingEngineTestsexpanded from 1 to 21 tests covering the engine build API, plusScriptedScheduleControllerTestsreplaying a committed script fixture through the realScriptedScheduleControlleras the documented in-process stand-in for theCli.Wrap/HTTP shell-out, which remains explicitly out of scope.Root cause: N/A (test-harness build-out, not a bug fix). The original gap:
ScriptedPlayoutBuildershells out viaCli.Wrapto an external script process talking toSchedulingEngineover HTTP loopback, which the shared in-memory-SQLite golden harness from #381 doesn't fit.Decisions/conventions changed:
testing.scripted-engine-in-process-net(new, active) supersedestesting.scripted-playout-golden-deferred(git mv'd todocs/decisions/archive/testing/, frontmatter retargeted, body byte-identical).docs/decisions.mdand the generated catalog (docs/decisions/README.md) updated accordingly.Reusable knowledge: The replay fixture must bind through the production Newtonsoft configuration (
ErsatzTV/Serialization/ApiJsonSettings.cs, shared withStartupandOpenApiSerializerContractTests) — aSystem.Text.Jsonstand-in silently diverges on a body omitting arequiredmember. And a trimming instruction (AddDuration/PadUntilExact) that lands exactly on a content boundary silences its ownTrimflag mutant — a fixture must aim off-boundary or the controller'srequest.Trimargument is witnessed by nothing. Both are now stated in the decision record'srule:.Verification:
dotnet build ErsatzTV.sln -c Debug: 0 Warning(s), 0 Error(s).dotnet teston all five relevant projects:ErsatzTV.Tests2111 passed/6 skipped/0 failed;ErsatzTV.Core.Tests716/1/0;ErsatzTV.Infrastructure.Tests114/0/0;ErsatzTV.FFmpeg.Tests83/0/0;ErsatzTV.Scanner.Tests1504/0/0;ErsatzTV.Mcp.Tests61/0/0;ErsatzTV.Architecture.Tests7/0/0.ScriptedScheduleController.PadUntilExact(..., request.Trim, ...) -> falsereddensCommitted_Script_Fixture_Produces_The_Pinned_Snapshot(Failed: 1, Passed: 8, Total: 9); mutant reverted viagit checkout --, tree verified clean.SchedulingEngineTests/ScriptedScheduleControllerTestsverified passing (not skipped) underTZ=UTC, America/New_York, Australia/Lord_Howe, Asia/Kathmandu(per #24 rule).PYTHONPATH=. python3 scripts/decisions_validate.py: OK, exit 0.build_decisions_catalog.py: no diff.check-doc-narrative.py --diff origin/main: 0 warnings.dotnet format whitespace --verify-no-changeson every touched.csfile: clean.Deferred: The
Cli.Wrap/HTTP shell-out itself (external script process launch, real Kestrel endpoint,ScriptedPlayoutBuilderService.StartSessionover loopback) stays untested — the first arm of this issue, not taken. A pre-existing 401 blind spot onScriptedScheduleController(no request-key credential supplied anywhere in the scripted pipeline) surfaced during this work and is filed as ersatztv#913.Docs updated:
docs/testing.md(new "Scripted playout coverage" section + TZ-independence rationale),docs/decisions.md,docs/decisions/README.md(regenerated catalog),docs/decisions/records/testing/scripted-engine-in-process-net.md(new),docs/decisions/archive/testing/scripted-playout-golden-deferred.md(moved),docs/api-conventions.md(binder-settings note).Done-when evidence (boxes left unticked for the orchestrator to check):
SchedulingEngineTestscovering the engine build API with the process/HTTP shell-out explicitly scoped out (documented reasoning) — evidenced byErsatzTV.Core.Tests/Scheduling/Engine/SchedulingEngineTests.cs(1→21 tests) andErsatzTV.Tests/Controllers/ScriptedScheduleControllerTests.cs(9 tests replayingFixtures/scripted-build.jsonthrough the real controller, pinned 13-item snapshot), plusdocs/decisions/records/testing/scripted-engine-in-process-net.md's explicit scope-out of theCli.Wrap/HTTP shell-out.docs/testing.md.docs/testing.mdupdated — evidenced by the diff in PR #915 (new "Scripted playout coverage" section, Timezone independence section).