mirror of
https://github.com/ruvnet/RuView
synced 2026-07-25 17:51:48 +00:00
eb996294fb
Iter 3. Lands the structural enforcement of ADR-118 invariant I1
("raw BFI never exits the node") and ADR-120 §2.2 ("Sink marker types").
Added:
- src/sink.rs:
* Sink trait with MIN_CLASS and KIND associated constants
* LocalSink (Raw OK), NetworkSink (Derived+ only), MatterSink (Anonymous+)
* Hierarchy: MatterSink: NetworkSink (every Matter sink is a NetworkSink)
* check_class<S>(class) runtime gate, returns PrivacyViolation{reason:KIND}
* Zero-sized kind tags: LocalKind / NetworkKind / MatterKind
- PrivacyClass::as_u8() const helper
- TryFrom<u8> for PrivacyClass (0..=3 valid; 4..=255 → InvalidPrivacyClass)
- BfldError::InvalidPrivacyClass(u8) variant
tests/sink_enforcement.rs adds 8 tests:
privacy_class_try_from_accepts_all_four_valid_bytes
privacy_class_try_from_rejects_out_of_range_bytes
privacy_class_byte_roundtrip_is_stable
local_sink_accepts_all_classes
network_sink_rejects_raw_frames
network_sink_accepts_derived_anonymous_restricted
matter_sink_rejects_raw_and_derived
matter_sink_accepts_anonymous_and_restricted
Out of scope (next iter):
- BfldFrame (header + payload + section length-prefixes + CRC32 over payload)
— needs the `crc` crate dependency.
- PrivacyGate::demote(frame, target_class) transformer (ADR-120 §2.4).
- compile-fail test that proves a sink-trait bound rejects Raw at compile
time — needs `trybuild` integration; deferred to a separate iter.
cargo test -p wifi-densepose-bfld --no-default-features → 17 passed, 0 failed
(3 frame_header_size + 6 header_roundtrip + 8 sink_enforcement)
Co-Authored-By: claude-flow <ruv@ruv.net>
94 lines
2.9 KiB
Rust
94 lines
2.9 KiB
Rust
//! Acceptance tests for ADR-120 §2.2 sink marker enforcement (invariant I1).
|
|
|
|
use wifi_densepose_bfld::sink::{LocalKind, MatterKind, NetworkKind};
|
|
use wifi_densepose_bfld::{check_class, BfldError, PrivacyClass};
|
|
|
|
// --- PrivacyClass::try_from ----------------------------------------------
|
|
|
|
#[test]
|
|
fn privacy_class_try_from_accepts_all_four_valid_bytes() {
|
|
assert_eq!(PrivacyClass::try_from(0).unwrap(), PrivacyClass::Raw);
|
|
assert_eq!(PrivacyClass::try_from(1).unwrap(), PrivacyClass::Derived);
|
|
assert_eq!(PrivacyClass::try_from(2).unwrap(), PrivacyClass::Anonymous);
|
|
assert_eq!(PrivacyClass::try_from(3).unwrap(), PrivacyClass::Restricted);
|
|
}
|
|
|
|
#[test]
|
|
fn privacy_class_try_from_rejects_out_of_range_bytes() {
|
|
for b in [4u8, 5, 7, 17, 42, 100, 200, 255] {
|
|
match PrivacyClass::try_from(b) {
|
|
Err(BfldError::InvalidPrivacyClass(got)) => assert_eq!(got, b),
|
|
other => panic!("expected InvalidPrivacyClass({b}), got {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn privacy_class_byte_roundtrip_is_stable() {
|
|
for c in [
|
|
PrivacyClass::Raw,
|
|
PrivacyClass::Derived,
|
|
PrivacyClass::Anonymous,
|
|
PrivacyClass::Restricted,
|
|
] {
|
|
assert_eq!(PrivacyClass::try_from(c.as_u8()).unwrap(), c);
|
|
}
|
|
}
|
|
|
|
// --- LocalSink accepts everything ---------------------------------------
|
|
|
|
#[test]
|
|
fn local_sink_accepts_all_classes() {
|
|
for c in [
|
|
PrivacyClass::Raw,
|
|
PrivacyClass::Derived,
|
|
PrivacyClass::Anonymous,
|
|
PrivacyClass::Restricted,
|
|
] {
|
|
check_class::<LocalKind>(c).expect("LocalSink must accept every class");
|
|
}
|
|
}
|
|
|
|
// --- NetworkSink rejects Raw, accepts the rest --------------------------
|
|
|
|
#[test]
|
|
fn network_sink_rejects_raw_frames() {
|
|
let err = check_class::<NetworkKind>(PrivacyClass::Raw).unwrap_err();
|
|
match err {
|
|
BfldError::PrivacyViolation { reason } => assert_eq!(reason, "NetworkKind"),
|
|
other => panic!("expected PrivacyViolation, got {other:?}"),
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn network_sink_accepts_derived_anonymous_restricted() {
|
|
for c in [
|
|
PrivacyClass::Derived,
|
|
PrivacyClass::Anonymous,
|
|
PrivacyClass::Restricted,
|
|
] {
|
|
check_class::<NetworkKind>(c)
|
|
.expect("NetworkSink must accept Derived/Anonymous/Restricted");
|
|
}
|
|
}
|
|
|
|
// --- MatterSink rejects Raw and Derived ---------------------------------
|
|
|
|
#[test]
|
|
fn matter_sink_rejects_raw_and_derived() {
|
|
for c in [PrivacyClass::Raw, PrivacyClass::Derived] {
|
|
let err = check_class::<MatterKind>(c).unwrap_err();
|
|
match err {
|
|
BfldError::PrivacyViolation { reason } => assert_eq!(reason, "MatterKind"),
|
|
other => panic!("expected PrivacyViolation for {c:?}, got {other:?}"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn matter_sink_accepts_anonymous_and_restricted() {
|
|
for c in [PrivacyClass::Anonymous, PrivacyClass::Restricted] {
|
|
check_class::<MatterKind>(c).expect("MatterSink must accept anonymous + restricted");
|
|
}
|
|
}
|