dig_events_protocol/
lib.rs1#![forbid(unsafe_code)]
30#![warn(missing_docs)]
31
32mod cursor;
33mod event;
34mod kind;
35mod sync;
36mod traits;
37mod value;
38
39pub use cursor::{Cursor, EmittedEvent};
40pub use event::WalletEvent;
41pub use kind::EventKind;
42pub use sync::{SyncLifecycle, SyncStatus};
43pub use traits::{filter_events, CatchUp, EventEmitter};
44pub use value::{Amount, AssetId, WalletId};
45
46pub use enumset::{enum_set, EnumSet};
49
50#[cfg(test)]
51mod tests {
52 use super::*;
53
54 #[test]
55 fn cursor_advances_monotonically() {
56 assert_eq!(Cursor(0).next(), Cursor(1));
57 assert!(Cursor(1) > Cursor(0));
58 }
59
60 #[test]
61 fn event_is_tagged_snake_case() {
62 let e = WalletEvent::Confirmation {
63 tx_id: "ab".into(),
64 height: 100,
65 };
66 let json = serde_json::to_string(&e).unwrap();
67 assert!(json.contains("\"type\":\"confirmation\""), "{json}");
68 let back: WalletEvent = serde_json::from_str(&json).unwrap();
69 assert_eq!(back, e);
70 }
71
72 #[test]
73 fn kind_maps_each_variant() {
74 let e = WalletEvent::FundsReceived {
75 wallet_id: WalletId(1),
76 asset: None,
77 amount: Amount(5),
78 coin_id: "c".into(),
79 confirmed_height: 10,
80 };
81 assert_eq!(e.kind(), EventKind::FundsReceived);
82 }
83
84 #[test]
85 fn filter_admits_only_matching_kinds() {
86 let received = WalletEvent::FundsReceived {
87 wallet_id: WalletId(1),
88 asset: None,
89 amount: Amount(5),
90 coin_id: "c".into(),
91 confirmed_height: 10,
92 };
93 let tip = WalletEvent::NewTip {
94 height: 9,
95 header_hash: "hh".into(),
96 };
97
98 let funds_only = EventKind::FundsReceived | EventKind::FundsSent;
99 assert!(received.matches(funds_only));
100 assert!(!tip.matches(funds_only));
101 }
102
103 #[test]
104 fn filter_events_retains_only_matching_in_order() {
105 let events = vec![
106 EmittedEvent {
107 cursor: Cursor(1),
108 event: WalletEvent::NewTip {
109 height: 1,
110 header_hash: "a".into(),
111 },
112 },
113 EmittedEvent {
114 cursor: Cursor(2),
115 event: WalletEvent::FundsReceived {
116 wallet_id: WalletId(1),
117 asset: None,
118 amount: Amount(5),
119 coin_id: "c".into(),
120 confirmed_height: 10,
121 },
122 },
123 ];
124 let kept = filter_events(events, EventKind::FundsReceived.into());
125 assert_eq!(kept.len(), 1);
126 assert_eq!(kept[0].cursor, Cursor(2));
127 }
128
129 #[test]
130 fn amount_exposes_mojos() {
131 assert_eq!(Amount(42).mojos(), 42);
132 }
133
134 #[test]
135 fn newtypes_display() {
136 assert_eq!(WalletId(3).to_string(), "3");
137 assert_eq!(Amount(9).to_string(), "9");
138 assert_eq!(AssetId("tail".into()).to_string(), "tail");
139 }
140
141 #[test]
142 fn sync_status_round_trips() {
143 let s = SyncStatus {
144 state: SyncLifecycle::Synced,
145 peak_height: 100,
146 target_height: 100,
147 };
148 let json = serde_json::to_string(&s).unwrap();
149 assert!(json.contains("\"state\":\"synced\""), "{json}");
150 let back: SyncStatus = serde_json::from_str(&json).unwrap();
151 assert_eq!(back, s);
152 }
153}