tidaldb/tidal/tests/m10p2_capabilities.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

185 lines
5.5 KiB
Rust

//! M10p2 integration test — Agent Capability & Scope Controls.
//!
//! Validates the ROADMAP M10 UAT agent-rights flow: a trusted agent with a
//! community-write capability can contribute, an experimental agent without it
//! is rejected, revocation takes effect immediately, and capabilities +
//! revocation history survive close/reopen.
//!
//! ROADMAP §Milestone 10 Phase 2.
#![allow(clippy::too_many_lines, clippy::unwrap_used)]
use std::time::Duration;
use tidaldb::{
CommunityId, ScopeClass, ScopePermission, ShareIntent, SharePolicy, TidalDb, UserId,
schema::{DecaySpec, EntityId, EntityKind, SchemaBuilder, Timestamp, Window},
};
fn schema() -> tidaldb::schema::Schema {
let mut b = SchemaBuilder::new();
let _ = b
.signal(
"like",
EntityKind::Item,
DecaySpec::Exponential {
half_life: Duration::from_secs(7 * 24 * 3600),
},
)
.windows(&[Window::AllTime])
.velocity(false)
.add();
b.build().unwrap()
}
fn share() -> SharePolicy {
SharePolicy::community_share(1, [ShareIntent::Like])
}
#[test]
fn trusted_agent_writes_experimental_is_rejected() {
let db = TidalDb::builder()
.ephemeral()
.with_schema(schema())
.open()
.unwrap();
let (community, item, user) = (CommunityId(1), EntityId::new(5), UserId(1));
db.join_community(user, community, share()).unwrap();
// A_trusted gets community write; A_experimental gets only community read.
db.grant_capability(
"A_trusted",
vec![ScopePermission::read_write(ScopeClass::Community)],
None,
)
.unwrap();
db.grant_capability(
"A_experimental",
vec![ScopePermission::read_only(ScopeClass::Community)],
None,
)
.unwrap();
let now = Timestamp::now();
// Trusted agent: allowed -> contribution recorded.
assert!(
db.agent_signal_for_community("A_trusted", "like", item, 1.0, now, user, community)
.unwrap()
);
// Experimental agent: denied by capability policy -> error, nothing written.
assert!(
db.agent_signal_for_community("A_experimental", "like", item, 1.0, now, user, community)
.is_err()
);
// Unknown agent: also denied.
assert!(
db.agent_signal_for_community("A_unknown", "like", item, 1.0, now, user, community)
.is_err()
);
// Exactly one contribution (from A_trusted) landed.
assert_eq!(
db.read_community_windowed_count(community, item, "like", Window::AllTime)
.unwrap(),
1
);
}
#[test]
fn revocation_takes_effect_immediately() {
let db = TidalDb::builder()
.ephemeral()
.with_schema(schema())
.open()
.unwrap();
let (community, item, user) = (CommunityId(1), EntityId::new(5), UserId(1));
db.join_community(user, community, share()).unwrap();
let token = db
.grant_capability(
"A",
vec![ScopePermission::read_write(ScopeClass::Community)],
None,
)
.unwrap();
let now = Timestamp::now();
assert!(db.check_capability("A", ScopeClass::Community, true));
assert!(
db.agent_signal_for_community("A", "like", item, 1.0, now, user, community)
.unwrap()
);
// Revoke -> the very next check/write is denied (atomic, no sweeper).
assert!(db.revoke_capability(&token).unwrap());
assert!(!db.check_capability("A", ScopeClass::Community, true));
assert!(
db.agent_signal_for_community("A", "like", item, 1.0, now, user, community)
.is_err()
);
// Revoking an unknown token returns false.
assert!(!db.revoke_capability("does-not-exist").unwrap());
}
#[test]
fn ttl_expiry_denies() {
let db = TidalDb::builder()
.ephemeral()
.with_schema(schema())
.open()
.unwrap();
// Grant with a zero TTL -> already expired.
db.grant_capability(
"A",
vec![ScopePermission::read_write(ScopeClass::Community)],
Some(Duration::from_nanos(0)),
)
.unwrap();
assert!(!db.check_capability("A", ScopeClass::Community, true));
}
#[test]
fn capabilities_and_revocations_survive_reopen() {
let home = tidaldb::TempTidalHome::new().unwrap();
let token_id;
{
let db = TidalDb::builder()
.with_data_dir(home.path())
.with_schema(schema())
.open()
.unwrap();
db.grant_capability(
"keep",
vec![ScopePermission::read_write(ScopeClass::Community)],
None,
)
.unwrap();
token_id = db
.grant_capability(
"revoked_agent",
vec![ScopePermission::read_write(ScopeClass::Community)],
None,
)
.unwrap();
db.revoke_capability(&token_id).unwrap();
db.shutdown().unwrap();
}
let db = TidalDb::builder()
.with_data_dir(home.path())
.with_schema(schema())
.open()
.unwrap();
// The live capability is restored and usable.
assert!(db.check_capability("keep", ScopeClass::Community, true));
// The revoked one stays revoked across restart.
assert!(!db.check_capability("revoked_agent", ScopeClass::Community, true));
// Revocation history is inspectable.
let history = db.revocation_history();
assert!(
history
.iter()
.any(|(tid, agent, _)| tid == &token_id && agent == "revoked_agent")
);
}