117 lines
3.6 KiB
Rust
117 lines
3.6 KiB
Rust
//! Acceptance tests for ADR-121 §2.1 / ADR-122 §2.1 — `BfldEvent` privacy gating.
|
|
|
|
#![cfg(feature = "std")]
|
|
|
|
use wifi_densepose_bfld::{BfldEvent, PrivacyClass};
|
|
|
|
fn sample_at(class: PrivacyClass) -> BfldEvent {
|
|
BfldEvent::with_privacy_gating(
|
|
"seed-01".to_string(),
|
|
1_700_000_000_000_000_000,
|
|
true,
|
|
0.72,
|
|
1,
|
|
0.91,
|
|
Some("living_room".to_string()),
|
|
class,
|
|
Some(0.84),
|
|
Some([0xAB; 32]),
|
|
)
|
|
}
|
|
|
|
#[test]
|
|
fn anonymous_event_retains_identity_risk_and_hash() {
|
|
let e = sample_at(PrivacyClass::Anonymous);
|
|
assert!(e.identity_risk_score.is_some());
|
|
assert!(e.rf_signature_hash.is_some());
|
|
}
|
|
|
|
#[test]
|
|
fn restricted_event_strips_identity_fields() {
|
|
let e = sample_at(PrivacyClass::Restricted);
|
|
assert!(e.identity_risk_score.is_none(), "risk score must be None at class 3");
|
|
assert!(e.rf_signature_hash.is_none(), "rf hash must be None at class 3");
|
|
// Sensing fields still present.
|
|
assert!(e.presence);
|
|
assert_eq!(e.person_count, 1);
|
|
assert_eq!(e.zone_id.as_deref(), Some("living_room"));
|
|
}
|
|
|
|
#[test]
|
|
fn apply_privacy_gating_is_idempotent() {
|
|
let mut e = sample_at(PrivacyClass::Restricted);
|
|
e.apply_privacy_gating();
|
|
e.apply_privacy_gating();
|
|
assert!(e.identity_risk_score.is_none());
|
|
}
|
|
|
|
#[test]
|
|
fn event_type_is_always_bfld_update() {
|
|
for c in [
|
|
PrivacyClass::Anonymous,
|
|
PrivacyClass::Restricted,
|
|
PrivacyClass::Derived,
|
|
] {
|
|
assert_eq!(sample_at(c).event_type, "bfld_update");
|
|
}
|
|
}
|
|
|
|
#[cfg(feature = "serde-json")]
|
|
mod json {
|
|
use super::sample_at;
|
|
use wifi_densepose_bfld::PrivacyClass;
|
|
|
|
#[test]
|
|
fn json_round_trip_emits_type_field_first_or_last_but_present() {
|
|
let json = sample_at(PrivacyClass::Anonymous).to_json().unwrap();
|
|
assert!(json.contains(r#""type":"bfld_update""#), "JSON: {json}");
|
|
assert!(json.contains(r#""node_id":"seed-01""#));
|
|
assert!(json.contains(r#""presence":true"#));
|
|
assert!(json.contains(r#""privacy_class":"anonymous""#));
|
|
}
|
|
|
|
#[test]
|
|
fn anonymous_json_includes_identity_fields() {
|
|
let json = sample_at(PrivacyClass::Anonymous).to_json().unwrap();
|
|
assert!(json.contains("identity_risk_score"));
|
|
assert!(json.contains("rf_signature_hash"));
|
|
}
|
|
|
|
#[test]
|
|
fn restricted_json_omits_identity_fields_entirely() {
|
|
let json = sample_at(PrivacyClass::Restricted).to_json().unwrap();
|
|
assert!(
|
|
!json.contains("identity_risk_score"),
|
|
"JSON must omit identity_risk_score at class 3, got: {json}",
|
|
);
|
|
assert!(
|
|
!json.contains("rf_signature_hash"),
|
|
"JSON must omit rf_signature_hash at class 3, got: {json}",
|
|
);
|
|
// Sensing fields still emitted.
|
|
assert!(json.contains("presence"));
|
|
assert!(json.contains("motion"));
|
|
assert!(json.contains(r#""privacy_class":"restricted""#));
|
|
}
|
|
|
|
#[test]
|
|
fn privacy_class_serializes_to_lowercase_name() {
|
|
for (class, name) in [
|
|
(PrivacyClass::Anonymous, "anonymous"),
|
|
(PrivacyClass::Restricted, "restricted"),
|
|
] {
|
|
let json = sample_at(class).to_json().unwrap();
|
|
let needle = format!(r#""privacy_class":"{name}""#);
|
|
assert!(json.contains(&needle), "missing {needle} in: {json}");
|
|
}
|
|
}
|
|
|
|
#[test]
|
|
fn zone_id_none_is_omitted_from_json() {
|
|
let mut e = sample_at(PrivacyClass::Anonymous);
|
|
e.zone_id = None;
|
|
let json = e.to_json().unwrap();
|
|
assert!(!json.contains("zone_id"), "None zone_id must be omitted: {json}");
|
|
}
|
|
}
|