feat(core): add safe Rust wire session core

This commit is contained in:
sechmachine
2026-08-12 14:03:53 +07:00
parent 12ad2a4daa
commit 48ee082c0b
15 changed files with 2115 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
# Protocol fixture provenance
- Repository: `git.sechmachine.io.vn/sechmachine/VerseVDI-Protocol`
- Commit: `4693102b3ccbb81aeb1144c1a3b0884ee682bfa3`
- Tag: `v1.0.0-phase3d-macos-rc.5`
- Schema SHA-256: `b2353c12269304289b4e872f27cc370ae61b958dea90d9fb7b6ab8afd7d37248`
- Fixture corpus SHA-256: `6d2ce3a855b2fa45733a5f7b5b4c2e68448cceed5dfbca535ec81fe8cf230b30`
Copied byte-for-byte from `fixtures/conformance/tunnel-v1.tsv`,
`fixtures/conformance/datagram-v2.tsv`,
`fixtures/conformance/gateway-input-feedback-v1.tsv`, and
`fixtures/manifest.json`. RC4 is superseded and is not an authority for these
fixtures.
+10
View File
@@ -0,0 +1,10 @@
id version kind input expected
v2-valid-video-single 2 datagram hex=5644020a00000000010000000000000002000000010003010203 valid
v2-valid-video-last-fragment 2 datagram hex=5644020a00000000010000000000000002037a037b0000 valid
v2-invalid-short 2 datagram hex=564402 invalid:truncated
v2-invalid-version 2 datagram hex=5644030a00000000010000000000000002000000010000 invalid:unsupported_version
v2-invalid-channel 2 datagram hex=5644020d00000000010000000000000002000000010000 invalid:unknown_channel
v2-invalid-fragment-zero 2 datagram hex=5644020a00000000010000000000000002000000000000 invalid:fragment
v2-invalid-fragment-index 2 datagram hex=5644020a00000000010000000000000002000100010000 invalid:fragment
v2-invalid-fragment-count-limit 2 datagram hex=5644020a000000000100000000000000020000037c0000 invalid:fragment_limit
v2-invalid-length 2 datagram hex=5644020a00000000010000000000000002000000010001 invalid:length_mismatch
1 id version kind input expected
2 v2-valid-video-single 2 datagram hex=5644020a00000000010000000000000002000000010003010203 valid
3 v2-valid-video-last-fragment 2 datagram hex=5644020a00000000010000000000000002037a037b0000 valid
4 v2-invalid-short 2 datagram hex=564402 invalid:truncated
5 v2-invalid-version 2 datagram hex=5644030a00000000010000000000000002000000010000 invalid:unsupported_version
6 v2-invalid-channel 2 datagram hex=5644020d00000000010000000000000002000000010000 invalid:unknown_channel
7 v2-invalid-fragment-zero 2 datagram hex=5644020a00000000010000000000000002000000000000 invalid:fragment
8 v2-invalid-fragment-index 2 datagram hex=5644020a00000000010000000000000002000100010000 invalid:fragment
9 v2-invalid-fragment-count-limit 2 datagram hex=5644020a000000000100000000000000020000037c0000 invalid:fragment_limit
10 v2-invalid-length 2 datagram hex=5644020a00000000010000000000000002000000010001 invalid:length_mismatch
+34
View File
@@ -0,0 +1,34 @@
id version kind input expected
valid-keyboard-press 1 gateway_input hex=5647493101040102001e valid
valid-keyboard-release 1 gateway_input hex=5647493101040000001e valid
valid-mouse-button 1 gateway_input hex=564749310203010100 valid
valid-mouse-release 1 gateway_input hex=564749310203000100 valid
valid-relative-mouse 1 gateway_input hex=564749310304fffe0003 valid
valid-utf8-scalar 1 gateway_input hex=564749310403e29883 valid
valid-controller 1 gateway_input hex=5647493105110200030004ffff00010002000300040005 valid
valid-controller-release 1 gateway_input hex=5647493105110200000000000000000000000000000000 valid
valid-absolute-mouse 1 gateway_input hex=56474931060804d202370a0005a0 valid
valid-scroll 1 gateway_input hex=564749310704ff880078 valid
valid-idr 1 gateway_feedback hex=5647463100010000 valid
valid-fec 1 gateway_feedback hex=56474631000200150000002a000500030002000a000200080002140001 valid
valid-terminal-receipt 1 gateway_feedback hex=5647463100030000 valid
valid-termination 1 gateway_feedback hex=564746310110000400000001 valid
valid-rumble 1 gateway_feedback hex=56474631011100050112345678 valid
valid-hdr 1 gateway_feedback hex=564746310112000101 valid
invalid-input-magic 1 gateway_input hex=494e503101040102001e invalid:magic
invalid-input-kind 1 gateway_input hex=564749317f00 invalid:kind
invalid-input-reserved 1 gateway_input hex=564749310203010101 invalid:reserved
invalid-input-utf8 1 gateway_input hex=564749310402c328 invalid:utf8
invalid-input-length 1 gateway_input hex=564749310104010200 invalid:length
invalid-absolute-zero-viewport 1 gateway_input hex=56474931060800000000000005a0 invalid:field
invalid-absolute-x-out-of-range 1 gateway_input hex=5647493106080a0000000a0005a0 invalid:field
invalid-absolute-y-out-of-range 1 gateway_input hex=564749310608000005a00a0005a0 invalid:field
invalid-absolute-length 1 gateway_input hex=56474931060700000000010001 invalid:length
invalid-scroll-length 1 gateway_input hex=5647493107020000 invalid:length
invalid-feedback-direction 1 gateway_feedback hex=5647463101020000 invalid:direction
invalid-terminal-receipt-direction 1 gateway_feedback hex=5647463101030000 invalid:direction
invalid-terminal-receipt-body 1 gateway_feedback hex=5647463100030001ff invalid:length
invalid-terminal-receipt-truncated 1 gateway_feedback hex=56474631000300 invalid:truncated
invalid-terminal-receipt-length 1 gateway_feedback hex=5647463100030001 invalid:length
invalid-feedback-type 1 gateway_feedback hex=5647463100040000 invalid:type
invalid-feedback-length 1 gateway_feedback hex=5647463101100003000000 invalid:length
1 id version kind input expected
2 valid-keyboard-press 1 gateway_input hex=5647493101040102001e valid
3 valid-keyboard-release 1 gateway_input hex=5647493101040000001e valid
4 valid-mouse-button 1 gateway_input hex=564749310203010100 valid
5 valid-mouse-release 1 gateway_input hex=564749310203000100 valid
6 valid-relative-mouse 1 gateway_input hex=564749310304fffe0003 valid
7 valid-utf8-scalar 1 gateway_input hex=564749310403e29883 valid
8 valid-controller 1 gateway_input hex=5647493105110200030004ffff00010002000300040005 valid
9 valid-controller-release 1 gateway_input hex=5647493105110200000000000000000000000000000000 valid
10 valid-absolute-mouse 1 gateway_input hex=56474931060804d202370a0005a0 valid
11 valid-scroll 1 gateway_input hex=564749310704ff880078 valid
12 valid-idr 1 gateway_feedback hex=5647463100010000 valid
13 valid-fec 1 gateway_feedback hex=56474631000200150000002a000500030002000a000200080002140001 valid
14 valid-terminal-receipt 1 gateway_feedback hex=5647463100030000 valid
15 valid-termination 1 gateway_feedback hex=564746310110000400000001 valid
16 valid-rumble 1 gateway_feedback hex=56474631011100050112345678 valid
17 valid-hdr 1 gateway_feedback hex=564746310112000101 valid
18 invalid-input-magic 1 gateway_input hex=494e503101040102001e invalid:magic
19 invalid-input-kind 1 gateway_input hex=564749317f00 invalid:kind
20 invalid-input-reserved 1 gateway_input hex=564749310203010101 invalid:reserved
21 invalid-input-utf8 1 gateway_input hex=564749310402c328 invalid:utf8
22 invalid-input-length 1 gateway_input hex=564749310104010200 invalid:length
23 invalid-absolute-zero-viewport 1 gateway_input hex=56474931060800000000000005a0 invalid:field
24 invalid-absolute-x-out-of-range 1 gateway_input hex=5647493106080a0000000a0005a0 invalid:field
25 invalid-absolute-y-out-of-range 1 gateway_input hex=564749310608000005a00a0005a0 invalid:field
26 invalid-absolute-length 1 gateway_input hex=56474931060700000000010001 invalid:length
27 invalid-scroll-length 1 gateway_input hex=5647493107020000 invalid:length
28 invalid-feedback-direction 1 gateway_feedback hex=5647463101020000 invalid:direction
29 invalid-terminal-receipt-direction 1 gateway_feedback hex=5647463101030000 invalid:direction
30 invalid-terminal-receipt-body 1 gateway_feedback hex=5647463100030001ff invalid:length
31 invalid-terminal-receipt-truncated 1 gateway_feedback hex=56474631000300 invalid:truncated
32 invalid-terminal-receipt-length 1 gateway_feedback hex=5647463100030001 invalid:length
33 invalid-feedback-type 1 gateway_feedback hex=5647463100040000 invalid:type
34 invalid-feedback-length 1 gateway_feedback hex=5647463101100003000000 invalid:length
+15
View File
@@ -0,0 +1,15 @@
{
"algorithm": "sha256(path\\0bytes\\0 sorted by path)",
"files": [
"fixtures/conformance/control-v1.tsv",
"fixtures/conformance/datagram-v1.tsv",
"fixtures/conformance/datagram-v2.tsv",
"fixtures/conformance/device-proof-v1.tsv",
"fixtures/conformance/events-v1.tsv",
"fixtures/conformance/gateway-clipboard-audit-v1.tsv",
"fixtures/conformance/gateway-clipboard-v1.tsv",
"fixtures/conformance/gateway-input-feedback-v1.tsv",
"fixtures/conformance/tunnel-v1.tsv"
],
"corpus_sha256": "6d2ce3a855b2fa45733a5f7b5b4c2e68448cceed5dfbca535ec81fe8cf230b30"
}
+9
View File
@@ -0,0 +1,9 @@
id version kind input expected
tunnel-current 2 tunnel offered=2;feature=control.v2 valid
tunnel-n-minus-1 1 tunnel offered=1;feature=control.v1 valid
tunnel-n-minus-2 0 tunnel offered=0;feature=control.v1 valid
tunnel-display-request 2 tunnel offered=2;feature=display.request.v1 valid
tunnel-absolute-input 2 tunnel offered=2;feature=input.absolute.v1 valid
tunnel-scroll-input 2 tunnel offered=2;feature=input.scroll.v1 valid
tunnel-unsupported 2 tunnel offered=3;feature=control.v2 invalid:unsupported_version
tunnel-no-control 2 tunnel offered=2;feature=media.video invalid:unsupported_feature
1 id version kind input expected
2 tunnel-current 2 tunnel offered=2;feature=control.v2 valid
3 tunnel-n-minus-1 1 tunnel offered=1;feature=control.v1 valid
4 tunnel-n-minus-2 0 tunnel offered=0;feature=control.v1 valid
5 tunnel-display-request 2 tunnel offered=2;feature=display.request.v1 valid
6 tunnel-absolute-input 2 tunnel offered=2;feature=input.absolute.v1 valid
7 tunnel-scroll-input 2 tunnel offered=2;feature=input.scroll.v1 valid
8 tunnel-unsupported 2 tunnel offered=3;feature=control.v2 invalid:unsupported_version
9 tunnel-no-control 2 tunnel offered=2;feature=media.video invalid:unsupported_feature
+373
View File
@@ -0,0 +1,373 @@
use std::path::PathBuf;
use std::process::Command;
use versevdi_core::input::{decode_feedback, decode_input, encode_feedback, encode_input};
use versevdi_core::media::{MediaFragment, Reassembler};
use versevdi_core::wire::{
CapabilityProfile, ClientSessionAuthority, ConnectionManifest, NativeTunnelCredential,
TunnelAdmissionRequest,
};
fn fixture(name: &str) -> PathBuf {
PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures")
.join(name)
}
#[test]
fn copied_protocol_rc5_fixtures_have_immutable_hashes() {
let cases = [
(
"tunnel-v1.tsv",
"31a884800031c17844b9a8789702cf0d3639838ccfeb819c2bc3f0a5462ac5df",
),
(
"datagram-v2.tsv",
"65b9f6f018af624033562a78aae0b685df331c5e857d5f3d5d415aefa9d5b97b",
),
(
"gateway-input-feedback-v1.tsv",
"91b4dc1eb756476637ea7deebcb297256cb56d8e9c3d635088511af6b8914bda",
),
(
"manifest.json",
"76c33b33864d85f7d4a3761798eab52ffadbbe5fc313cf4e6e03369cfdc8df9a",
),
];
for (name, expected) in cases {
let output = Command::new("shasum")
.args(["-a", "256"])
.arg(fixture(name))
.output()
.expect("shasum must be installed for fixture verification");
assert!(output.status.success(), "shasum failed for {name}");
let actual = String::from_utf8(output.stdout).expect("shasum output is UTF-8");
assert_eq!(&actual[..64], expected, "fixture drifted: {name}");
}
}
fn valid_manifest() -> &'static [u8] {
br#"{
"version":"1","purpose":"launch","session_id":"session","reconnect_sequence":0,
"gateway":{"id":"gateway","addresses":["gateway.test:443"],"public_identity":"gateway.test"},
"tunnel":{"versions":["verse-gateway-v1/1"],"features":["control.v1","input.absolute.v1","input.scroll.v1"]},
"profile":{"id":"standard","bounds":{"minimum_kbps":1000,"target_kbps":5000,"maximum_kbps":10000},"display_mode":{"resolution_width":1920,"resolution_height":1080,"fps":60}},
"grant":{"opaque_value":"ggggggggggggggggggggggggggggggggggggggggggg","expires_at":"2099-01-01T00:00:00Z","audience":"audience"},
"correlation_id":"correlation"
}"#
}
fn capabilities() -> CapabilityProfile {
CapabilityProfile::new(
"quic-tls13",
"datagram-v2",
"encoded",
"encoded",
"server",
vec!["h264-opus".to_owned(), "hevc-opus".to_owned()],
)
.expect("literal capability profile is valid")
}
#[test]
fn strict_rc5_dtos_reject_duplicate_trailing_unknown_and_provider_fields() {
assert!(ConnectionManifest::decode(valid_manifest()).is_ok());
assert!(
ConnectionManifest::decode(br#"{"version":"1","version":"1","purpose":"launch"}"#).is_err()
);
let trailing = [valid_manifest(), b" {}"].concat();
assert!(ConnectionManifest::decode(&trailing).is_err());
let unknown = String::from_utf8(valid_manifest().to_vec())
.expect("fixture is UTF-8")
.replacen(
"\"correlation_id\"",
"\"unknown\":true,\"correlation_id\"",
1,
);
assert!(ConnectionManifest::decode(unknown.as_bytes()).is_err());
let provider = String::from_utf8(valid_manifest().to_vec())
.expect("fixture is UTF-8")
.replacen(
"\"addresses\"",
"\"providerIdentity\":\"hidden\",\"addresses\"",
1,
);
assert!(ConnectionManifest::decode(provider.as_bytes()).is_err());
let authority = br#"{"version":"1","session_id":"session","gateway_id":"gateway","audience":"audience","reconnect_sequence":0,"expires_at":"2098-01-01T00:00:00Z","capabilities":{"transport":"quic-tls13","framing":"datagram-v2","media":"encoded","audio":"encoded","source_rate_control":"server","client_decode":["h264-opus"]}}"#;
let authority_trailing = [authority.as_slice(), b" {}"].concat();
assert!(ClientSessionAuthority::decode(&authority_trailing).is_err());
let authority_duplicate = String::from_utf8(authority.to_vec())
.expect("fixture is UTF-8")
.replacen(
"\"session_id\":",
"\"session_id\":\"duplicate\",\"session_id\":",
1,
);
assert!(ClientSessionAuthority::decode(authority_duplicate.as_bytes()).is_err());
let admission_provider = format!(
r#"{{"version":"1","session_id":"session","gateway_id":"gateway","audience":"audience","grant":"{}","reconnect_sequence":0,"client_nonce":"{}","device_signature":"{}","provider_identity":"forbidden","capabilities":{{"transport":"quic-tls13","framing":"datagram-v2","media":"encoded","audio":"encoded","source_rate_control":"server","client_decode":["h264-opus"]}}}}"#,
"g".repeat(43),
"n".repeat(16),
"A".repeat(86),
);
assert!(TunnelAdmissionRequest::decode(admission_provider.as_bytes()).is_err());
}
#[test]
fn rc5_manifest_credential_and_authority_enforce_bounds_and_bindings() {
let manifest = ConnectionManifest::decode(valid_manifest()).expect("valid manifest");
manifest
.validate_at("2026-08-12T00:00:00Z")
.expect("unexpired manifest");
let credential = NativeTunnelCredential::decode(
br#"{"client_device_id":"device","device_key_id":"key","certificate_chain_pem":"certificate","trust_bundle_pem":"root","expires_at":"2099-01-01T00:00:00Z"}"#,
)
.expect("valid credential");
credential
.validate_at("2026-08-12T00:00:00Z")
.expect("unexpired credential");
assert!(NativeTunnelCredential::decode(
br#"{"client_device_id":"device","device_key_id":"key","certificate_chain_pem":"certificate","trust_bundle_pem":"root","client_private_key_pem":"forbidden","expires_at":"2099-01-01T00:00:00Z"}"#,
)
.is_err());
let authority = ClientSessionAuthority::decode(
br#"{"version":"1","session_id":"session","gateway_id":"gateway","audience":"audience","reconnect_sequence":0,"expires_at":"2098-01-01T00:00:00Z","capabilities":{"transport":"quic-tls13","framing":"datagram-v2","media":"encoded","audio":"encoded","source_rate_control":"server","client_decode":["h264-opus"]}}"#,
)
.expect("valid client-safe authority");
authority
.validate_binding(&manifest, &capabilities(), "2026-08-12T00:00:00Z")
.expect("authority is bound and is a capability subset");
let provider_authority = br#"{"version":"1","session_id":"session","gateway_id":"gateway","audience":"audience","reconnect_sequence":0,"expires_at":"2098-01-01T00:00:00Z","capabilities":{"transport":"quic-tls13","framing":"datagram-v2","media":"encoded","audio":"encoded","source_rate_control":"server","client_decode":["h264-opus"]},"provider_profile":"apollo"}"#;
assert!(ClientSessionAuthority::decode(provider_authority).is_err());
}
#[test]
fn expiry_mismatch_and_capability_escalation_are_rejected() {
let manifest = ConnectionManifest::decode(valid_manifest()).expect("valid manifest");
assert!(manifest.validate_at("2100-01-01T00:00:00Z").is_err());
let credential = NativeTunnelCredential::decode(
br#"{"client_device_id":"device","device_key_id":"key","certificate_chain_pem":"certificate","trust_bundle_pem":"root","expires_at":"2099-01-01T00:00:00Z"}"#,
)
.expect("valid credential");
assert!(credential.validate_at("2099-01-01T00:00:00Z").is_err());
let mismatched = ClientSessionAuthority::decode(
br#"{"version":"1","session_id":"other","gateway_id":"gateway","audience":"audience","reconnect_sequence":0,"expires_at":"2098-01-01T00:00:00Z","capabilities":{"transport":"quic-tls13","framing":"datagram-v2","media":"encoded","audio":"encoded","source_rate_control":"server","client_decode":["h264-opus"]}}"#,
)
.expect("shape is valid");
assert!(mismatched
.validate_binding(&manifest, &capabilities(), "2026-08-12T00:00:00Z")
.is_err());
let escalated = ClientSessionAuthority::decode(
br#"{"version":"1","session_id":"session","gateway_id":"gateway","audience":"audience","reconnect_sequence":0,"expires_at":"2098-01-01T00:00:00Z","capabilities":{"transport":"quic-tls13","framing":"datagram-v2","media":"encoded","audio":"encoded","source_rate_control":"server","client_decode":["h264-opus","hevc-opus"]}}"#,
)
.expect("shape is valid");
let h264_only = CapabilityProfile::new(
"quic-tls13",
"datagram-v2",
"encoded",
"encoded",
"server",
vec!["h264-opus".to_owned()],
)
.expect("literal capability profile");
assert!(escalated
.validate_binding(&manifest, &h264_only, "2026-08-12T00:00:00Z")
.is_err());
}
#[test]
fn admission_transcript_matches_rc5_literal() {
let request = TunnelAdmissionRequest::decode(
format!(
r#"{{"version":"1","session_id":"session","gateway_id":"gateway","audience":"audience","grant":"{}","reconnect_sequence":0,"client_nonce":"{}","device_signature":"{}","capabilities":{{"transport":"quic-tls13","framing":"datagram-v1","media":"encoded","audio":"encoded","source_rate_control":"server","client_decode":["h264-opus"]}}}}"#,
"g".repeat(43),
"n".repeat(16),
"A".repeat(86),
)
.as_bytes(),
)
.expect("valid admission request");
assert_eq!(
request.admission_transcript(),
format!(
"versevdi/tunnel-admission/v17:session7:gateway8:audience43:{}1:016:{}10:quic-tls1311:datagram-v17:encoded7:encoded6:server1:19:h264-opus",
"g".repeat(43),
"n".repeat(16),
)
.into_bytes()
);
}
fn decode_hex(value: &str) -> Vec<u8> {
value
.as_bytes()
.chunks_exact(2)
.map(|pair| {
let text = std::str::from_utf8(pair).expect("fixture hex is ASCII");
u8::from_str_radix(text, 16).expect("fixture hex is valid")
})
.collect()
}
fn fixture_rows(name: &str) -> impl Iterator<Item = Vec<&'static str>> {
let data = match name {
"datagram-v2.tsv" => include_str!("fixtures/datagram-v2.tsv"),
"gateway-input-feedback-v1.tsv" => {
include_str!("fixtures/gateway-input-feedback-v1.tsv")
}
_ => unreachable!("known fixture"),
};
data.lines()
.skip(1)
.map(|line| line.split('\t').collect::<Vec<_>>())
}
#[test]
fn datagram_v2_codec_matches_all_rc5_conformance_rows() {
for row in fixture_rows("datagram-v2.tsv") {
let bytes = decode_hex(row[3].strip_prefix("hex=").expect("hex fixture"));
match row[4] {
"valid" => {
let fragment = MediaFragment::decode(&bytes).expect(row[0]);
assert_eq!(fragment.encode().expect(row[0]), bytes, "{}", row[0]);
}
error => assert_eq!(
MediaFragment::decode(&bytes).expect_err(row[0]).code(),
error.strip_prefix("invalid:").expect("invalid fixture"),
"{}",
row[0]
),
}
}
}
#[test]
fn vgi1_and_vgf1_codecs_match_all_rc5_conformance_rows() {
let features = ["input.absolute.v1", "input.scroll.v1"];
for row in fixture_rows("gateway-input-feedback-v1.tsv") {
let bytes = decode_hex(row[3].strip_prefix("hex=").expect("hex fixture"));
let result = match row[2] {
"gateway_input" => {
decode_input(&bytes, &features).and_then(|event| encode_input(&event, &features))
}
"gateway_feedback" => decode_feedback(&bytes).and_then(|event| encode_feedback(&event)),
_ => unreachable!("known fixture kind"),
};
match row[4] {
"valid" => assert_eq!(result.expect(row[0]), bytes, "{}", row[0]),
error => assert_eq!(
result.expect_err(row[0]).code(),
error.strip_prefix("invalid:").expect("invalid fixture"),
"{}",
row[0]
),
}
}
}
#[test]
fn datagram_v2_reassembly_is_bounded_reordered_and_duplicate_safe() {
let first = MediaFragment::new_video(7, 42, 0, 2, b"hello".to_vec()).expect("fragment");
let second = MediaFragment::new_video(7, 42, 1, 2, b" world".to_vec()).expect("fragment");
let mut reassembler = Reassembler::new();
assert!(reassembler
.push(second.clone(), 10)
.expect("second")
.is_none());
assert!(reassembler.push(second, 11).expect("duplicate").is_none());
let unit = reassembler
.push(first, 12)
.expect("first")
.expect("complete unit");
assert_eq!(unit.payload, b"hello world");
assert_eq!(reassembler.incomplete_units(), 0);
for sequence in 0..5 {
let fragment = MediaFragment::new_video(sequence, 1, 0, 2, vec![0]).expect("fragment");
assert!(reassembler.push(fragment, 20 + u64::from(sequence)).is_ok());
}
assert_eq!(reassembler.incomplete_units(), 4);
assert_eq!(reassembler.evicted_units(), 1);
}
#[test]
fn datagram_v2_boundary_loop_covers_payload_fragment_count_and_audio() {
for payload_length in [0, 1, 1_177] {
let fragment = MediaFragment::new_audio(
u32::try_from(payload_length).expect("small"),
1,
0,
1,
vec![0; payload_length],
)
.expect("boundary is valid");
assert_eq!(
MediaFragment::decode(&fragment.encode().expect("encode")).expect("decode"),
fragment
);
}
assert!(MediaFragment::new_audio(1, 1, 0, 1, vec![0; 1_178]).is_err());
assert!(MediaFragment::new_audio(1, 1, 0, 0, Vec::new()).is_err());
assert!(MediaFragment::new_audio(1, 1, 0, 892, Vec::new()).is_err());
}
#[test]
fn reassembly_discards_conflicts_and_expires_after_250_ms() {
let mut reassembler = Reassembler::new();
let first = MediaFragment::new_video(1, 1, 0, 2, vec![1]).expect("fragment");
reassembler.push(first, 0).expect("first");
let conflict = MediaFragment::new_video(1, 1, 0, 2, vec![2]).expect("fragment");
assert!(reassembler.push(conflict, 1).is_err());
assert_eq!(reassembler.incomplete_units(), 0);
let expiring = MediaFragment::new_video(2, 1, 0, 2, vec![1]).expect("fragment");
reassembler.push(expiring, 10).expect("first");
let next = MediaFragment::new_video(3, 1, 0, 2, vec![1]).expect("fragment");
reassembler.push(next, 261).expect("expiry sweep");
assert_eq!(reassembler.expired_units(), 1);
}
#[test]
fn reassembly_rejects_a_complete_unit_above_one_mebibyte() {
let mut reassembler = Reassembler::new();
for index in 0..891_u16 {
let fragment = MediaFragment::new_video(9, 1, index, 891, vec![0; 1_177])
.expect("individual fragment is bounded");
let result = reassembler.push(fragment, 0);
if index < 890 {
assert!(result.expect("within aggregate bound").is_none());
} else {
assert!(result.is_err());
}
}
assert_eq!(reassembler.incomplete_units(), 0);
}
#[test]
fn input_features_and_feedback_booleans_fail_closed() {
assert_eq!(
decode_input(&decode_hex("56474931060804d202370a0005a0"), &[])
.expect_err("absolute feature is required")
.code(),
"unsupported_feature"
);
assert_eq!(
decode_feedback(&decode_hex("564746310112000102"))
.expect_err("HDR is boolean")
.code(),
"length"
);
}