1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3use std::collections::HashMap;
4use std::fmt;
5
6#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
7#[serde(tag = "type", content = "value")]
8pub enum Key {
9 Variant(u8, u8),
10 Custom(CustomKey),
11}
12
13#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
14pub struct CustomKey {
15 pub name: String,
16 #[serde(flatten)]
17 pub value: CustomValue,
18}
19
20impl CustomKey {
21 pub fn scalar(name: impl Into<String>, value: CustomValue) -> Self {
22 Self {
23 name: name.into(),
24 value,
25 }
26 }
27
28 pub fn composite(
29 name: impl Into<String>,
30 values: impl IntoIterator<Item = CustomScalarValue>,
31 ) -> Self {
32 Self {
33 name: name.into(),
34 value: CustomValue::Composite(values.into_iter().collect()),
35 }
36 }
37}
38
39#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
40pub struct Bytes32(pub [u8; 32]);
41
42impl Serialize for Bytes32 {
43 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
44 where
45 S: serde::Serializer,
46 {
47 serializer.serialize_str(&format!("0x{}", hex::encode(self.0)))
48 }
49}
50
51impl<'de> Deserialize<'de> for Bytes32 {
52 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
53 where
54 D: serde::Deserializer<'de>,
55 {
56 let s = String::deserialize(deserializer)?;
57 let hex_part = s.strip_prefix("0x").unwrap_or(&s);
58 let bytes = hex::decode(hex_part).map_err(serde::de::Error::custom)?;
59 let arr: [u8; 32] = bytes
60 .try_into()
61 .map_err(|_| serde::de::Error::custom("expected 32 bytes"))?;
62 Ok(Self(arr))
63 }
64}
65
66impl From<[u8; 32]> for Bytes32 {
67 fn from(value: [u8; 32]) -> Self {
68 Self(value)
69 }
70}
71
72#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
73pub struct U64Text(pub u64);
74
75impl Serialize for U64Text {
76 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
77 where
78 S: serde::Serializer,
79 {
80 serializer.serialize_str(&self.0.to_string())
81 }
82}
83
84impl<'de> Deserialize<'de> for U64Text {
85 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
86 where
87 D: serde::Deserializer<'de>,
88 {
89 struct Visitor;
90
91 impl serde::de::Visitor<'_> for Visitor {
92 type Value = U64Text;
93
94 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
95 f.write_str("a u64 string or integer")
96 }
97
98 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
99 where
100 E: serde::de::Error,
101 {
102 Ok(U64Text(value))
103 }
104
105 fn visit_u128<E>(self, value: u128) -> Result<Self::Value, E>
106 where
107 E: serde::de::Error,
108 {
109 Ok(U64Text(u64::try_from(value).map_err(E::custom)?))
110 }
111
112 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
113 where
114 E: serde::de::Error,
115 {
116 Ok(U64Text(value.parse::<u64>().map_err(E::custom)?))
117 }
118 }
119
120 deserializer.deserialize_any(Visitor)
121 }
122}
123
124#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
125pub struct U128Text(pub u128);
126
127impl Serialize for U128Text {
128 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
129 where
130 S: serde::Serializer,
131 {
132 serializer.serialize_str(&self.0.to_string())
133 }
134}
135
136impl<'de> Deserialize<'de> for U128Text {
137 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
138 where
139 D: serde::Deserializer<'de>,
140 {
141 struct Visitor;
142
143 impl serde::de::Visitor<'_> for Visitor {
144 type Value = U128Text;
145
146 fn expecting(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
147 f.write_str("a u128 string or integer")
148 }
149
150 fn visit_u64<E>(self, value: u64) -> Result<Self::Value, E>
151 where
152 E: serde::de::Error,
153 {
154 Ok(U128Text(value.into()))
155 }
156
157 fn visit_u128<E>(self, value: u128) -> Result<Self::Value, E>
158 where
159 E: serde::de::Error,
160 {
161 Ok(U128Text(value))
162 }
163
164 fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
165 where
166 E: serde::de::Error,
167 {
168 Ok(U128Text(value.parse::<u128>().map_err(E::custom)?))
169 }
170 }
171
172 deserializer.deserialize_any(Visitor)
173 }
174}
175
176#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
177#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
178pub enum CustomValue {
179 Bytes32(Bytes32),
180 U32(u32),
181 U64(U64Text),
182 U128(U128Text),
183 String(String),
184 Bool(bool),
185 Composite(Vec<CustomScalarValue>),
186}
187
188#[derive(Serialize, Deserialize, Clone, Debug, PartialEq, Eq, Hash)]
189#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
190pub enum CustomScalarValue {
191 Bytes32(Bytes32),
192 U32(u32),
193 U64(U64Text),
194 U128(U128Text),
195 String(String),
196 Bool(bool),
197}
198
199#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
200#[serde(rename_all = "camelCase")]
201pub struct EventRef {
202 pub block_number: u32,
203 pub event_index: u16,
204}
205
206#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
207#[serde(rename_all = "camelCase")]
208pub struct DecodedEvent {
209 pub block_number: u32,
210 pub event_index: u16,
211 pub event: StoredEvent,
212}
213
214#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
215#[serde(rename_all = "camelCase")]
216pub struct StoredEvent {
217 pub spec_version: u32,
218 pub pallet_name: String,
219 pub event_name: String,
220 pub pallet_index: u8,
221 pub variant_index: u8,
222 pub event_index: u16,
223 pub fields: Value,
224}
225
226#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
227pub struct EventMeta {
228 pub index: u8,
229 pub name: String,
230}
231
232#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
233pub struct PalletMeta {
234 pub index: u8,
235 pub name: String,
236 pub events: Vec<EventMeta>,
237}
238
239#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
240pub struct Span {
241 pub start: u32,
242 pub end: u32,
243}
244
245#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
246#[serde(rename_all = "camelCase")]
247pub struct EventsResponse {
248 pub key: Key,
249 pub events: Vec<EventRef>,
250 #[serde(default)]
251 pub decoded_events: Vec<DecodedEvent>,
252}
253
254#[derive(Debug, Clone, PartialEq)]
255pub struct EventMatch {
256 pub event_ref: EventRef,
257 pub decoded_event: Option<DecodedEvent>,
258}
259
260#[derive(Serialize, Deserialize, Debug, Clone, PartialEq, Eq)]
261#[serde(tag = "type", rename_all = "camelCase")]
262pub enum SubscriptionTarget {
263 Status,
264 Events { key: Key },
265}
266
267#[derive(Debug, Clone, PartialEq, Eq)]
268pub struct StatusUpdate {
269 pub spans: Vec<Span>,
270}
271
272#[derive(Debug, Clone, PartialEq)]
273pub struct EventNotification {
274 pub key: Key,
275 pub event: EventRef,
276 pub decoded_event: Option<DecodedEvent>,
277}
278
279impl StoredEvent {
280 pub fn field(&self, name: &str) -> Option<&Value> {
281 self.fields.get(name)
282 }
283
284 pub fn variant(&self) -> (u8, u8) {
285 (self.pallet_index, self.variant_index)
286 }
287}
288
289impl DecodedEvent {
290 pub fn pallet_name(&self) -> &str {
291 &self.event.pallet_name
292 }
293
294 pub fn event_name(&self) -> &str {
295 &self.event.event_name
296 }
297
298 pub fn variant(&self) -> (u8, u8) {
299 self.event.variant()
300 }
301
302 pub fn field(&self, name: &str) -> Option<&Value> {
303 self.event.field(name)
304 }
305}
306
307impl EventsResponse {
308 pub fn event_matches(&self) -> Vec<EventMatch> {
309 let decoded_by_ref: HashMap<(u32, u16), DecodedEvent> = self
310 .decoded_events
311 .iter()
312 .cloned()
313 .map(|decoded| ((decoded.block_number, decoded.event_index), decoded))
314 .collect();
315
316 self.events
317 .iter()
318 .cloned()
319 .map(|event_ref| EventMatch {
320 decoded_event: decoded_by_ref
321 .get(&(event_ref.block_number, event_ref.event_index))
322 .cloned(),
323 event_ref,
324 })
325 .collect()
326 }
327}
328
329#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
330pub struct RequestMessage<T> {
331 pub id: u64,
332 #[serde(rename = "type")]
333 pub message_type: &'static str,
334 #[serde(flatten)]
335 pub payload: T,
336}
337
338#[derive(Serialize, Default, Debug, Clone, PartialEq, Eq)]
339pub struct EmptyPayload {}
340
341#[derive(Serialize, Debug, Clone, PartialEq)]
342pub struct GetEventsPayload {
343 pub key: Key,
344 #[serde(skip_serializing_if = "Option::is_none")]
345 pub limit: Option<u16>,
346 #[serde(skip_serializing_if = "Option::is_none")]
347 pub before: Option<EventRef>,
348}
349
350#[derive(Serialize, Debug, Clone, PartialEq, Eq)]
351pub struct SubscribeEventsPayload {
352 pub key: Key,
353}
354
355#[derive(Deserialize, Debug, Clone, PartialEq)]
356pub struct Envelope {
357 pub id: Option<u64>,
358 #[serde(rename = "type")]
359 pub message_type: String,
360 #[serde(default)]
361 pub data: Option<Value>,
362}
363
364#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
365pub struct ErrorPayload {
366 pub code: String,
367 pub message: String,
368}
369
370#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
371#[serde(rename_all = "camelCase")]
372pub struct SubscriptionStatusPayload {
373 pub action: String,
374 pub target: SubscriptionTarget,
375}
376
377#[derive(Deserialize, Debug, Clone, PartialEq, Eq)]
378#[serde(rename_all = "camelCase")]
379pub struct SubscriptionTerminatedPayload {
380 pub reason: String,
381 pub message: String,
382}
383
384#[derive(Deserialize, Debug, Clone, PartialEq)]
385#[serde(rename_all = "camelCase")]
386pub struct EventNotificationPayload {
387 pub key: Key,
388 pub event: EventRef,
389 pub decoded_event: Option<DecodedEvent>,
390}
391
392#[cfg(test)]
393mod tests {
394 use super::*;
395 use serde::de::value::{
396 Error as ValueError, StrDeserializer, U128Deserializer, U64Deserializer,
397 };
398 use serde::Deserialize;
399 use serde_json::json;
400
401 fn bytes32_hex(byte: u8) -> String {
402 format!("0x{}", hex::encode([byte; 32]))
403 }
404
405 #[test]
406 fn bytes32_serializes_as_prefixed_hex() {
407 assert_eq!(
408 serde_json::to_string(&Bytes32([0xAB; 32])).unwrap(),
409 format!("\"{}\"", bytes32_hex(0xAB))
410 );
411 }
412
413 #[test]
414 fn bytes32_deserializes_with_and_without_prefix() {
415 let prefixed =
416 serde_json::from_str::<Bytes32>(&format!("\"{}\"", bytes32_hex(0x11))).unwrap();
417 let unprefixed =
418 serde_json::from_str::<Bytes32>(&format!("\"{}\"", "22".repeat(32))).unwrap();
419
420 assert_eq!(prefixed, Bytes32([0x11; 32]));
421 assert_eq!(unprefixed, Bytes32([0x22; 32]));
422 }
423
424 #[test]
425 fn bytes32_rejects_wrong_length() {
426 let error = serde_json::from_str::<Bytes32>("\"0x1234\"")
427 .unwrap_err()
428 .to_string();
429 assert!(error.contains("expected 32 bytes"));
430 }
431
432 #[test]
433 fn u64_text_serializes_and_deserializes_multiple_input_shapes() {
434 assert_eq!(serde_json::to_string(&U64Text(42)).unwrap(), "\"42\"");
435 assert_eq!(
436 U64Text::deserialize(U64Deserializer::<ValueError>::new(42)).unwrap(),
437 U64Text(42)
438 );
439 assert_eq!(
440 U64Text::deserialize(U128Deserializer::<ValueError>::new(42)).unwrap(),
441 U64Text(42)
442 );
443 assert_eq!(
444 U64Text::deserialize(StrDeserializer::<ValueError>::new("42")).unwrap(),
445 U64Text(42)
446 );
447 }
448
449 #[test]
450 fn u128_text_serializes_and_deserializes_multiple_input_shapes() {
451 assert_eq!(serde_json::to_string(&U128Text(42)).unwrap(), "\"42\"");
452 assert_eq!(
453 U128Text::deserialize(U64Deserializer::<ValueError>::new(42)).unwrap(),
454 U128Text(42)
455 );
456 assert_eq!(
457 U128Text::deserialize(U128Deserializer::<ValueError>::new(42)).unwrap(),
458 U128Text(42)
459 );
460 assert_eq!(
461 U128Text::deserialize(StrDeserializer::<ValueError>::new("42")).unwrap(),
462 U128Text(42)
463 );
464 }
465
466 #[test]
467 fn custom_value_serializes_all_scalar_kinds() {
468 let cases = [
469 (
470 CustomValue::Bytes32(Bytes32([0xAB; 32])),
471 json!({"kind": "bytes32", "value": bytes32_hex(0xAB)}),
472 ),
473 (CustomValue::U32(7), json!({"kind": "u32", "value": 7})),
474 (
475 CustomValue::U64(U64Text(8)),
476 json!({"kind": "u64", "value": "8"}),
477 ),
478 (
479 CustomValue::U128(U128Text(9)),
480 json!({"kind": "u128", "value": "9"}),
481 ),
482 (
483 CustomValue::String("slug".into()),
484 json!({"kind": "string", "value": "slug"}),
485 ),
486 (
487 CustomValue::Bool(true),
488 json!({"kind": "bool", "value": true}),
489 ),
490 ];
491
492 for (value, expected) in cases {
493 assert_eq!(serde_json::to_value(value).unwrap(), expected);
494 }
495 }
496
497 #[test]
498 fn custom_key_scalar_constructor_preserves_scalar_value() {
499 assert_eq!(
500 CustomKey::scalar("ref_index", CustomValue::U32(42)),
501 CustomKey {
502 name: "ref_index".into(),
503 value: CustomValue::U32(42),
504 }
505 );
506 }
507
508 #[test]
509 fn custom_key_composite_constructor_collects_values() {
510 assert_eq!(
511 CustomKey::composite(
512 "item_revision",
513 [
514 CustomScalarValue::Bytes32([0x11; 32].into()),
515 CustomScalarValue::U32(7),
516 ],
517 ),
518 CustomKey {
519 name: "item_revision".into(),
520 value: CustomValue::Composite(vec![
521 CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
522 CustomScalarValue::U32(7),
523 ]),
524 }
525 );
526 }
527
528 #[test]
529 fn custom_key_composite_constructor_allows_empty_values() {
530 assert_eq!(
531 CustomKey::composite("empty", std::iter::empty()),
532 CustomKey {
533 name: "empty".into(),
534 value: CustomValue::Composite(vec![]),
535 }
536 );
537 }
538
539 #[test]
540 fn custom_value_serializes_flat_composite_values() {
541 let value = CustomValue::Composite(vec![
542 CustomScalarValue::Bytes32(Bytes32([0xAB; 32])),
543 CustomScalarValue::U32(7),
544 ]);
545
546 assert_eq!(
547 serde_json::to_value(value).unwrap(),
548 json!({
549 "kind": "composite",
550 "value": [
551 {"kind": "bytes32", "value": bytes32_hex(0xAB)},
552 {"kind": "u32", "value": 7}
553 ]
554 })
555 );
556 }
557
558 #[test]
559 fn custom_value_rejects_nested_composites() {
560 let error = serde_json::from_value::<CustomValue>(json!({
561 "kind": "composite",
562 "value": [
563 {"kind": "u32", "value": 7},
564 {
565 "kind": "composite",
566 "value": [
567 {"kind": "bool", "value": true}
568 ]
569 }
570 ]
571 }))
572 .unwrap_err()
573 .to_string();
574
575 assert!(error.contains("unknown variant `composite`"));
576 }
577
578 #[test]
579 fn key_deserializes_variant_and_custom_shapes() {
580 let variant =
581 serde_json::from_value::<Key>(json!({"type": "Variant", "value": [5, 3]})).unwrap();
582 let custom = serde_json::from_value::<Key>(json!({
583 "type": "Custom",
584 "value": {"name": "published", "kind": "bool", "value": true}
585 }))
586 .unwrap();
587
588 assert_eq!(variant, Key::Variant(5, 3));
589 assert_eq!(
590 custom,
591 Key::Custom(CustomKey {
592 name: "published".into(),
593 value: CustomValue::Bool(true),
594 })
595 );
596 }
597
598 #[test]
599 fn key_deserializes_composite_custom_shape() {
600 let custom = serde_json::from_value::<Key>(json!({
601 "type": "Custom",
602 "value": {
603 "name": "item_revision",
604 "kind": "composite",
605 "value": [
606 {"kind": "bytes32", "value": bytes32_hex(0x11)},
607 {"kind": "u32", "value": 7}
608 ]
609 }
610 }))
611 .unwrap();
612
613 assert_eq!(
614 custom,
615 Key::Custom(CustomKey {
616 name: "item_revision".into(),
617 value: CustomValue::Composite(vec![
618 CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
619 CustomScalarValue::U32(7),
620 ]),
621 })
622 );
623 }
624
625 #[test]
626 fn serializes_empty_payload_request_without_extra_fields() {
627 let request = RequestMessage {
628 id: 1,
629 message_type: "Status",
630 payload: EmptyPayload::default(),
631 };
632
633 let json = serde_json::to_value(&request).unwrap();
634 assert_eq!(json, json!({"id": 1, "type": "Status"}));
635 }
636
637 #[test]
638 fn serializes_get_events_request_in_server_shape() {
639 let request = RequestMessage {
640 id: 3,
641 message_type: "GetEvents",
642 payload: GetEventsPayload {
643 key: Key::Custom(CustomKey {
644 name: "item_id".into(),
645 value: CustomValue::Bytes32(Bytes32([0x12; 32])),
646 }),
647 limit: Some(25),
648 before: Some(EventRef {
649 block_number: 50,
650 event_index: 3,
651 }),
652 },
653 };
654
655 let json = serde_json::to_value(&request).unwrap();
656 assert_eq!(json["id"], 3);
657 assert_eq!(json["type"], "GetEvents");
658 assert_eq!(json["key"]["type"], "Custom");
659 assert_eq!(json["key"]["value"]["name"], "item_id");
660 assert_eq!(json["key"]["value"]["kind"], "bytes32");
661 assert_eq!(json["limit"], 25);
662 assert_eq!(json["before"]["blockNumber"], 50);
663 assert_eq!(json["before"]["eventIndex"], 3);
664 }
665
666 #[test]
667 fn serializes_get_events_request_without_optional_fields() {
668 let request = RequestMessage {
669 id: 4,
670 message_type: "GetEvents",
671 payload: GetEventsPayload {
672 key: Key::Custom(CustomKey {
673 name: "ref_index".into(),
674 value: CustomValue::U32(42),
675 }),
676 limit: None,
677 before: None,
678 },
679 };
680
681 let json = serde_json::to_value(&request).unwrap();
682 assert_eq!(
683 json,
684 json!({
685 "id": 4,
686 "type": "GetEvents",
687 "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}}
688 })
689 );
690 }
691
692 #[test]
693 fn serializes_get_events_request_with_composite_custom_key() {
694 let request = RequestMessage {
695 id: 5,
696 message_type: "GetEvents",
697 payload: GetEventsPayload {
698 key: Key::Custom(CustomKey {
699 name: "item_revision".into(),
700 value: CustomValue::Composite(vec![
701 CustomScalarValue::Bytes32(Bytes32([0x12; 32])),
702 CustomScalarValue::U32(7),
703 ]),
704 }),
705 limit: None,
706 before: None,
707 },
708 };
709
710 let json = serde_json::to_value(&request).unwrap();
711 assert_eq!(json["key"]["value"]["kind"], "composite");
712 assert_eq!(json["key"]["value"]["value"][0]["kind"], "bytes32");
713 assert_eq!(
714 json["key"]["value"]["value"][1],
715 json!({"kind": "u32", "value": 7})
716 );
717 }
718
719 #[test]
720 fn serializes_subscribe_events_request_in_server_shape() {
721 let request = RequestMessage {
722 id: 7,
723 message_type: "SubscribeEvents",
724 payload: SubscribeEventsPayload {
725 key: Key::Custom(CustomKey {
726 name: "account_id".into(),
727 value: CustomValue::Bytes32(Bytes32([0xAB; 32])),
728 }),
729 },
730 };
731
732 let json = serde_json::to_value(&request).unwrap();
733 assert_eq!(json["type"], "SubscribeEvents");
734 assert_eq!(json["key"]["value"]["kind"], "bytes32");
735 }
736
737 #[test]
738 fn serializes_subscribe_events_request_with_composite_custom_key() {
739 let request = RequestMessage {
740 id: 8,
741 message_type: "SubscribeEvents",
742 payload: SubscribeEventsPayload {
743 key: Key::Custom(CustomKey {
744 name: "item_revision".into(),
745 value: CustomValue::Composite(vec![
746 CustomScalarValue::Bytes32(Bytes32([0xAB; 32])),
747 CustomScalarValue::U32(9),
748 ]),
749 }),
750 },
751 };
752
753 let json = serde_json::to_value(&request).unwrap();
754 assert_eq!(json["type"], "SubscribeEvents");
755 assert_eq!(json["key"]["value"]["kind"], "composite");
756 }
757
758 #[test]
759 fn deserializes_events_response_payload() {
760 let payload = serde_json::from_value::<EventsResponse>(json!({
761 "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}},
762 "events": [{"blockNumber": 50, "eventIndex": 3}],
763 "decodedEvents": [{
764 "blockNumber": 50,
765 "eventIndex": 3,
766 "event": {
767 "specVersion": 1234,
768 "palletName": "Referenda",
769 "eventName": "Submitted",
770 "palletIndex": 42,
771 "variantIndex": 0,
772 "eventIndex": 3,
773 "fields": {"index": 42}
774 }
775 }]
776 }))
777 .unwrap();
778
779 assert_eq!(payload.events.len(), 1);
780 assert_eq!(payload.decoded_events.len(), 1);
781 }
782
783 #[test]
784 fn deserializes_events_response_without_decoded_events() {
785 let payload = serde_json::from_value::<EventsResponse>(json!({
786 "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}},
787 "events": [{"blockNumber": 50, "eventIndex": 3}]
788 }))
789 .unwrap();
790
791 assert_eq!(payload.events.len(), 1);
792 assert!(payload.decoded_events.is_empty());
793 }
794
795 #[test]
796 fn deserializes_events_response_with_composite_key() {
797 let payload = serde_json::from_value::<EventsResponse>(json!({
798 "key": {
799 "type": "Custom",
800 "value": {
801 "name": "item_revision",
802 "kind": "composite",
803 "value": [
804 {"kind": "bytes32", "value": bytes32_hex(0x11)},
805 {"kind": "u32", "value": 7}
806 ]
807 }
808 },
809 "events": [{"blockNumber": 50, "eventIndex": 3}]
810 }))
811 .unwrap();
812
813 assert_eq!(
814 payload.key,
815 Key::Custom(CustomKey {
816 name: "item_revision".into(),
817 value: CustomValue::Composite(vec![
818 CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
819 CustomScalarValue::U32(7),
820 ]),
821 })
822 );
823 }
824
825 #[test]
826 fn stored_event_and_events_response_helpers_work() {
827 let payload = serde_json::from_value::<EventsResponse>(json!({
828 "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}},
829 "events": [
830 {"blockNumber": 50, "eventIndex": 3},
831 {"blockNumber": 49, "eventIndex": 1}
832 ],
833 "decodedEvents": [{
834 "blockNumber": 50,
835 "eventIndex": 3,
836 "event": {
837 "specVersion": 1234,
838 "palletName": "Referenda",
839 "eventName": "Submitted",
840 "palletIndex": 42,
841 "variantIndex": 0,
842 "eventIndex": 3,
843 "fields": {"index": 42}
844 }
845 }]
846 }))
847 .unwrap();
848
849 assert_eq!(payload.decoded_events[0].pallet_name(), "Referenda");
850 assert_eq!(payload.decoded_events[0].event_name(), "Submitted");
851 assert_eq!(payload.decoded_events[0].variant(), (42, 0));
852 assert_eq!(payload.decoded_events[0].field("index"), Some(&json!(42)));
853
854 let matches = payload.event_matches();
855 assert_eq!(matches.len(), 2);
856 assert!(matches[0].decoded_event.is_some());
857 assert!(matches[1].decoded_event.is_none());
858 }
859
860 #[test]
861 fn stored_event_field_returns_none_for_non_object_fields() {
862 let event = StoredEvent {
863 spec_version: 1,
864 pallet_name: "Example".into(),
865 event_name: "Positional".into(),
866 pallet_index: 1,
867 variant_index: 2,
868 event_index: 3,
869 fields: json!([1, 2, 3]),
870 };
871
872 assert_eq!(event.variant(), (1, 2));
873 assert!(event.field("0").is_none());
874 }
875
876 #[test]
877 fn deserializes_subscription_status_payload() {
878 let payload = serde_json::from_value::<SubscriptionStatusPayload>(json!({
879 "action": "subscribed",
880 "target": {"type": "events", "key": {"type": "Custom", "value": {"name": "ref_index", "kind": "u32", "value": 42}}}
881 }))
882 .unwrap();
883
884 assert_eq!(payload.action, "subscribed");
885 assert_eq!(
886 payload.target,
887 SubscriptionTarget::Events {
888 key: Key::Custom(CustomKey {
889 name: "ref_index".into(),
890 value: CustomValue::U32(42),
891 })
892 }
893 );
894 }
895
896 #[test]
897 fn deserializes_subscription_status_payload_with_composite_key() {
898 let payload = serde_json::from_value::<SubscriptionStatusPayload>(json!({
899 "action": "subscribed",
900 "target": {
901 "type": "events",
902 "key": {
903 "type": "Custom",
904 "value": {
905 "name": "item_revision",
906 "kind": "composite",
907 "value": [
908 {"kind": "bytes32", "value": bytes32_hex(0x11)},
909 {"kind": "u32", "value": 7}
910 ]
911 }
912 }
913 }
914 }))
915 .unwrap();
916
917 assert_eq!(
918 payload.target,
919 SubscriptionTarget::Events {
920 key: Key::Custom(CustomKey {
921 name: "item_revision".into(),
922 value: CustomValue::Composite(vec![
923 CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
924 CustomScalarValue::U32(7),
925 ]),
926 })
927 }
928 );
929 }
930
931 #[test]
932 fn deserializes_subscription_terminated_payload() {
933 let payload = serde_json::from_value::<SubscriptionTerminatedPayload>(json!({
934 "reason": "backpressure",
935 "message": "subscriber disconnected due to backpressure"
936 }))
937 .unwrap();
938
939 assert_eq!(payload.reason, "backpressure");
940 assert_eq!(
941 payload.message,
942 "subscriber disconnected due to backpressure"
943 );
944 }
945
946 #[test]
947 fn deserializes_event_notification_payload() {
948 let payload = serde_json::from_value::<EventNotificationPayload>(json!({
949 "key": {"type": "Custom", "value": {"name": "item_id", "kind": "bytes32", "value": bytes32_hex(0x11)}},
950 "event": {"blockNumber": 50, "eventIndex": 3},
951 "decodedEvent": {
952 "blockNumber": 50,
953 "eventIndex": 3,
954 "event": {
955 "specVersion": 1234,
956 "palletName": "Content",
957 "eventName": "PublishRevision",
958 "palletIndex": 42,
959 "variantIndex": 1,
960 "eventIndex": 3,
961 "fields": {"revision_id": 1}
962 }
963 }
964 }))
965 .unwrap();
966
967 assert_eq!(payload.event.block_number, 50);
968 assert!(payload.decoded_event.is_some());
969 }
970
971 #[test]
972 fn deserializes_event_notification_payload_with_composite_key() {
973 let payload = serde_json::from_value::<EventNotificationPayload>(json!({
974 "key": {
975 "type": "Custom",
976 "value": {
977 "name": "item_revision",
978 "kind": "composite",
979 "value": [
980 {"kind": "bytes32", "value": bytes32_hex(0x11)},
981 {"kind": "u32", "value": 7}
982 ]
983 }
984 },
985 "event": {"blockNumber": 50, "eventIndex": 3},
986 "decodedEvent": null
987 }))
988 .unwrap();
989
990 assert_eq!(
991 payload.key,
992 Key::Custom(CustomKey {
993 name: "item_revision".into(),
994 value: CustomValue::Composite(vec![
995 CustomScalarValue::Bytes32(Bytes32([0x11; 32])),
996 CustomScalarValue::U32(7),
997 ]),
998 })
999 );
1000 }
1001
1002 #[test]
1003 fn envelope_defaults_missing_data_to_none() {
1004 let envelope = serde_json::from_value::<Envelope>(json!({
1005 "id": 1,
1006 "type": "subscriptionStatus"
1007 }))
1008 .unwrap();
1009
1010 assert_eq!(envelope.id, Some(1));
1011 assert_eq!(envelope.message_type, "subscriptionStatus");
1012 assert_eq!(envelope.data, None);
1013 }
1014}