use std::time::Duration;
use moloch_core::agent::{
Capability, CapabilityKind, CapabilityState, PrincipalId, ResourceScope, RuntimeAttestation,
Session,
};
use moloch_core::crypto::{hash, SecretKey};
use moloch_core::event::EventId;
fn test_principal() -> PrincipalId {
PrincipalId::user("test@example.com").unwrap()
}
fn test_key() -> SecretKey {
SecretKey::generate()
}
fn test_event_id() -> EventId {
EventId(hash(b"boundary-test"))
}
#[test]
fn attestation_valid_at_exact_expiry_boundary() {
use moloch_core::agent::AgentAttestationBuilder;
let authority = test_key();
let start = 1_700_000_000_000i64;
let attestation = AgentAttestationBuilder::new()
.agent_id(test_key().public_key())
.code_hash(hash(b"code"))
.config_hash(hash(b"config"))
.prompt_hash(hash(b"prompt"))
.runtime(RuntimeAttestation::new(
"test-runtime-v1.0.0",
hash(b"runtime-binary"),
))
.authority(authority.public_key())
.attested_at(start)
.validity_period(Duration::from_secs(60))
.sign(&authority)
.unwrap();
let expires_at = start + 60_000;
assert!(attestation.is_valid_at(expires_at - 1));
assert!(!attestation.is_valid_at(expires_at));
assert!(!attestation.is_valid_at(expires_at + 1));
}
#[test]
fn session_expired_at_exact_max_duration() {
let start = 1_700_000_000_000i64;
let max_dur = Duration::from_secs(60);
let session = Session::builder()
.principal(test_principal())
.started_at(start)
.max_duration(max_dur)
.build()
.unwrap();
let max_ms = max_dur.as_millis() as i64;
assert!(!session.is_expired(start + max_ms - 1));
assert!(!session.is_expired(start + max_ms));
assert!(session.is_expired(start + max_ms + 1));
}
#[test]
fn capability_valid_at_exact_expiry_boundary() {
let key = test_key();
let now = chrono::Utc::now().timestamp_millis();
let expires_at = now + 60_000;
let cap = Capability::builder()
.kind(CapabilityKind::Read)
.scope(ResourceScope::all())
.grantor(test_principal())
.expires_at(expires_at)
.sign(&key)
.unwrap();
assert!(cap.is_valid_at(expires_at - 1));
assert_eq!(cap.lifecycle_state(expires_at - 1), CapabilityState::Active);
assert!(!cap.is_valid_at(expires_at));
assert_eq!(cap.lifecycle_state(expires_at), CapabilityState::Expired);
assert!(!cap.is_valid_at(expires_at + 1));
assert_eq!(
cap.lifecycle_state(expires_at + 1),
CapabilityState::Expired
);
}
#[test]
fn idempotency_record_not_expired_when_just_created() {
use moloch_core::agent::{ActionOutcome, IdempotencyKey, IdempotencyRecord};
let key = IdempotencyKey::new(test_key().public_key(), "test-action", "client-1");
let record = IdempotencyRecord::new(
key,
test_event_id(),
ActionOutcome::success(serde_json::json!({})),
60_000, );
assert!(!record.is_expired());
assert!(record.is_valid());
}
#[test]
fn idempotency_record_zero_ttl_borderline() {
use moloch_core::agent::{ActionOutcome, IdempotencyKey, IdempotencyRecord};
let key = IdempotencyKey::new(test_key().public_key(), "test-action", "client-1");
let record = IdempotencyRecord::new(
key,
test_event_id(),
ActionOutcome::success(serde_json::json!({})),
0, );
let _ = record.is_expired();
}
#[test]
fn delegation_at_exact_max_depth_succeeds() {
let key = test_key();
let cap = Capability::builder()
.kind(CapabilityKind::Read)
.scope(ResourceScope::all())
.grantor(test_principal())
.delegatable(2)
.sign(&key)
.unwrap();
let child1 = cap.delegate(&key, None, None).unwrap();
assert_eq!(child1.delegation_depth(), 1);
let child2 = child1.delegate(&key, None, None).unwrap();
assert_eq!(child2.delegation_depth(), 2);
assert!(child2.delegate(&key, None, None).is_err());
}
#[test]
fn revocation_timestamp_precision() {
let key = test_key();
let mut cap = Capability::builder()
.kind(CapabilityKind::Read)
.scope(ResourceScope::all())
.grantor(test_principal())
.sign(&key)
.unwrap();
let before = chrono::Utc::now().timestamp_millis();
cap.revoke("test");
let after = chrono::Utc::now().timestamp_millis();
let ts = cap.revoked_at().unwrap();
assert!(ts >= before, "revoked_at {} < before {}", ts, before);
assert!(ts <= after, "revoked_at {} > after {}", ts, after);
}
#[test]
fn scope_subset_edge_cases() {
let key = test_key();
let cap = Capability::builder()
.kind(CapabilityKind::Read)
.scope(ResourceScope::pattern("repo:org/*"))
.grantor(test_principal())
.delegatable(3)
.sign(&key)
.unwrap();
let child = cap.delegate(&key, Some(ResourceScope::pattern("repo:org/*")), None);
assert!(child.is_ok(), "equal patterns should be valid subsets");
let cap2 = Capability::builder()
.kind(CapabilityKind::Write)
.scope(ResourceScope::kind(
moloch_core::event::ResourceKind::Repository,
))
.grantor(test_principal())
.delegatable(3)
.sign(&key)
.unwrap();
let child2 = cap2.delegate(
&key,
Some(ResourceScope::kind(
moloch_core::event::ResourceKind::Repository,
)),
None,
);
assert!(child2.is_ok(), "same kind should be valid subset");
let child3 = cap2.delegate(
&key,
Some(ResourceScope::kind(moloch_core::event::ResourceKind::File)),
None,
);
assert!(child3.is_err(), "different kind should not be subset");
}