119 lines
3.5 KiB
Rust
119 lines
3.5 KiB
Rust
use coracle_lib::events::{
|
|
Event, EventContent, EventError, EventTemplate, HashedEvent, OwnedEvent, StampedEvent,
|
|
};
|
|
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(content: &str, tags: Vec<Tag>) -> HashedEvent {
|
|
EventContent::new(content, tags)
|
|
.kind(1)
|
|
.stamp(1_700_000_000)
|
|
.own(fixed_secret().public_key())
|
|
.hash()
|
|
}
|
|
|
|
fn sign_fixture() -> Event {
|
|
let sk = fixed_secret();
|
|
let hashed = build_hashed("hello nostr", vec![Tag::new("t", ["nostr"])]);
|
|
let sig = sk.sign(&hashed.id);
|
|
hashed.sign(sig)
|
|
}
|
|
|
|
#[test]
|
|
fn pipeline_progresses_through_types() {
|
|
let content = EventContent::new("hi", Tags::new());
|
|
let template: EventTemplate = content.kind(1);
|
|
let stamped: StampedEvent = template.stamp(1_700_000_000);
|
|
let owned: OwnedEvent = stamped.own(fixed_secret().public_key());
|
|
let hashed: HashedEvent = owned.hash();
|
|
let sig = fixed_secret().sign(&hashed.id);
|
|
let event: Event = hashed.sign(sig);
|
|
assert_eq!(event.content, "hi");
|
|
assert_eq!(event.kind, 1);
|
|
}
|
|
|
|
#[test]
|
|
fn id_is_deterministic() {
|
|
let a = build_hashed("hello", vec![]);
|
|
let b = build_hashed("hello", vec![]);
|
|
assert_eq!(a.id, b.id);
|
|
}
|
|
|
|
#[test]
|
|
fn id_changes_with_content() {
|
|
let a = build_hashed("hello", vec![]);
|
|
let b = build_hashed("goodbye", vec![]);
|
|
assert_ne!(a.id, b.id);
|
|
}
|
|
|
|
#[test]
|
|
fn sign_and_verify_roundtrip() {
|
|
let event = sign_fixture();
|
|
assert!(event.verify_id());
|
|
event.verify().expect("signature should verify");
|
|
}
|
|
|
|
#[test]
|
|
fn tampered_content_fails_id_check() {
|
|
let mut event = sign_fixture();
|
|
event.content = "something else".to_string();
|
|
assert!(!event.verify_id());
|
|
assert_eq!(event.verify(), Err(EventError::InvalidId));
|
|
}
|
|
|
|
#[test]
|
|
fn tampered_sig_fails_verify() {
|
|
let mut event = sign_fixture();
|
|
event.sig[0] ^= 0x01;
|
|
assert_eq!(event.verify(), Err(EventError::InvalidSignature));
|
|
}
|
|
|
|
#[test]
|
|
fn json_roundtrip() {
|
|
let event = sign_fixture();
|
|
let json = serde_json::to_string(&event).unwrap();
|
|
let parsed: Event = serde_json::from_str(&json).unwrap();
|
|
assert_eq!(parsed, event);
|
|
parsed.verify().expect("roundtripped event still verifies");
|
|
}
|
|
|
|
#[test]
|
|
fn json_contains_hex_fields() {
|
|
let event = sign_fixture();
|
|
let value: serde_json::Value = serde_json::to_value(&event).unwrap();
|
|
let obj = value.as_object().unwrap();
|
|
assert_eq!(obj["id"].as_str().unwrap().len(), 64);
|
|
assert_eq!(obj["pubkey"].as_str().unwrap().len(), 64);
|
|
assert_eq!(obj["sig"].as_str().unwrap().len(), 128);
|
|
assert_eq!(obj["kind"].as_u64().unwrap(), 1);
|
|
assert_eq!(obj["content"].as_str().unwrap(), "hello nostr");
|
|
assert!(obj["tags"].is_array());
|
|
}
|
|
|
|
#[test]
|
|
fn json_ignores_unknown_fields() {
|
|
let event = sign_fixture();
|
|
let mut value: serde_json::Value = serde_json::to_value(&event).unwrap();
|
|
value
|
|
.as_object_mut()
|
|
.unwrap()
|
|
.insert("custom_field".to_string(), serde_json::json!("ignored"));
|
|
let reparsed: Event = serde_json::from_value(value).unwrap();
|
|
assert_eq!(reparsed, event);
|
|
}
|
|
|
|
#[test]
|
|
fn secret_key_sign_is_deterministic() {
|
|
let sk = fixed_secret();
|
|
let msg = [42u8; 32];
|
|
assert_eq!(sk.sign(&msg), sk.sign(&msg));
|
|
}
|