tidaldb/tidal/tests/m10p1_governance.rs
jx12n b55ad70141 fix: M0-M10 third-pass remediation — durability, replication, and CLI hardening
Resolves the 142 findings from tidal/docs/reviews/CODE_REVIEW_m0-m10.md across
the engine, server, net, and CLI surfaces:

- WAL/session-journal durability, checkpoint format, and crash-recovery hardening
- Replication shipper/receiver, tenant isolation, and migration paths
- Cluster scatter-gather, router, standalone server + health/offload endpoints
- tidalctl refactored into command modules with JSON output and WAL-state tooling
- Cohort, governance, signal-ledger, and vector-registry correctness fixes
- Expanded UAT/integration/durability test coverage across all milestones
2026-06-08 10:28:34 -06:00

206 lines
6.2 KiB
Rust

//! M10p1 integration test — Community Governance Policy Engine.
//!
//! Validates versioned community policies governing signal eligibility and
//! per-contribution weighting: excluded intents are rejected, admitted weights
//! are clamped, versions are monotonic with effective timestamps, the governing
//! version is queryable, and policies survive close/reopen.
//!
//! ROADMAP §Milestone 10 Phase 1.
#![allow(clippy::too_many_lines, clippy::unwrap_used)]
use std::{collections::BTreeSet, time::Duration};
use tidaldb::{
CommunityId, GovernancePolicy, ShareIntent, SharePolicy, TidalDb, UserId, WeightingBounds,
schema::{DecaySpec, EntityId, EntityKind, SchemaBuilder, Timestamp, Window},
};
fn schema() -> tidaldb::schema::Schema {
let mut b = SchemaBuilder::new();
for name in ["like", "low_quality", "skip_for_now"] {
let _ = b
.signal(
name,
EntityKind::Item,
DecaySpec::Exponential {
half_life: Duration::from_secs(7 * 24 * 3600),
},
)
.windows(&[Window::AllTime])
.velocity(false)
.add();
}
b.build().unwrap()
}
fn full_share() -> SharePolicy {
SharePolicy::community_share(
1,
[
ShareIntent::Like,
ShareIntent::LowQuality,
ShareIntent::SkipForNow,
],
)
}
fn policy(community: CommunityId, version: u32, effective_at_ns: u64) -> GovernancePolicy {
GovernancePolicy {
community_id: community,
version,
effective_at_ns,
allowed_intents: BTreeSet::new(),
excluded_intents: BTreeSet::from(["skip_for_now".to_string()]),
weighting: WeightingBounds { min: 0.0, max: 2.0 },
trust_threshold: 0.0,
}
}
#[test]
fn excluded_intent_is_rejected_by_policy() {
let db = TidalDb::builder()
.ephemeral()
.with_schema(schema())
.open()
.unwrap();
let (c, item, user) = (CommunityId(1), EntityId::new(5), UserId(1));
db.join_community(user, c, full_share()).unwrap();
db.define_community_policy(policy(c, 1, 0)).unwrap();
let now = Timestamp::now();
// `skip_for_now` is excluded by governance -> rejected (not recorded).
assert!(
!db.signal_for_community("skip_for_now", item, 1.0, now, user, c)
.unwrap()
);
// `like` is admitted.
assert!(
db.signal_for_community("like", item, 1.0, now, user, c)
.unwrap()
);
assert_eq!(
db.read_community_windowed_count(c, item, "like", Window::AllTime)
.unwrap(),
1
);
assert_eq!(
db.read_community_windowed_count(c, item, "skip_for_now", Window::AllTime)
.unwrap(),
0
);
}
#[test]
fn weight_is_clamped_by_policy() {
let db = TidalDb::builder()
.ephemeral()
.with_schema(schema())
.open()
.unwrap();
let item = EntityId::new(5);
let now = Timestamp::now();
// Governed community: max weight 2.0.
let governed = CommunityId(1);
db.join_community(UserId(1), governed, full_share())
.unwrap();
db.define_community_policy(policy(governed, 1, 0)).unwrap();
assert!(
db.signal_for_community("like", item, 100.0, now, UserId(1), governed)
.unwrap()
);
// Ungoverned community: weight passes through.
let free = CommunityId(2);
db.join_community(UserId(1), free, full_share()).unwrap();
assert!(
db.signal_for_community("like", item, 100.0, now, UserId(1), free)
.unwrap()
);
let governed_score = db
.read_community_decay_score(governed, item, "like", 0)
.unwrap()
.unwrap();
let free_score = db
.read_community_decay_score(free, item, "like", 0)
.unwrap()
.unwrap();
assert!(
governed_score <= 2.0 + 1e-6,
"governed weight must be clamped to 2.0, got {governed_score}"
);
assert!(
free_score > 2.0,
"ungoverned weight must pass through, got {free_score}"
);
}
#[test]
fn policy_versions_are_monotonic_and_queryable() {
let db = TidalDb::builder()
.ephemeral()
.with_schema(schema())
.open()
.unwrap();
let c = CommunityId(1);
db.define_community_policy(policy(c, 1, 0)).unwrap();
assert_eq!(db.governing_policy_version(c), Some(1));
// v2 supersedes once registered (effective at 0 here).
db.define_community_policy(policy(c, 2, 0)).unwrap();
assert_eq!(db.governing_policy_version(c), Some(2));
// Re-registering a non-increasing version fails.
assert!(db.define_community_policy(policy(c, 2, 0)).is_err());
assert!(db.define_community_policy(policy(c, 1, 0)).is_err());
}
#[test]
fn no_policy_means_no_governance() {
let db = TidalDb::builder()
.ephemeral()
.with_schema(schema())
.open()
.unwrap();
let (c, item, user) = (CommunityId(9), EntityId::new(1), UserId(1));
db.join_community(user, c, full_share()).unwrap();
let now = Timestamp::now();
// Without a governance policy, even `skip_for_now` flows (share policy allows it).
assert!(
db.signal_for_community("skip_for_now", item, 1.0, now, user, c)
.unwrap()
);
assert_eq!(db.governing_policy_version(c), None);
}
#[test]
fn policy_survives_reopen() {
let home = tidaldb::TempTidalHome::new().unwrap();
let c = CommunityId(1);
{
let db = TidalDb::builder()
.with_data_dir(home.path())
.with_schema(schema())
.open()
.unwrap();
db.define_community_policy(policy(c, 1, 0)).unwrap();
db.define_community_policy(policy(c, 2, 0)).unwrap();
db.shutdown().unwrap();
}
let db = TidalDb::builder()
.with_data_dir(home.path())
.with_schema(schema())
.open()
.unwrap();
assert_eq!(
db.governing_policy_version(c),
Some(2),
"governance policy must survive reopen"
);
// Monotonic check still holds against restored history.
assert!(db.define_community_policy(policy(c, 2, 0)).is_err());
assert!(db.define_community_policy(policy(c, 3, 0)).is_ok());
}