165 lines
4.4 KiB
Rust
165 lines
4.4 KiB
Rust
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));
|
|
}
|