Skip to content

test(samples): guard dynamic samples with parse/compile + node-availability checks#543

Merged
streamer45 merged 6 commits into
mainfrom
devin/1780156850-dynamic-samples-coverage
May 30, 2026
Merged

test(samples): guard dynamic samples with parse/compile + node-availability checks#543
streamer45 merged 6 commits into
mainfrom
devin/1780156850-dynamic-samples-coverage

Conversation

@staging-devin-ai-integration
Copy link
Copy Markdown
Contributor

@staging-devin-ai-integration staging-devin-ai-integration Bot commented May 30, 2026

Summary

  • Adds two registry-independent tests that lock the shipped samples/pipelines/dynamic/ pipelines to the YAML/compiler + node-registry contracts, so future drift is caught in CI rather than at runtime.
  • crates/api (test_all_dynamic_samples_parse_and_compile): every dynamic sample must parse_yaml + compile cleanly (schema, needs references, pin resolution, cycle rules).
  • apps/skit (every_dynamic_sample_node_kind_is_available_or_known): every node kind a sample references is either registered in the default build or a recognized external kind — a marketplace plugin (plugin::…) or a codec behind a non-default cargo feature / dedicated GPU (svt_av1, dav1d, nvcodec, vaapi, vulkan_video).
  • Why it matters: the compiler is registry-agnostic, so a renamed/mistyped builtin node kind compiles fine yet silently fails at runtime (the session is created, the node just never starts — no clear error). The availability test closes that gap with no GPU, plugins, or models required.
  • Relocates test_overlay_controls.yml out of the shipped samples. It's a Playwright fixture (colorbars→core::sink driving overlay-controls.spec.ts), not a user-facing sample, yet it appeared in the Stream view sample picker as "Test: Overlay Controls". Moved to e2e/fixtures/overlay-controls.yaml; the spec now loads the YAML straight into the Stream view's editor instead of clicking the sample card. The loader scans samples/pipelines/dynamic at runtime, so this drops it from /api/v1/samples/dynamic with no loader/samples.rs change.
  • Outcome of the S3 dynamic-sample triage: all remaining 40 dynamic samples pass both guard tests; no other samples changed or removed.

Review & Validation

  • cargo test -p streamkit-api --lib test_all_dynamic_samples_parse_and_compile
  • cargo test -p streamkit-server --test dynamic_samples_node_availability_test
  • just e2e-external http://localhost:4545overlay-controls.spec.ts still passes (now loads the relocated fixture).
  • Sanity-check the FEATURE_GATED_KINDS allowlist in the new skit test matches the codec/HW kinds intended to be absent from the default build.

Notes

  • Triage scope was dynamic samples only (oneshot = S2; sample discovery-UX/grouping = S4); this PR is limited to test coverage + the one approved fixture relocation.
  • The guard tests auto-discover the samples directory (no hard-coded count), so the 41→40 change required no test edits.
  • Browser E2E for representative dynamic samples (colorbars canvas pixels, webcam-PiP a/v, compositor monitor) already exists in e2e/ and passes against a local MoQ server; these headless guards complement it.

Link to Devin session: https://staging.itsdev.in/sessions/18d695809f504cea85bd63b318684b3d
Requested by: @streamer45


Devin Review

Status Commit
🕐 Outdated 29b7353 (HEAD is f9159ac)

Run Devin Review

Open in Devin Review (Staging)

…bility checks

Add two registry-independent tests that catch drift between the shipped
dynamic sample pipelines and the YAML/compiler + node-registry contracts:

- crates/api: every sample in samples/pipelines/dynamic must parse and
  compile (schema, needs references, pin resolution, cycle rules).
- apps/skit: every node kind a sample references is either registered in
  the default build or a known external kind (marketplace plugin or a
  codec behind a non-default cargo feature / dedicated hardware).

The compiler is registry-agnostic, so a renamed or mistyped node kind
compiles fine yet silently fails at runtime when the engine asks the
registry to build the node. The node-availability test closes that gap
in CI without requiring a GPU, plugins, or models.

Signed-off-by: streamkit-devin <devin@streamkit.dev>
@staging-devin-ai-integration
Copy link
Copy Markdown
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment, CI, and merge conflict monitoring

Copy link
Copy Markdown
Contributor Author

@staging-devin-ai-integration staging-devin-ai-integration Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 5 potential issues.

Open in Devin Review (Staging)
Debug

Playground

//! (`plugin::…`) or a codec behind a non-default cargo feature / dedicated
//! hardware. Anything else (e.g. a typo) fails the test.

#![allow(clippy::expect_used, clippy::panic)]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Lint suppression is missing the required rationale

AGENTS.md requires every lint suppression to include a rationale comment ("Always include ... // reason (#[allow])"), but this new crate-level #[allow] suppresses expect_used/panic without explaining why the suppression is necessary. This violates the repository's mandatory linting-discipline rule for new code.

Suggested change
#![allow(clippy::expect_used, clippy::panic)]
// Test fixture checks should fail fast with contextual assertion messages.
#![allow(clippy::expect_used, clippy::panic)]
Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — added the rationale comment to this crate-level suppression in 1c73ee0.

/// *availability* (feature flags, plugins) is checked separately against a live
/// registry, since the compiler is registry-agnostic.
#[test]
#[allow(clippy::unwrap_used)]
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 New unwrap suppression lacks the required rationale

AGENTS.md requires lint suppressions to explain their rationale, but this new #[allow(clippy::unwrap_used)] has no accompanying reason. Even if the unwraps are acceptable in this fixture-style test, the suppression itself violates the repository's mandatory linting-discipline rule.

Suggested change
#[allow(clippy::unwrap_used)]
// Sample fixture traversal should fail fast so failures identify the offending file.
#[allow(clippy::unwrap_used)]
Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done — added a rationale comment above this #[allow(clippy::unwrap_used)] in 1c73ee0.

Comment on lines +29 to +36
const FEATURE_GATED_KINDS: &[&str] = &[
"video::svt_av1::encoder",
"video::dav1d::decoder",
"video::nv::av1_encoder",
"video::vaapi::av1_encoder",
"video::vaapi::h264_encoder",
"video::vulkan_video::h264_encoder",
];
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: Registry availability test intentionally validates only the default server feature set

The availability test constructs a fresh NodeRegistry and calls streamkit_nodes::register_nodes with GlobalNodeConstraints::new(), so it verifies what the streamkit-server test target can register under the feature set used for that test. This means samples using svt_av1, dav1d, nvcodec, vaapi, or vulkan_video are expected to remain allowlisted rather than validated by registry.contains. That is consistent with the test’s stated scope and with the feature-gated registrations in crates/nodes/src/video/mod.rs:677-693, but reviewers should update FEATURE_GATED_KINDS whenever new non-default dynamic sample kinds are added.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Comment on lines +103 to +108
// Every shipped sample either uses default nodes or a recognized external
// kind; `unverified_external` records the latter for debugging context.
assert!(
unverified_external.iter().all(|k| is_known_external(k)),
"unexpected external kinds: {unverified_external:?}"
);
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: The final external-kind assertion is redundant with the insertion guard

unverified_external is only populated after is_known_external(&kind) has already returned true at lines 89-91, so the later all(|k| is_known_external(k)) assertion cannot catch any additional failure unless the code above is changed incorrectly. This is harmless and may provide future-edit guardrails, but it does not currently add independent validation beyond the main failure collection.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intentional — it's a future-edit guardrail (cheap, no runtime cost). If someone later changes the insertion logic so an unknown kind reaches unverified_external, this catches it. Leaving as-is.

Comment on lines +197 to +203
match parse_yaml(&yaml) {
Ok(pipeline) => {
if let Err(e) = compile(pipeline) {
failures.push(format!("{name}: compile failed: {e}"));
}
},
Err(e) => failures.push(format!("{name}: parse failed: {e}")),
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📝 Info: The API-level sample compilation test deliberately remains registry-agnostic

test_all_dynamic_samples_parse_and_compile exercises parse_yaml and compile, and compile only performs structural validation such as dependency existence, pin mapping, cycle checks, and special mixer parameter injection; it does not consult NodeRegistry (crates/api/src/yaml/compiler.rs:9-17). That means this test will pass for unknown node kinds as long as the graph is structurally valid, which is intentional given the separate server registry test, not a coverage gap in this file.

Open in Devin Review (Staging)

Was this helpful? React with 👍 or 👎 to provide feedback.

Debug

Playground

streamkit-devin and others added 4 commits May 30, 2026 16:07
Add the rationale comments required by AGENTS.md linting discipline to the
#[allow] suppressions in the dynamic-sample parse/compile and node-availability
tests.

Signed-off-by: streamkit-devin <devin@streamkit.dev>
test_overlay_controls.yml is a Playwright fixture (the colorbars->core::sink
pipeline driving e2e/tests/overlay-controls.spec.ts), not a user-facing sample,
so it was being listed in the Stream view sample picker. Move it to
e2e/fixtures/overlay-controls.yaml and load it into the YAML editor directly
in the spec instead of selecting it from the sample list.

The dynamic-samples loader scans samples/pipelines/dynamic at runtime, so the
relocation drops it from /api/v1/samples/dynamic with no loader change. The
sample-guard tests auto-discover the directory and now cover 40 samples.

Signed-off-by: streamkit-devin <devin@streamkit.dev>
The overlay-controls fixture has no MoQ transport, but editing the YAML editor
directly (rather than picking a sample) leaves the broadcast names from the
auto-selected first sample in the store, so the post-create auto-connect
attempted a MoQ session and failed with a QUIC console error in CI. Clear the
input/output broadcast fields after loading the fixture.

Signed-off-by: streamkit-devin <devin@streamkit.dev>
@codecov
Copy link
Copy Markdown

codecov Bot commented May 30, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 79.99%. Comparing base (78f8332) to head (f9159ac).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #543      +/-   ##
==========================================
+ Coverage   79.96%   79.99%   +0.02%     
==========================================
  Files         234      234              
  Lines       68061    68061              
  Branches     1846     1933      +87     
==========================================
+ Hits        54426    54443      +17     
+ Misses      13629    13612      -17     
  Partials        6        6              
Flag Coverage Δ
backend 79.76% <ø> (+0.02%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Components Coverage Δ
core 85.37% <ø> (ø)
engine 83.03% <ø> (+0.09%) ⬆️
api 89.96% <ø> (ø)
nodes 76.89% <ø> (+0.04%) ⬆️
server 80.34% <ø> (+0.01%) ⬆️
plugin-native 83.70% <ø> (ø)
plugin-wasm 92.20% <ø> (ø)
ui-services 84.69% <ø> (ø)
ui-components 60.49% <ø> (ø)
see 3 files with indirect coverage changes
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

…ssue

Replace the tautological final assertion in the node-availability test with a
real invariant: every FEATURE_GATED_KINDS entry must be exercised by at least
one dynamic sample, so a stale allowlist entry left after a sample is removed
fails the test.

Reference issue #550 (Stream view doesn't re-derive MoQ settings on direct YAML
edits) next to the broadcast-clearing workaround in the overlay-controls spec.

Signed-off-by: streamkit-devin <devin@streamkit.dev>
@streamer45 streamer45 merged commit 6abe3ba into main May 30, 2026
29 checks passed
@streamer45 streamer45 deleted the devin/1780156850-dynamic-samples-coverage branch May 30, 2026 19:22
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants