Add addresses chapter
This commit is contained in:
@@ -0,0 +1,206 @@
|
||||
use coracle_lib::addresses::{Address, AddressError};
|
||||
use coracle_lib::events::EventContent;
|
||||
use coracle_lib::keys::SecretKey;
|
||||
use coracle_lib::tags::Tag;
|
||||
|
||||
|
||||
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 make_signed_event(kind: u16, tags: Vec<Tag>) -> coracle_lib::events::Event {
|
||||
let sk = fixed_secret();
|
||||
let pk = sk.public_key();
|
||||
let hashed = EventContent::new("test", tags)
|
||||
.kind(kind)
|
||||
.stamp(1_700_000_000)
|
||||
.own(pk)
|
||||
.hash();
|
||||
let sig = sk.sign(&hashed.id);
|
||||
hashed.sign(sig)
|
||||
}
|
||||
|
||||
// --- Construction ---
|
||||
|
||||
#[test]
|
||||
fn new_basic() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(30023, pk, "my-article");
|
||||
assert_eq!(addr.kind, 30023);
|
||||
assert_eq!(addr.pubkey, pk);
|
||||
assert_eq!(addr.identifier, "my-article");
|
||||
assert!(addr.hints.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn hints_builder() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(30023, pk, "my-article")
|
||||
.hints(["wss://relay.example.com", "wss://other.relay"]);
|
||||
assert_eq!(addr.hints.len(), 2);
|
||||
assert_eq!(addr.hints[0], "wss://relay.example.com");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn equality_ignores_hints() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let a = Address::new(30023, pk, "slug");
|
||||
let b = Address::new(30023, pk, "slug").hints(["wss://relay.example.com"]);
|
||||
assert_eq!(a, b);
|
||||
}
|
||||
|
||||
// --- from_event ---
|
||||
|
||||
#[test]
|
||||
fn from_event_addressable() {
|
||||
let event = make_signed_event(30023, vec![Tag::new("d", ["my-article"])]);
|
||||
let addr = Address::from_event(&event).unwrap();
|
||||
assert_eq!(addr.kind, 30023);
|
||||
assert_eq!(addr.identifier, "my-article");
|
||||
assert_eq!(addr.pubkey, event.pubkey);
|
||||
assert!(addr.hints.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_event_replaceable_kind_0() {
|
||||
let event = make_signed_event(0, vec![]);
|
||||
let addr = Address::from_event(&event).unwrap();
|
||||
assert_eq!(addr.kind, 0);
|
||||
assert_eq!(addr.identifier, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_event_replaceable_kind_3() {
|
||||
let event = make_signed_event(3, vec![]);
|
||||
let addr = Address::from_event(&event).unwrap();
|
||||
assert_eq!(addr.kind, 3);
|
||||
assert_eq!(addr.identifier, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_event_replaceable_range() {
|
||||
let event = make_signed_event(10002, vec![]);
|
||||
let addr = Address::from_event(&event).unwrap();
|
||||
assert_eq!(addr.kind, 10002);
|
||||
assert_eq!(addr.identifier, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_event_replaceable_ignores_d_tag() {
|
||||
let event = make_signed_event(0, vec![Tag::new("d", ["should-be-ignored"])]);
|
||||
let addr = Address::from_event(&event).unwrap();
|
||||
assert_eq!(addr.kind, 0);
|
||||
assert_eq!(addr.identifier, "");
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_event_regular_returns_none() {
|
||||
let event = make_signed_event(1, vec![]);
|
||||
assert!(Address::from_event(&event).is_none());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_event_ephemeral_returns_none() {
|
||||
let event = make_signed_event(20000, vec![]);
|
||||
assert!(Address::from_event(&event).is_none());
|
||||
}
|
||||
|
||||
// --- Display / FromStr ---
|
||||
|
||||
#[test]
|
||||
fn display_roundtrip() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(30023, pk, "my-article");
|
||||
let s = addr.to_string();
|
||||
assert!(s.starts_with("30023:"));
|
||||
assert!(s.ends_with(":my-article"));
|
||||
let parsed: Address = s.parse().unwrap();
|
||||
assert_eq!(parsed, addr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fromstr_empty_identifier() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(0, pk, "");
|
||||
let s = addr.to_string();
|
||||
let parsed: Address = s.parse().unwrap();
|
||||
assert_eq!(parsed.identifier, "");
|
||||
assert_eq!(parsed, addr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fromstr_identifier_with_colons() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(30023, pk, "has:colons:inside");
|
||||
let s = addr.to_string();
|
||||
let parsed: Address = s.parse().unwrap();
|
||||
assert_eq!(parsed.identifier, "has:colons:inside");
|
||||
assert_eq!(parsed, addr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fromstr_invalid() {
|
||||
assert!("not-an-address".parse::<Address>().is_err());
|
||||
assert!("123".parse::<Address>().is_err());
|
||||
assert!("abc:def:ghi".parse::<Address>().is_err());
|
||||
}
|
||||
|
||||
// --- naddr encoding ---
|
||||
|
||||
#[test]
|
||||
fn naddr_roundtrip_no_relays() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(30023, pk, "my-article");
|
||||
let naddr = addr.to_naddr();
|
||||
assert!(naddr.starts_with("naddr1"));
|
||||
let decoded = Address::from_naddr(&naddr).unwrap();
|
||||
assert_eq!(decoded, addr);
|
||||
assert!(decoded.hints.is_empty());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn naddr_roundtrip_with_relays() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(30023, pk, "my-article")
|
||||
.hints(["wss://relay.example.com", "wss://other.relay"]);
|
||||
let naddr = addr.to_naddr();
|
||||
let decoded = Address::from_naddr(&naddr).unwrap();
|
||||
assert_eq!(decoded, addr);
|
||||
assert_eq!(
|
||||
decoded.hints,
|
||||
vec!["wss://relay.example.com", "wss://other.relay"]
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn naddr_roundtrip_empty_identifier() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(10002, pk, "");
|
||||
let naddr = addr.to_naddr();
|
||||
let decoded = Address::from_naddr(&naddr).unwrap();
|
||||
assert_eq!(decoded, addr);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn fromstr_accepts_naddr() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let addr = Address::new(30023, pk, "my-article")
|
||||
.hints(["wss://relay.example.com"]);
|
||||
let naddr = addr.to_naddr();
|
||||
let parsed: Address = naddr.parse().unwrap();
|
||||
assert_eq!(parsed, addr);
|
||||
assert_eq!(parsed.hints, vec!["wss://relay.example.com"]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn from_naddr_wrong_prefix() {
|
||||
let pk = fixed_secret().public_key();
|
||||
let npub = pk.to_npub();
|
||||
let err = Address::from_naddr(&npub).unwrap_err();
|
||||
assert!(matches!(err, AddressError::WrongPrefix { .. }));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user