gevulot_rs/
events.rs

1use cosmrs::tendermint::block::Height;
2
3use crate::error::Error;
4
5#[derive(Clone, Debug)]
6pub enum GevulotEvent {
7    Pin(PinEvent),
8    Task(TaskEvent),
9    Worker(WorkerEvent),
10    Workflow(WorkflowEvent),
11}
12
13impl GevulotEvent {
14    pub fn from_cosmos(
15        event: &cosmrs::tendermint::abci::Event,
16        block_height: Height,
17    ) -> crate::error::Result<Self> {
18        match event.kind.as_str() {
19            "create-worker" => {
20                let worker_id = event
21                    .attributes
22                    .iter()
23                    .find(|attr| attr.key_bytes() == b"worker-id")
24                    .ok_or(Error::MissingEventAttribute("worker-id"))?
25                    .value_str()?
26                    .to_string();
27
28                let creator = event
29                    .attributes
30                    .iter()
31                    .find(|attr| attr.key_bytes() == b"creator")
32                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
33                    .unwrap_or_default();
34
35                Ok(GevulotEvent::Worker(WorkerEvent::Create(
36                    WorkerCreateEvent {
37                        block_height,
38                        worker_id,
39                        creator,
40                    },
41                )))
42            }
43            "update-worker" => {
44                let worker_id = event
45                    .attributes
46                    .iter()
47                    .find(|attr| attr.key_bytes() == b"worker-id")
48                    .ok_or(Error::MissingEventAttribute("worker-id"))?
49                    .value_str()?
50                    .to_string();
51
52                let creator = event
53                    .attributes
54                    .iter()
55                    .find(|attr| attr.key_bytes() == b"creator")
56                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
57                    .unwrap_or_default();
58
59                Ok(GevulotEvent::Worker(WorkerEvent::Update(
60                    WorkerUpdateEvent {
61                        block_height,
62                        worker_id,
63                        creator,
64                    },
65                )))
66            }
67            "delete-worker" => {
68                let worker_id = event
69                    .attributes
70                    .iter()
71                    .find(|attr| attr.key_bytes() == b"worker-id")
72                    .ok_or(Error::MissingEventAttribute("worker-id"))?
73                    .value_str()?
74                    .to_string();
75
76                let creator = event
77                    .attributes
78                    .iter()
79                    .find(|attr| attr.key_bytes() == b"creator")
80                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
81                    .unwrap_or_default();
82
83                Ok(GevulotEvent::Worker(WorkerEvent::Delete(
84                    WorkerDeleteEvent {
85                        block_height,
86                        worker_id,
87                        creator,
88                    },
89                )))
90            }
91            "announce-worker-exit" => {
92                let worker_id = event
93                    .attributes
94                    .iter()
95                    .find(|attr| attr.key_bytes() == b"worker-id")
96                    .ok_or(Error::MissingEventAttribute("worker-id"))?
97                    .value_str()?
98                    .to_string();
99
100                let creator = event
101                    .attributes
102                    .iter()
103                    .find(|attr| attr.key_bytes() == b"creator")
104                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
105                    .unwrap_or_default();
106
107                Ok(GevulotEvent::Worker(WorkerEvent::AnnounceExit(
108                    WorkerAnnounceExitEvent {
109                        block_height,
110                        worker_id,
111                        creator,
112                    },
113                )))
114            }
115            "create-task" => {
116                let task_id = event
117                    .attributes
118                    .iter()
119                    .find(|attr| attr.key_bytes() == b"task-id")
120                    .ok_or(Error::MissingEventAttribute("task-id"))?
121                    .value_str()?
122                    .to_string();
123
124                let creator = event
125                    .attributes
126                    .iter()
127                    .find(|attr| attr.key_bytes() == b"creator")
128                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
129                    .unwrap_or_default();
130
131                let assigned_workers = event
132                    .attributes
133                    .iter()
134                    .filter(|attr| attr.key_bytes() == b"worker-id")
135                    .flat_map(|attr| {
136                        attr.value_str()
137                            .map(|s| {
138                                s.split(',')
139                                    .map(|x| x.trim().to_string())
140                                    .collect::<Vec<_>>()
141                            })
142                            .unwrap_or_default()
143                    })
144                    .collect::<Vec<String>>();
145
146                Ok(GevulotEvent::Task(TaskEvent::Create(TaskCreateEvent {
147                    block_height,
148                    task_id,
149                    creator,
150                    assigned_workers,
151                })))
152            }
153            "delete-task" => {
154                let task_id = event
155                    .attributes
156                    .iter()
157                    .find(|attr| attr.key_bytes() == b"task-id")
158                    .ok_or(Error::MissingEventAttribute("task-id"))?
159                    .value_str()?
160                    .to_string();
161
162                let creator = event
163                    .attributes
164                    .iter()
165                    .find(|attr| attr.key_bytes() == b"creator")
166                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
167                    .unwrap_or_default();
168
169                Ok(GevulotEvent::Task(TaskEvent::Delete(TaskDeleteEvent {
170                    block_height,
171                    task_id,
172                    creator,
173                })))
174            }
175            "finish-task" => {
176                let task_id = event
177                    .attributes
178                    .iter()
179                    .find(|attr| attr.key_bytes() == b"task-id")
180                    .ok_or(Error::MissingEventAttribute("task-id"))?
181                    .value_str()?
182                    .to_string();
183
184                let worker_id = event
185                    .attributes
186                    .iter()
187                    .find(|attr| attr.key_bytes() == b"worker-id")
188                    .ok_or(Error::MissingEventAttribute("worker-id"))?
189                    .value_str()?
190                    .to_string();
191
192                let creator = event
193                    .attributes
194                    .iter()
195                    .find(|attr| attr.key_bytes() == b"creator")
196                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
197                    .unwrap_or_default();
198                Ok(GevulotEvent::Task(TaskEvent::Finish(TaskFinishEvent {
199                    block_height,
200                    task_id,
201                    worker_id,
202                    creator,
203                })))
204            }
205            "decline-task" => {
206                let task_id = event
207                    .attributes
208                    .iter()
209                    .find(|attr| attr.key_bytes() == b"task-id")
210                    .ok_or(Error::MissingEventAttribute("task-id"))?
211                    .value_str()?
212                    .to_string();
213
214                let creator = event
215                    .attributes
216                    .iter()
217                    .find(|attr| attr.key_bytes() == b"creator")
218                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
219                    .unwrap_or_default();
220
221                let worker_id = event
222                    .attributes
223                    .iter()
224                    .find(|attr| attr.key_bytes() == b"worker-id")
225                    .ok_or(Error::MissingEventAttribute("worker-id"))?
226                    .value_str()?
227                    .to_string();
228                Ok(GevulotEvent::Task(TaskEvent::Decline(TaskDeclineEvent {
229                    block_height,
230                    task_id,
231                    creator,
232                    worker_id,
233                })))
234            }
235            "accept-task" => {
236                let task_id = event
237                    .attributes
238                    .iter()
239                    .find(|attr| attr.key_bytes() == b"task-id")
240                    .ok_or(Error::MissingEventAttribute("task-id"))?
241                    .value_str()?
242                    .to_string();
243
244                let creator = event
245                    .attributes
246                    .iter()
247                    .find(|attr| attr.key_bytes() == b"creator")
248                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
249                    .unwrap_or_default();
250
251                let worker_id = event
252                    .attributes
253                    .iter()
254                    .find(|attr| attr.key_bytes() == b"worker-id")
255                    .ok_or(Error::MissingEventAttribute("worker-id"))?
256                    .value_str()?
257                    .to_string();
258                Ok(GevulotEvent::Task(TaskEvent::Accept(TaskAcceptEvent {
259                    block_height,
260                    task_id,
261                    creator,
262                    worker_id,
263                })))
264            }
265            "create-workflow" => {
266                let workflow_id = event
267                    .attributes
268                    .iter()
269                    .find(|attr| attr.key_bytes() == b"workflow-id")
270                    .ok_or(Error::MissingEventAttribute("workflow-id"))?
271                    .value_str()?
272                    .to_string();
273
274                let creator = event
275                    .attributes
276                    .iter()
277                    .find(|attr| attr.key_bytes() == b"creator")
278                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
279                    .unwrap_or_default();
280
281                Ok(GevulotEvent::Workflow(WorkflowEvent::Create(
282                    WorkflowCreateEvent {
283                        block_height,
284                        workflow_id,
285                        creator,
286                    },
287                )))
288            }
289            "delete-workflow" => {
290                let workflow_id = event
291                    .attributes
292                    .iter()
293                    .find(|attr| attr.key_bytes() == b"workflow-id")
294                    .ok_or(Error::MissingEventAttribute("workflow-id"))?
295                    .value_str()?
296                    .to_string();
297
298                let creator = event
299                    .attributes
300                    .iter()
301                    .find(|attr| attr.key_bytes() == b"creator")
302                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
303                    .unwrap_or_default();
304
305                Ok(GevulotEvent::Workflow(WorkflowEvent::Delete(
306                    WorkflowDeleteEvent {
307                        block_height,
308                        workflow_id,
309                        creator,
310                    },
311                )))
312            }
313            "finish-workflow" => {
314                let workflow_id = event
315                    .attributes
316                    .iter()
317                    .find(|attr| attr.key_bytes() == b"workflow-id")
318                    .ok_or(Error::MissingEventAttribute("workflow-id"))?
319                    .value_str()?
320                    .to_string();
321
322                let creator = event
323                    .attributes
324                    .iter()
325                    .find(|attr| attr.key_bytes() == b"creator")
326                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
327                    .unwrap_or_default();
328
329                Ok(GevulotEvent::Workflow(WorkflowEvent::Finish(
330                    WorkflowFinishEvent {
331                        block_height,
332                        workflow_id,
333                        creator,
334                    },
335                )))
336            }
337            "progress-workflow" => {
338                let workflow_id = event
339                    .attributes
340                    .iter()
341                    .find(|attr| attr.key_bytes() == b"workflow-id")
342                    .ok_or(Error::MissingEventAttribute("workflow-id"))?
343                    .value_str()?
344                    .to_string();
345
346                let creator = event
347                    .attributes
348                    .iter()
349                    .find(|attr| attr.key_bytes() == b"creator")
350                    .ok_or(Error::MissingEventAttribute("creator"))?
351                    .value_str()?
352                    .to_string();
353
354                Ok(GevulotEvent::Workflow(WorkflowEvent::Progress(
355                    WorkflowProgressEvent {
356                        block_height,
357                        workflow_id,
358                        creator,
359                    },
360                )))
361            }
362            "create-pin" => {
363                let cid = event
364                    .attributes
365                    .iter()
366                    .find(|attr| attr.key_bytes() == b"cid")
367                    .ok_or(Error::MissingEventAttribute("cid"))?
368                    .value_str()?
369                    .to_string();
370                let creator = event
371                    .attributes
372                    .iter()
373                    .find(|attr| attr.key_bytes() == b"creator")
374                    .ok_or(Error::MissingEventAttribute("creator"))?
375                    .value_str()?
376                    .to_string();
377                let assigned_workers = event
378                    .attributes
379                    .iter()
380                    .filter(|attr| attr.key_bytes() == b"assigned-workers")
381                    .flat_map(|attr| {
382                        attr.value_str()
383                            .map(|s| {
384                                s.split(',')
385                                    .map(|x| x.trim().to_string())
386                                    .collect::<Vec<_>>()
387                            })
388                            .unwrap_or_default()
389                    })
390                    .collect::<Vec<String>>();
391                let retention_period = event
392                    .attributes
393                    .iter()
394                    .find(|attr| attr.key_bytes() == b"retention-period")
395                    .ok_or(Error::MissingEventAttribute("retention-period"))?
396                    .value_str()?
397                    .parse()
398                    .map_err(|_| Error::InvalidEventAttribute("retention-period"))?;
399                let fallback_urls = event
400                    .attributes
401                    .iter()
402                    .filter(|attr| attr.key_bytes() == b"fallback-urls")
403                    .flat_map(|attr| {
404                        attr.value_str()
405                            .map(|s| {
406                                s.split(',')
407                                    .map(|x| x.trim().to_string())
408                                    .collect::<Vec<_>>()
409                            })
410                            .unwrap_or_default()
411                    })
412                    .filter(|url| !url.is_empty())
413                    .collect::<Vec<String>>();
414                let id = event
415                    .attributes
416                    .iter()
417                    .find(|attr| attr.key_bytes() == b"id")
418                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
419                    .unwrap_or_else(|| cid.clone());
420
421                Ok(GevulotEvent::Pin(PinEvent::Create(PinCreateEvent {
422                    block_height,
423                    cid,
424                    creator,
425                    assigned_workers,
426                    retention_period,
427                    fallback_urls,
428                    id,
429                })))
430            }
431            "delete-pin" => {
432                let cid = event
433                    .attributes
434                    .iter()
435                    .find(|attr| attr.key_bytes() == b"cid")
436                    .ok_or(Error::MissingEventAttribute("cid"))?
437                    .value_str()?
438                    .to_string();
439                let creator = event
440                    .attributes
441                    .iter()
442                    .find(|attr| attr.key_bytes() == b"creator")
443                    .ok_or(Error::MissingEventAttribute("creator"))?
444                    .value_str()?
445                    .to_string();
446                let id = event
447                    .attributes
448                    .iter()
449                    .find(|attr| attr.key_bytes() == b"id")
450                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
451                    .unwrap_or_else(|| cid.clone());
452
453                Ok(GevulotEvent::Pin(PinEvent::Delete(PinDeleteEvent {
454                    block_height,
455                    cid,
456                    creator,
457                    id,
458                })))
459            }
460            "ack-pin" => {
461                let cid = event
462                    .attributes
463                    .iter()
464                    .find(|attr| attr.key_bytes() == b"cid")
465                    .ok_or(Error::MissingEventAttribute("cid"))?
466                    .value_str()?
467                    .to_string();
468                let worker_id = event
469                    .attributes
470                    .iter()
471                    .find(|attr| attr.key_bytes() == b"worker-id")
472                    .ok_or(Error::MissingEventAttribute("worker-id"))?
473                    .value_str()?
474                    .to_string();
475                let success = event
476                    .attributes
477                    .iter()
478                    .find(|attr| attr.key_bytes() == b"success")
479                    .map(|attr| attr.value_str().unwrap_or("true").parse().unwrap_or(true))
480                    .unwrap_or(true);
481                let id = event
482                    .attributes
483                    .iter()
484                    .find(|attr| attr.key_bytes() == b"id")
485                    .map(|attr| attr.value_str().unwrap_or_default().to_string())
486                    .unwrap_or_else(|| cid.clone());
487                Ok(GevulotEvent::Pin(PinEvent::Ack(PinAckEvent {
488                    block_height,
489                    cid,
490                    worker_id,
491                    success,
492                    id,
493                })))
494            }
495            _ => Err(Error::UnknownEventKind(event.kind.clone())),
496        }
497    }
498}
499
500#[derive(Clone, Debug)]
501pub struct PinCreateEvent {
502    pub block_height: Height,
503    pub cid: String,
504    pub id: String,
505    pub creator: String,
506    pub assigned_workers: Vec<String>,
507    pub retention_period: u64,
508    pub fallback_urls: Vec<String>,
509}
510
511#[derive(Clone, Debug)]
512pub struct PinDeleteEvent {
513    pub block_height: Height,
514    pub cid: String,
515    pub id: String,
516    pub creator: String,
517}
518
519#[derive(Clone, Debug)]
520pub struct PinAckEvent {
521    pub block_height: Height,
522    pub cid: String,
523    pub id: String,
524    pub worker_id: String,
525    pub success: bool,
526}
527
528#[derive(Clone, Debug)]
529pub enum PinEvent {
530    Create(PinCreateEvent),
531    Delete(PinDeleteEvent),
532    Ack(PinAckEvent),
533}
534
535#[derive(Clone, Debug)]
536pub struct TaskCreateEvent {
537    pub block_height: Height,
538    pub task_id: String,
539    pub creator: String,
540    pub assigned_workers: Vec<String>,
541}
542
543#[derive(Clone, Debug)]
544pub struct TaskDeleteEvent {
545    pub block_height: Height,
546    pub task_id: String,
547    pub creator: String,
548}
549
550#[derive(Clone, Debug)]
551pub struct TaskAcceptEvent {
552    pub block_height: Height,
553    pub task_id: String,
554    pub worker_id: String,
555    pub creator: String,
556}
557
558#[derive(Clone, Debug)]
559pub struct TaskDeclineEvent {
560    pub block_height: Height,
561    pub task_id: String,
562    pub worker_id: String,
563    pub creator: String,
564}
565
566#[derive(Clone, Debug)]
567pub struct TaskFinishEvent {
568    pub block_height: Height,
569    pub task_id: String,
570    pub worker_id: String,
571    pub creator: String,
572}
573
574#[derive(Clone, Debug)]
575pub enum TaskEvent {
576    Create(TaskCreateEvent),
577    Delete(TaskDeleteEvent),
578    Accept(TaskAcceptEvent),
579    Decline(TaskDeclineEvent),
580    Finish(TaskFinishEvent),
581}
582
583#[derive(Clone, Debug)]
584pub struct WorkerCreateEvent {
585    pub block_height: Height,
586    pub worker_id: String,
587    pub creator: String,
588}
589
590#[derive(Clone, Debug)]
591pub struct WorkerUpdateEvent {
592    pub block_height: Height,
593    pub worker_id: String,
594    pub creator: String,
595}
596
597#[derive(Clone, Debug)]
598pub struct WorkerDeleteEvent {
599    pub block_height: Height,
600    pub worker_id: String,
601    pub creator: String,
602}
603
604#[derive(Clone, Debug)]
605pub struct WorkerAnnounceExitEvent {
606    pub block_height: Height,
607    pub worker_id: String,
608    pub creator: String,
609}
610
611#[derive(Clone, Debug)]
612pub enum WorkerEvent {
613    Create(WorkerCreateEvent),
614    Update(WorkerUpdateEvent),
615    Delete(WorkerDeleteEvent),
616    AnnounceExit(WorkerAnnounceExitEvent),
617}
618
619#[derive(Clone, Debug)]
620pub struct WorkflowCreateEvent {
621    pub block_height: Height,
622    pub workflow_id: String,
623    pub creator: String,
624}
625
626#[derive(Clone, Debug)]
627pub struct WorkflowDeleteEvent {
628    pub block_height: Height,
629    pub workflow_id: String,
630    pub creator: String,
631}
632
633#[derive(Clone, Debug)]
634pub struct WorkflowProgressEvent {
635    pub block_height: Height,
636    pub workflow_id: String,
637    pub creator: String,
638}
639
640#[derive(Clone, Debug)]
641pub struct WorkflowFinishEvent {
642    pub block_height: Height,
643    pub workflow_id: String,
644    pub creator: String,
645}
646
647#[derive(Clone, Debug)]
648pub enum WorkflowEvent {
649    Create(WorkflowCreateEvent),
650    Delete(WorkflowDeleteEvent),
651    Progress(WorkflowProgressEvent),
652    Finish(WorkflowFinishEvent),
653}
654
655#[cfg(test)]
656mod tests {
657
658    use super::*;
659    use cosmrs::{rpc::dialect::v0_34::EventAttribute, tendermint::abci::Event};
660
661    #[test]
662    fn test_from_cosmos_create_pin() {
663        let event = Event::new(
664            "create-pin",
665            vec![
666                EventAttribute {
667                    index: true,
668                    key: b"cid".to_vec(),
669                    value: b"QmYwMXeEc3Z64vqcPXx8p8Y8Y5tE9Y5sYW42FZ1U87Y".to_vec(),
670                },
671                EventAttribute {
672                    index: true,
673                    key: b"creator".to_vec(),
674                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
675                },
676                EventAttribute {
677                    index: true,
678                    key: b"assigned-workers".to_vec(),
679                    value: b"1,2,3".to_vec(),
680                },
681                EventAttribute {
682                    index: true,
683                    key: b"retention-period".to_vec(),
684                    value: b"86400".to_vec(),
685                },
686                EventAttribute {
687                    index: true,
688                    key: b"fallback-urls".to_vec(),
689                    value: b"https://example1.com,https://example2.org".to_vec(),
690                },
691                EventAttribute {
692                    index: true,
693                    key: b"fallback-urls".to_vec(),
694                    value: b"https://example3.com".to_vec(),
695                },
696            ],
697        );
698
699        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
700
701        assert!(parsed.is_ok());
702        if let Ok(GevulotEvent::Pin(PinEvent::Create(event))) = parsed {
703            assert_eq!(event.block_height, Height::from(1000u32));
704            assert_eq!(event.cid, "QmYwMXeEc3Z64vqcPXx8p8Y8Y5tE9Y5sYW42FZ1U87Y");
705            assert_eq!(
706                event.creator,
707                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
708            );
709            assert_eq!(event.assigned_workers, vec!["1", "2", "3"]);
710            assert_eq!(event.retention_period, 86400);
711            assert_eq!(
712                event.fallback_urls,
713                vec![
714                    "https://example1.com",
715                    "https://example2.org",
716                    "https://example3.com"
717                ]
718            );
719        } else {
720            panic!("Unexpected event type");
721        }
722    }
723
724    #[test]
725    fn test_from_cosmos_delete_pin() {
726        let event = Event::new(
727            "delete-pin",
728            vec![
729                EventAttribute {
730                    index: true,
731                    key: b"cid".to_vec(),
732                    value: b"QmYwMXeEc3Z64vqcPXx8p8Y8Y5tE9Y5sYW42FZ1U87Y".to_vec(),
733                },
734                EventAttribute {
735                    index: true,
736                    key: b"creator".to_vec(),
737                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
738                },
739            ],
740        );
741
742        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
743
744        assert!(parsed.is_ok());
745        if let Ok(GevulotEvent::Pin(PinEvent::Delete(event))) = parsed {
746            assert_eq!(event.block_height, Height::from(1000u32));
747            assert_eq!(event.cid, "QmYwMXeEc3Z64vqcPXx8p8Y8Y5tE9Y5sYW42FZ1U87Y");
748            assert_eq!(
749                event.creator,
750                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
751            );
752        } else {
753            panic!("Unexpected event type");
754        }
755    }
756
757    #[test]
758    fn test_from_cosmos_ack_pin() {
759        let event = Event::new(
760            "ack-pin",
761            vec![
762                EventAttribute {
763                    index: true,
764                    key: b"cid".to_vec(),
765                    value: b"QmYwMXeEc3Z64vqcPXx8p8Y8Y5tE9Y5sYW42FZ1U87Y".to_vec(),
766                },
767                EventAttribute {
768                    index: true,
769                    key: b"worker-id".to_vec(),
770                    value: b"worker1".to_vec(),
771                },
772                EventAttribute {
773                    index: true,
774                    key: b"success".to_vec(),
775                    value: b"true".to_vec(),
776                },
777                EventAttribute {
778                    index: true,
779                    key: b"id".to_vec(),
780                    value: b"123".to_vec(),
781                },
782            ],
783        );
784
785        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
786
787        assert!(parsed.is_ok());
788        if let Ok(GevulotEvent::Pin(PinEvent::Ack(event))) = parsed {
789            assert_eq!(event.block_height, Height::from(1000u32));
790            assert_eq!(event.cid, "QmYwMXeEc3Z64vqcPXx8p8Y8Y5tE9Y5sYW42FZ1U87Y");
791            assert_eq!(event.worker_id, "worker1");
792            assert!(event.success);
793            assert_eq!(event.id, "123");
794        } else {
795            panic!("Unexpected event type");
796        }
797    }
798
799    #[test]
800    fn test_from_cosmos_create_worker() {
801        let event = Event::new(
802            "create-worker",
803            vec![
804                EventAttribute {
805                    index: true,
806                    key: b"worker-id".to_vec(),
807                    value: b"worker1".to_vec(),
808                },
809                EventAttribute {
810                    index: true,
811                    key: b"creator".to_vec(),
812                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
813                },
814            ],
815        );
816
817        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
818
819        assert!(parsed.is_ok());
820        if let Ok(GevulotEvent::Worker(WorkerEvent::Create(event))) = parsed {
821            assert_eq!(event.block_height, Height::from(1000u32));
822            assert_eq!(event.worker_id, "worker1");
823            assert_eq!(
824                event.creator,
825                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
826            );
827        } else {
828            panic!("Unexpected event type");
829        }
830    }
831
832    #[test]
833    fn test_from_cosmos_update_worker() {
834        let event = Event::new(
835            "update-worker",
836            vec![
837                EventAttribute {
838                    index: true,
839                    key: b"worker-id".to_vec(),
840                    value: b"worker1".to_vec(),
841                },
842                EventAttribute {
843                    index: true,
844                    key: b"creator".to_vec(),
845                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
846                },
847            ],
848        );
849
850        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
851
852        assert!(parsed.is_ok());
853        if let Ok(GevulotEvent::Worker(WorkerEvent::Update(event))) = parsed {
854            assert_eq!(event.block_height, Height::from(1000u32));
855            assert_eq!(event.worker_id, "worker1");
856            assert_eq!(
857                event.creator,
858                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
859            );
860        } else {
861            panic!("Unexpected event type");
862        }
863    }
864
865    #[test]
866    fn test_from_cosmos_delete_worker() {
867        let event = Event::new(
868            "delete-worker",
869            vec![
870                EventAttribute {
871                    index: true,
872                    key: b"worker-id".to_vec(),
873                    value: b"worker1".to_vec(),
874                },
875                EventAttribute {
876                    index: true,
877                    key: b"creator".to_vec(),
878                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
879                },
880            ],
881        );
882
883        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
884
885        assert!(parsed.is_ok());
886        if let Ok(GevulotEvent::Worker(WorkerEvent::Delete(event))) = parsed {
887            assert_eq!(event.block_height, Height::from(1000u32));
888            assert_eq!(event.worker_id, "worker1");
889            assert_eq!(
890                event.creator,
891                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
892            );
893        } else {
894            panic!("Unexpected event type");
895        }
896    }
897
898    #[test]
899    fn test_from_cosmos_announce_worker_exit() {
900        let event = Event::new(
901            "announce-worker-exit",
902            vec![
903                EventAttribute {
904                    index: true,
905                    key: b"worker-id".to_vec(),
906                    value: b"worker1".to_vec(),
907                },
908                EventAttribute {
909                    index: true,
910                    key: b"creator".to_vec(),
911                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
912                },
913            ],
914        );
915
916        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
917
918        assert!(parsed.is_ok());
919        if let Ok(GevulotEvent::Worker(WorkerEvent::AnnounceExit(event))) = parsed {
920            assert_eq!(event.block_height, Height::from(1000u32));
921            assert_eq!(event.worker_id, "worker1");
922            assert_eq!(
923                event.creator,
924                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
925            );
926        } else {
927            panic!("Unexpected event type");
928        }
929    }
930
931    #[test]
932    fn test_from_cosmos_create_task() {
933        let event = Event::new(
934            "create-task",
935            vec![
936                EventAttribute {
937                    index: true,
938                    key: b"task-id".to_vec(),
939                    value: b"task1".to_vec(),
940                },
941                EventAttribute {
942                    index: true,
943                    key: b"creator".to_vec(),
944                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
945                },
946                EventAttribute {
947                    index: true,
948                    key: b"worker-id".to_vec(),
949                    value: b"worker1,worker2".to_vec(),
950                },
951                EventAttribute {
952                    index: true,
953                    key: b"worker-id".to_vec(),
954                    value: b"worker3".to_vec(),
955                },
956            ],
957        );
958
959        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
960
961        assert!(parsed.is_ok());
962        if let Ok(GevulotEvent::Task(TaskEvent::Create(event))) = parsed {
963            assert_eq!(event.block_height, Height::from(1000u32));
964            assert_eq!(event.task_id, "task1");
965            assert_eq!(
966                event.creator,
967                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
968            );
969            assert_eq!(
970                event.assigned_workers,
971                vec!["worker1", "worker2", "worker3"]
972            );
973        } else {
974            panic!("Unexpected event type");
975        }
976    }
977
978    #[test]
979    fn test_from_cosmos_delete_task() {
980        let event = Event::new(
981            "delete-task",
982            vec![
983                EventAttribute {
984                    index: true,
985                    key: b"task-id".to_vec(),
986                    value: b"task1".to_vec(),
987                },
988                EventAttribute {
989                    index: true,
990                    key: b"creator".to_vec(),
991                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
992                },
993            ],
994        );
995
996        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
997
998        assert!(parsed.is_ok());
999        if let Ok(GevulotEvent::Task(TaskEvent::Delete(event))) = parsed {
1000            assert_eq!(event.block_height, Height::from(1000u32));
1001            assert_eq!(event.task_id, "task1");
1002            assert_eq!(
1003                event.creator,
1004                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
1005            );
1006        } else {
1007            panic!("Unexpected event type");
1008        }
1009    }
1010
1011    #[test]
1012    fn test_from_cosmos_finish_task() {
1013        let event = Event::new(
1014            "finish-task",
1015            vec![
1016                EventAttribute {
1017                    index: true,
1018                    key: b"task-id".to_vec(),
1019                    value: b"task1".to_vec(),
1020                },
1021                EventAttribute {
1022                    index: true,
1023                    key: b"creator".to_vec(),
1024                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
1025                },
1026                EventAttribute {
1027                    index: true,
1028                    key: b"worker-id".to_vec(),
1029                    value: b"worker1".to_vec(),
1030                },
1031            ],
1032        );
1033
1034        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
1035
1036        assert!(parsed.is_ok());
1037        if let Ok(GevulotEvent::Task(TaskEvent::Finish(event))) = parsed {
1038            assert_eq!(event.block_height, Height::from(1000u32));
1039            assert_eq!(event.task_id, "task1");
1040            assert_eq!(
1041                event.creator,
1042                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
1043            );
1044            assert_eq!(event.worker_id, "worker1");
1045        } else {
1046            panic!("Unexpected event type");
1047        }
1048    }
1049
1050    #[test]
1051    fn test_from_cosmos_create_workflow() {
1052        let event = Event::new(
1053            "create-workflow",
1054            vec![
1055                EventAttribute {
1056                    index: true,
1057                    key: b"workflow-id".to_vec(),
1058                    value: b"workflow1".to_vec(),
1059                },
1060                EventAttribute {
1061                    index: true,
1062                    key: b"creator".to_vec(),
1063                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
1064                },
1065            ],
1066        );
1067
1068        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
1069
1070        assert!(parsed.is_ok());
1071        if let Ok(GevulotEvent::Workflow(WorkflowEvent::Create(event))) = parsed {
1072            assert_eq!(event.block_height, Height::from(1000u32));
1073            assert_eq!(event.workflow_id, "workflow1");
1074            assert_eq!(
1075                event.creator,
1076                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
1077            );
1078        } else {
1079            panic!("Unexpected event type");
1080        }
1081    }
1082
1083    #[test]
1084    fn test_from_cosmos_delete_workflow() {
1085        let event = Event::new(
1086            "delete-workflow",
1087            vec![
1088                EventAttribute {
1089                    index: true,
1090                    key: b"workflow-id".to_vec(),
1091                    value: b"workflow1".to_vec(),
1092                },
1093                EventAttribute {
1094                    index: true,
1095                    key: b"creator".to_vec(),
1096                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
1097                },
1098            ],
1099        );
1100
1101        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
1102
1103        assert!(parsed.is_ok());
1104        if let Ok(GevulotEvent::Workflow(WorkflowEvent::Delete(event))) = parsed {
1105            assert_eq!(event.block_height, Height::from(1000u32));
1106            assert_eq!(event.workflow_id, "workflow1");
1107            assert_eq!(
1108                event.creator,
1109                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
1110            );
1111        } else {
1112            panic!("Unexpected event type");
1113        }
1114    }
1115
1116    #[test]
1117    fn test_from_cosmos_progress_workflow() {
1118        let event = Event::new(
1119            "progress-workflow",
1120            vec![
1121                EventAttribute {
1122                    index: true,
1123                    key: b"workflow-id".to_vec(),
1124                    value: b"workflow1".to_vec(),
1125                },
1126                EventAttribute {
1127                    index: true,
1128                    key: b"creator".to_vec(),
1129                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
1130                },
1131            ],
1132        );
1133
1134        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
1135
1136        assert!(parsed.is_ok());
1137        if let Ok(GevulotEvent::Workflow(WorkflowEvent::Progress(event))) = parsed {
1138            assert_eq!(event.block_height, Height::from(1000u32));
1139            assert_eq!(event.workflow_id, "workflow1");
1140            assert_eq!(
1141                event.creator,
1142                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
1143            );
1144        } else {
1145            panic!("Unexpected event type");
1146        }
1147    }
1148
1149    #[test]
1150    fn test_from_cosmos_finish_workflow() {
1151        let event = Event::new(
1152            "finish-workflow",
1153            vec![
1154                EventAttribute {
1155                    index: true,
1156                    key: b"workflow-id".to_vec(),
1157                    value: b"workflow1".to_vec(),
1158                },
1159                EventAttribute {
1160                    index: true,
1161                    key: b"creator".to_vec(),
1162                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
1163                },
1164            ],
1165        );
1166
1167        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
1168
1169        assert!(parsed.is_ok());
1170        if let Ok(GevulotEvent::Workflow(WorkflowEvent::Finish(event))) = parsed {
1171            assert_eq!(event.block_height, Height::from(1000u32));
1172            assert_eq!(event.workflow_id, "workflow1");
1173            assert_eq!(
1174                event.creator,
1175                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
1176            );
1177        } else {
1178            panic!("Unexpected event type");
1179        }
1180    }
1181
1182    #[test]
1183    fn test_from_cosmos_accept_task() {
1184        let event = Event::new(
1185            "accept-task",
1186            vec![
1187                EventAttribute {
1188                    index: true,
1189                    key: b"task-id".to_vec(),
1190                    value: b"task1".to_vec(),
1191                },
1192                EventAttribute {
1193                    index: true,
1194                    key: b"worker-id".to_vec(),
1195                    value: b"worker1".to_vec(),
1196                },
1197                EventAttribute {
1198                    index: true,
1199                    key: b"creator".to_vec(),
1200                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
1201                },
1202            ],
1203        );
1204
1205        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
1206
1207        assert!(parsed.is_ok());
1208        if let Ok(GevulotEvent::Task(TaskEvent::Accept(event))) = parsed {
1209            assert_eq!(event.block_height, Height::from(1000u32));
1210            assert_eq!(event.task_id, "task1");
1211            assert_eq!(event.worker_id, "worker1");
1212            assert_eq!(
1213                event.creator,
1214                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
1215            );
1216        } else {
1217            panic!("Unexpected event type");
1218        }
1219    }
1220
1221    #[test]
1222    fn test_from_cosmos_decline_task() {
1223        let event = Event::new(
1224            "decline-task",
1225            vec![
1226                EventAttribute {
1227                    index: true,
1228                    key: b"task-id".to_vec(),
1229                    value: b"task1".to_vec(),
1230                },
1231                EventAttribute {
1232                    index: true,
1233                    key: b"worker-id".to_vec(),
1234                    value: b"worker1".to_vec(),
1235                },
1236                EventAttribute {
1237                    index: true,
1238                    key: b"creator".to_vec(),
1239                    value: b"cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh".to_vec(),
1240                },
1241            ],
1242        );
1243
1244        let parsed = GevulotEvent::from_cosmos(&event, Height::from(1000u32));
1245
1246        assert!(parsed.is_ok());
1247        if let Ok(GevulotEvent::Task(TaskEvent::Decline(event))) = parsed {
1248            assert_eq!(event.block_height, Height::from(1000u32));
1249            assert_eq!(event.task_id, "task1");
1250            assert_eq!(event.worker_id, "worker1");
1251            assert_eq!(
1252                event.creator,
1253                "cosmos1fl48vsnmsdzcv85q5d2q4z5ajdha8yu34mf0eh"
1254            );
1255        } else {
1256            panic!("Unexpected event type");
1257        }
1258    }
1259}