use coracle_lib::events::{EventContent, HashedEvent}; use coracle_lib::expiration::{get_expiration, is_expired, is_expired_at}; use coracle_lib::keys::SecretKey; use coracle_lib::tags::{Tag, Tags}; fn fixed_secret() -> SecretKey { let bytes: [u8; 32] = [ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, ]; SecretKey::from_hex(&hex::encode(bytes)).unwrap() } fn build_hashed(tags: Tags) -> HashedEvent { EventContent::new() .content("hi") .tags(tags) .kind(1) .stamp(1_700_000_000) .own(fixed_secret().public_key()) .hash() } // --- Tag::expiration constructor --- #[test] fn tag_constructor_shape() { let t = Tag::expiration(1_700_000_000); assert_eq!(t.name(), "expiration"); assert_eq!(t.value(), "1700000000"); assert_eq!(t.len(), 2); } // --- get_expiration --- #[test] fn get_expiration_returns_none_when_tag_absent() { let tags = Tags::new(); assert_eq!(get_expiration(&tags), None); } #[test] fn get_expiration_parses_numeric_value() { let tags = Tags::from(vec![Tag::expiration(1_700_000_000)]); assert_eq!(get_expiration(&tags), Some(1_700_000_000)); } #[test] fn get_expiration_returns_none_when_malformed() { let tags = Tags::from(vec![Tag::new("expiration", ["not-a-number"])]); assert_eq!(get_expiration(&tags), None); } #[test] fn get_expiration_reads_first_matching_tag() { let tags = Tags::from(vec![ Tag::new("t", ["nostr"]), Tag::expiration(100), Tag::expiration(200), ]); assert_eq!(get_expiration(&tags), Some(100)); } // --- is_expired_at --- #[test] fn is_expired_at_false_when_tag_absent() { let tags = Tags::new(); assert!(!is_expired_at(&tags, u64::MAX)); } #[test] fn is_expired_at_false_when_now_before_expiration() { let tags = Tags::from(vec![Tag::expiration(100)]); assert!(!is_expired_at(&tags, 99)); } #[test] fn is_expired_at_false_when_now_equals_expiration() { // Strict `<`: equal is not yet expired. let tags = Tags::from(vec![Tag::expiration(100)]); assert!(!is_expired_at(&tags, 100)); } #[test] fn is_expired_at_true_when_now_past_expiration() { let tags = Tags::from(vec![Tag::expiration(100)]); assert!(is_expired_at(&tags, 101)); } #[test] fn is_expired_at_false_when_malformed() { let tags = Tags::from(vec![Tag::new("expiration", ["nope"])]); assert!(!is_expired_at(&tags, u64::MAX)); } // --- is_expired (system clock) --- #[test] fn is_expired_true_for_unix_epoch_tag() { // Unless the system clock is wildly wrong, `now` is far past 1. let tags = Tags::from(vec![Tag::expiration(1)]); assert!(is_expired(&tags)); } #[test] fn is_expired_false_for_far_future_tag() { let tags = Tags::from(vec![Tag::expiration(u64::MAX)]); assert!(!is_expired(&tags)); } #[test] fn is_expired_false_when_tag_absent() { let tags = Tags::new(); assert!(!is_expired(&tags)); } // --- HashedEvent method facades --- #[test] fn hashed_event_reads_expiration() { let tags = Tags::from(vec![Tag::expiration(1_700_000_500)]); let event = build_hashed(tags); assert_eq!(event.get_expiration(), Some(1_700_000_500)); assert!(!event.is_expired_at(1_700_000_500)); assert!(event.is_expired_at(1_700_000_501)); } #[test] fn hashed_event_no_expiration_without_tag() { let event = build_hashed(Tags::new()); assert_eq!(event.get_expiration(), None); assert!(!event.is_expired_at(u64::MAX)); assert!(!event.is_expired()); } // --- Event method facades --- #[test] fn signed_event_reads_expiration() { let sk = fixed_secret(); let tags = Tags::from(vec![Tag::expiration(1_700_000_500)]); let hashed = build_hashed(tags); let sig = sk.sign(&hashed.id); let signed = hashed.sign(sig); assert_eq!(signed.get_expiration(), Some(1_700_000_500)); assert!(signed.is_expired_at(1_700_000_501)); assert!(!signed.is_expired_at(1_700_000_499)); } #[test] fn expiration_tag_survives_hashing_and_signing() { // The tag is part of the canonical hash input, so it has to // round-trip through `hash()` and `sign()` intact. let sk = fixed_secret(); let ts = 1_700_000_500; let hashed = build_hashed(Tags::from(vec![Tag::expiration(ts)])); let sig = sk.sign(&hashed.id); let signed = hashed.sign(sig); assert!(signed.verify_id()); assert_eq!(signed.get_expiration(), Some(ts)); }