1use std::collections::{HashMap, HashSet};
4use std::fmt::Debug;
5use std::marker::PhantomData;
6
7use serde::{Deserialize, Serialize};
8use thiserror::Error;
9
10use crate::traits::{AckedGroupMembership, IdentityHandle, OperationId};
11
12#[derive(Clone, Debug, Serialize, Deserialize)]
18pub struct AckedTestDgm<ID, OP> {
19 _marker: PhantomData<(ID, OP)>,
20}
21
22impl<ID, OP> AckedTestDgm<ID, OP>
23where
24 ID: IdentityHandle + Serialize + for<'a> Deserialize<'a>,
25 OP: OperationId + Serialize + for<'a> Deserialize<'a>,
26{
27 pub fn init(my_id: ID) -> State<ID, OP> {
28 State {
29 my_id,
30 members: HashSet::new(),
31 removed_members: HashSet::new(),
32 infos: HashMap::new(),
33 remove_infos: HashMap::new(),
34 adds_by_msg: HashMap::new(),
35 removes_by_msg: HashSet::new(),
36 }
37 }
38}
39
40#[derive(Clone, Debug, Serialize, Deserialize)]
41pub struct State<ID, OP>
42where
43 ID: IdentityHandle,
44 OP: OperationId,
45{
46 my_id: ID,
47 members: HashSet<ID>,
48 removed_members: HashSet<ID>,
49 infos: HashMap<ID, MemberInfo<ID, OP>>,
50 remove_infos: HashMap<OP, RemoveInfo<ID>>,
51 adds_by_msg: HashMap<OP, ID>,
52 removes_by_msg: HashSet<OP>,
53}
54
55impl<ID, OP> AckedGroupMembership<ID, OP> for AckedTestDgm<ID, OP>
56where
57 ID: IdentityHandle + Serialize + for<'a> Deserialize<'a>,
58 OP: OperationId + Serialize + for<'a> Deserialize<'a>,
59{
60 type State = State<ID, OP>;
61
62 type Error = TestAckedGroupError<ID, OP>;
63
64 fn from_welcome(
65 mut y: Self::State,
66 y_welcome: Self::State,
67 ) -> Result<Self::State, Self::Error> {
68 y.members.extend(y_welcome.members);
71 y.removed_members.extend(y_welcome.removed_members);
72 y.infos.extend(y_welcome.infos);
73 y.remove_infos.extend(y_welcome.remove_infos);
74 y.adds_by_msg.extend(y_welcome.adds_by_msg);
75 y.removes_by_msg.extend(y_welcome.removes_by_msg);
76 Ok(y)
77 }
78
79 fn create(my_id: ID, initial_members: &[ID]) -> Result<Self::State, Self::Error> {
80 let mut initial_members = initial_members.to_vec();
81 if !initial_members.contains(&my_id) {
82 initial_members.push(my_id);
83 }
84
85 let mut infos = HashMap::with_capacity(initial_members.len());
86 let mut members = HashSet::with_capacity(initial_members.len());
87
88 for member in &initial_members {
89 infos.insert(*member, MemberInfo::new(*member, None, &initial_members));
90 members.insert(*member);
91 }
92
93 Ok(Self::State {
94 my_id,
95 members,
96 removed_members: HashSet::new(),
97 infos,
98 remove_infos: HashMap::new(),
99 adds_by_msg: HashMap::new(),
100 removes_by_msg: HashSet::new(),
101 })
102 }
103
104 fn add(
107 mut y: Self::State,
108 adder: ID,
109 added: ID,
110 message_id: OP,
111 ) -> Result<Self::State, Self::Error> {
112 let mut added_info = MemberInfo::new(added, Some(adder), &[]);
113 added_info.acks.insert(adder);
114 added_info.acks.insert(added);
115 added_info.acks.insert(y.my_id);
116
117 if y.members.contains(&adder) {
119 y.members.insert(added);
122 y.infos.insert(added, added_info);
123 } else {
124 let actor_info = y
128 .infos
129 .get_mut(&adder)
130 .ok_or(TestAckedGroupError::UnrecognizedMember(adder))?;
131 for remove_message_id in &actor_info.remove_messages {
132 let remove_info = y
133 .remove_infos
134 .get_mut(remove_message_id)
135 .expect("remove_infos values should be consistent with remove_messages");
136 remove_info.removed.insert(added);
137 added_info.remove_messages.push(*remove_message_id);
138 }
139 y.removed_members.insert(added);
140 y.infos.insert(added, added_info);
141 }
142
143 for member in &y.members {
147 let member_info = y
148 .infos
149 .get_mut(member)
150 .expect("infos values should be consistent with members keys");
151 if member_info.acks.contains(&adder) {
152 member_info.acks.insert(added);
153 }
154 }
155
156 for member in &y.removed_members {
157 let member_info = y
158 .infos
159 .get_mut(member)
160 .expect("infos values should be consistent with removed_members keys");
161 if member_info.acks.contains(&adder) {
162 member_info.acks.insert(added);
163 }
164 }
165
166 for message_id in &y.removes_by_msg {
167 let remove_info = y
168 .remove_infos
169 .get_mut(message_id)
170 .expect("remove_infos values should be consistent with removes_by_msg keys");
171 if remove_info.acks.contains(&adder) {
172 remove_info.acks.insert(added);
173 }
174 }
175
176 y.adds_by_msg.insert(message_id, added);
177
178 Ok(y)
179 }
180
181 fn remove(
182 mut y: Self::State,
183 remover: ID,
184 removed: &ID,
185 message_id: OP,
186 ) -> Result<Self::State, Self::Error> {
187 let removed: &[ID] = &[*removed];
188
189 let mut remove_result = Vec::new();
190
191 let mut remove_info = RemoveInfo::new(removed);
192 remove_info.acks.insert(remover);
193 remove_info.acks.insert(y.my_id);
194
195 for removed_member in removed {
197 let has_removed_member = y.members.remove(removed_member);
198 if has_removed_member {
199 let member_info = y
200 .infos
201 .get_mut(removed_member)
202 .expect("infos values should be consistent with members keys");
203 y.removed_members.insert(*removed_member);
204 member_info.remove_messages.push(message_id);
205 remove_result.push(*removed_member);
206 } else if y.removed_members.contains(removed_member) {
207 if let Some(member_info) = y.infos.get_mut(removed_member) {
208 member_info.remove_messages.push(message_id);
210 } else {
211 return Err(TestAckedGroupError::UnrecognizedMember(*removed_member));
212 }
213 } else {
214 return Err(TestAckedGroupError::UnrecognizedMember(*removed_member));
215 }
216 }
217
218 y.removes_by_msg.insert(message_id);
219 y.remove_infos.insert(message_id, remove_info);
220
221 loop {
228 let remove_info = y
229 .remove_infos
230 .get_mut(&message_id)
231 .expect("infos values should be consistent with members keys");
232
233 let mut made_progress = false;
234
235 for member in &y.members {
236 let member_info = y
237 .infos
238 .get_mut(member)
239 .expect("infos values should be consistent with members keys");
240 let contains = member_info
241 .actor
242 .is_some_and(|actor| remove_info.removed.contains(&actor));
243 if contains && !member_info.acks.contains(&remover) {
244 remove_result.push(*member);
245 y.removed_members.insert(*member);
246 member_info.remove_messages.push(message_id);
247 remove_info.removed.insert(*member);
248 made_progress = true;
249 }
250 }
251
252 for removed_member in &remove_result {
253 y.members.remove(removed_member);
254 }
255
256 for member in &y.removed_members {
259 let member_info = y
260 .infos
261 .get_mut(member)
262 .expect("infos values should be consistent with members keys");
263 let contains = member_info
264 .actor
265 .is_some_and(|actor| remove_info.removed.contains(&actor));
266 if contains
267 && member_info.acks.contains(&remover)
268 && member_info.remove_messages.contains(&message_id)
269 {
270 remove_info.removed.insert(*member);
271 made_progress = true;
272 }
273 }
274
275 if !made_progress {
276 break;
277 }
278 }
279
280 Ok(y)
281 }
282
283 fn members_view(y: &Self::State, viewer: &ID) -> Result<HashSet<ID>, Self::Error> {
284 if viewer.eq(&y.my_id) {
285 return Ok(y.members.clone());
286 }
287
288 let mut view = HashSet::new();
289
290 for member in &y.members {
292 let member_info = y
293 .infos
294 .get(member)
295 .expect("infos values should be consistent with members keys");
296 if member_info.acks.contains(viewer) {
297 view.insert(*member);
298 }
299 }
300
301 for member in &y.removed_members {
303 let member_info = y
304 .infos
305 .get(member)
306 .expect("infos values should be consistent with removed_members keys");
307 let any_acked = member_info.remove_messages.iter().any(|message_id| {
308 let remove_info = y
309 .remove_infos
310 .get(message_id)
311 .expect("remove_infos values should be consistent with remove_messages");
312 remove_info.acks.contains(viewer)
313 });
314 if !any_acked {
315 if member_info.acks.contains(viewer) {
324 view.insert(*member);
325 }
326 }
327 }
328
329 Ok(view)
330 }
331
332 fn ack(mut y: Self::State, acker: ID, message_id: OP) -> Result<Self::State, Self::Error> {
333 let added = y.adds_by_msg.get_mut(&message_id);
334 match added {
335 Some(added) => {
336 let member_info = y
337 .infos
338 .get_mut(added)
339 .expect("adds_by_msg values should be consistent with members keys");
340 member_info.acks.insert(acker);
341 }
342 None => {
343 let remove_info = y.remove_infos.get_mut(&message_id);
344 match remove_info {
345 Some(remove_info) => {
346 if !remove_info.acks.insert(acker) {
347 return Err(TestAckedGroupError::AlreadyAcked);
348 }
349
350 if remove_info.removed.contains(&acker) {
351 return Err(TestAckedGroupError::AckingOwnRemoval);
352 }
353 }
354 None => return Err(TestAckedGroupError::UnknownMessage(message_id)),
355 }
356 }
357 }
358
359 Ok(y)
360 }
361
362 fn is_add(y: &Self::State, message_id: OP) -> bool {
363 y.adds_by_msg.contains_key(&message_id)
364 }
365
366 fn is_remove(y: &Self::State, message_id: OP) -> bool {
367 y.removes_by_msg.contains(&message_id)
368 }
369}
370
371#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
372struct MemberInfo<ID, OP>
373where
374 ID: IdentityHandle,
375{
376 pub id: ID,
377
378 pub actor: Option<ID>,
380
381 pub remove_messages: Vec<OP>,
383
384 pub acks: HashSet<ID>,
386}
387
388impl<ID, OP> MemberInfo<ID, OP>
389where
390 ID: IdentityHandle,
391 OP: OperationId,
392{
393 fn new(id: ID, actor: Option<ID>, initial_acks: &[ID]) -> Self {
394 let mut acks = HashSet::with_capacity(initial_acks.len());
395 for ack in initial_acks {
396 acks.insert(*ack);
397 }
398
399 Self {
400 id,
401 actor,
402 remove_messages: Vec::new(),
403 acks,
404 }
405 }
406}
407
408#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
409struct RemoveInfo<ID>
410where
411 ID: IdentityHandle,
412{
413 pub removed: HashSet<ID>,
416
417 pub acks: HashSet<ID>,
419}
420
421impl<ID> RemoveInfo<ID>
422where
423 ID: IdentityHandle,
424{
425 pub fn new(removed_members: &[ID]) -> Self {
426 let mut removed = HashSet::with_capacity(removed_members.len());
427 for member in removed_members {
428 removed.insert(*member);
429 }
430
431 Self {
432 removed,
433 acks: HashSet::new(),
434 }
435 }
436}
437
438#[derive(Debug, Error)]
439pub enum TestAckedGroupError<ID, OP> {
440 #[error("tried to access unrecognized member")]
441 UnrecognizedMember(ID),
442
443 #[error("already acked")]
444 AlreadyAcked,
445
446 #[error("member acking their own removal")]
447 AckingOwnRemoval,
448
449 #[error("message not recognized")]
450 UnknownMessage(OP),
451}
452
453#[cfg(test)]
454mod tests {
455 use std::collections::HashSet;
456
457 use crate::test_utils::MessageId;
458 use crate::traits::AckedGroupMembership;
459
460 use super::AckedTestDgm;
461
462 #[test]
463 fn concurrent_operations() {
464 let alice = 0;
465 let bob = 1;
466 let charlie = 2;
467 let daphne = 3;
468
469 let charlie_y = AckedTestDgm::create(charlie, &[charlie]).unwrap();
472
473 let charlie_y = AckedTestDgm::add(
476 charlie_y,
477 charlie,
478 alice,
479 MessageId {
480 sender: charlie,
481 seq: 1,
482 },
483 )
484 .unwrap();
485
486 let alice_y = AckedTestDgm::init(alice);
489 let alice_y = AckedTestDgm::from_welcome(alice_y, charlie_y.clone()).unwrap();
490
491 let charlie_y = AckedTestDgm::ack(
494 charlie_y,
495 alice,
496 MessageId {
497 sender: charlie,
498 seq: 1,
499 },
500 )
501 .unwrap();
502
503 for id in [alice, charlie] {
506 assert_eq!(
507 AckedTestDgm::members_view(&charlie_y, &id).unwrap(),
508 HashSet::from([alice, charlie])
509 );
510
511 assert_eq!(
512 AckedTestDgm::members_view(&alice_y, &id).unwrap(),
513 HashSet::from([alice, charlie])
514 );
515 }
516
517 let charlie_y = AckedTestDgm::add(
522 charlie_y,
523 charlie,
524 daphne,
525 MessageId {
526 sender: charlie,
527 seq: 2,
528 },
529 )
530 .unwrap();
531
532 assert_eq!(
533 AckedTestDgm::members_view(&charlie_y, &charlie).unwrap(),
534 HashSet::from([alice, charlie, daphne])
535 );
536
537 let daphne_y = AckedTestDgm::init(daphne);
540 let daphne_y = AckedTestDgm::from_welcome(daphne_y, charlie_y.clone()).unwrap();
541
542 assert_eq!(
543 AckedTestDgm::members_view(&daphne_y, &daphne).unwrap(),
544 HashSet::from([alice, charlie, daphne])
545 );
546
547 let alice_y = AckedTestDgm::add(
550 alice_y,
551 charlie,
552 daphne,
553 MessageId {
554 sender: charlie,
555 seq: 2,
556 },
557 )
558 .unwrap();
559
560 assert_eq!(
561 AckedTestDgm::members_view(&alice_y, &alice).unwrap(),
562 HashSet::from([alice, charlie, daphne])
563 );
564
565 let charlie_y = AckedTestDgm::ack(
568 charlie_y,
569 daphne,
570 MessageId {
571 sender: charlie,
572 seq: 2,
573 },
574 )
575 .unwrap();
576 let alice_y = AckedTestDgm::ack(
577 alice_y,
578 daphne,
579 MessageId {
580 sender: charlie,
581 seq: 2,
582 },
583 )
584 .unwrap();
585 let charlie_y = AckedTestDgm::ack(
586 charlie_y,
587 alice,
588 MessageId {
589 sender: charlie,
590 seq: 2,
591 },
592 )
593 .unwrap();
594 let daphne_y = AckedTestDgm::ack(
595 daphne_y,
596 alice,
597 MessageId {
598 sender: charlie,
599 seq: 2,
600 },
601 )
602 .unwrap();
603
604 for id in [alice, charlie, daphne] {
607 assert_eq!(
608 AckedTestDgm::members_view(&alice_y, &id).unwrap(),
609 HashSet::from([alice, charlie, daphne])
610 );
611
612 assert_eq!(
613 AckedTestDgm::members_view(&charlie_y, &id).unwrap(),
614 HashSet::from([alice, charlie, daphne])
615 );
616
617 assert_eq!(
618 AckedTestDgm::members_view(&daphne_y, &id).unwrap(),
619 HashSet::from([alice, charlie, daphne])
620 );
621 }
622
623 let alice_y = AckedTestDgm::remove(
628 alice_y,
629 alice,
630 &charlie,
631 MessageId {
632 sender: alice,
633 seq: 2,
634 },
635 )
636 .unwrap();
637
638 assert_eq!(
639 AckedTestDgm::members_view(&alice_y, &alice).unwrap(),
640 HashSet::from([alice, daphne])
641 );
642
643 let charlie_y = AckedTestDgm::add(
646 charlie_y,
647 charlie,
648 bob,
649 MessageId {
650 sender: charlie,
651 seq: 3,
652 },
653 )
654 .unwrap();
655
656 for id in [bob, charlie] {
657 assert_eq!(
658 AckedTestDgm::members_view(&charlie_y, &id).unwrap(),
659 HashSet::from([alice, bob, charlie, daphne])
660 );
661 }
662
663 for id in [alice, daphne] {
664 assert_eq!(
665 AckedTestDgm::members_view(&charlie_y, &id).unwrap(),
666 HashSet::from([alice, charlie, daphne])
667 );
668 }
669
670 let bob_y = AckedTestDgm::init(bob);
673 let bob_y = AckedTestDgm::from_welcome(bob_y, charlie_y.clone()).unwrap();
674
675 for id in [bob, charlie] {
676 assert_eq!(
677 AckedTestDgm::members_view(&bob_y, &id).unwrap(),
678 HashSet::from([alice, bob, charlie, daphne])
679 );
680 }
681
682 let bob_y = AckedTestDgm::remove(
685 bob_y,
686 alice,
687 &charlie,
688 MessageId {
689 sender: alice,
690 seq: 2,
691 },
692 )
693 .unwrap();
694
695 let charlie_y = AckedTestDgm::remove(
696 charlie_y,
697 alice,
698 &charlie,
699 MessageId {
700 sender: alice,
701 seq: 2,
702 },
703 )
704 .unwrap();
705
706 let daphne_y = AckedTestDgm::remove(
707 daphne_y,
708 alice,
709 &charlie,
710 MessageId {
711 sender: alice,
712 seq: 2,
713 },
714 )
715 .unwrap();
716
717 assert_eq!(
718 AckedTestDgm::members_view(&bob_y, &bob).unwrap(),
719 HashSet::from([alice, daphne])
720 );
721
722 assert_eq!(
723 AckedTestDgm::members_view(&charlie_y, &charlie).unwrap(),
724 HashSet::from([alice, daphne])
725 );
726
727 assert_eq!(
728 AckedTestDgm::members_view(&daphne_y, &daphne).unwrap(),
729 HashSet::from([alice, daphne])
730 );
731
732 let alice_y = AckedTestDgm::add(
735 alice_y,
736 charlie,
737 bob,
738 MessageId {
739 sender: charlie,
740 seq: 3,
741 },
742 )
743 .unwrap();
744
745 let daphne_y = AckedTestDgm::add(
746 daphne_y,
747 charlie,
748 bob,
749 MessageId {
750 sender: charlie,
751 seq: 3,
752 },
753 )
754 .unwrap();
755
756 assert_eq!(
760 AckedTestDgm::members_view(&alice_y, &alice).unwrap(),
761 HashSet::from([alice, daphne])
762 );
763
764 assert_eq!(
765 AckedTestDgm::members_view(&charlie_y, &charlie).unwrap(),
766 HashSet::from([alice, daphne])
767 );
768
769 assert_eq!(
770 AckedTestDgm::members_view(&daphne_y, &daphne).unwrap(),
771 HashSet::from([alice, daphne])
772 );
773
774 assert_eq!(
778 AckedTestDgm::members_view(&alice_y, &daphne).unwrap(),
779 HashSet::from([alice, charlie, daphne])
780 );
781
782 assert_eq!(
783 AckedTestDgm::members_view(&bob_y, &alice).unwrap(),
784 HashSet::from([alice, daphne])
785 );
786
787 assert_eq!(
788 AckedTestDgm::members_view(&bob_y, &charlie).unwrap(),
789 HashSet::from([alice, bob, charlie, daphne])
790 );
791
792 assert_eq!(
793 AckedTestDgm::members_view(&bob_y, &daphne).unwrap(),
794 HashSet::from([alice, charlie, daphne])
795 );
796
797 assert_eq!(
798 AckedTestDgm::members_view(&charlie_y, &daphne).unwrap(),
799 HashSet::from([alice, charlie, daphne])
800 );
801 }
802
803 #[test]
804 fn members_view() {
805 let alice = 0;
806 let bob = 1;
807 let charlie = 2;
808
809 let alice_y = AckedTestDgm::create(alice, &[alice, charlie]).unwrap();
812
813 let charlie_y = AckedTestDgm::init(charlie);
814 let charlie_y = AckedTestDgm::from_welcome(charlie_y, alice_y.clone()).unwrap();
815
816 let alice_y = AckedTestDgm::add(
819 alice_y,
820 alice,
821 bob,
822 MessageId {
823 sender: alice,
824 seq: 1,
825 },
826 )
827 .unwrap();
828
829 let bob_y = AckedTestDgm::init(bob);
830 let bob_y = AckedTestDgm::from_welcome(bob_y, alice_y.clone()).unwrap();
831
832 let alice_y = AckedTestDgm::ack(
835 alice_y,
836 bob,
837 MessageId {
838 sender: alice,
839 seq: 1,
840 },
841 )
842 .unwrap();
843
844 assert_eq!(
847 AckedTestDgm::members_view(&alice_y, &alice).unwrap(),
848 HashSet::from([alice, bob, charlie])
849 );
850
851 assert_eq!(
852 AckedTestDgm::members_view(&alice_y, &bob).unwrap(),
853 HashSet::from([alice, bob, charlie])
854 );
855
856 assert_eq!(
857 AckedTestDgm::members_view(&bob_y, &alice).unwrap(),
858 HashSet::from([alice, bob, charlie])
859 );
860
861 assert_eq!(
862 AckedTestDgm::members_view(&bob_y, &bob).unwrap(),
863 HashSet::from([alice, bob, charlie])
864 );
865
866 assert_eq!(
869 AckedTestDgm::members_view(&alice_y, &charlie).unwrap(),
870 HashSet::from([alice, charlie])
871 );
872
873 assert_eq!(
874 AckedTestDgm::members_view(&bob_y, &charlie).unwrap(),
875 HashSet::from([alice, charlie])
876 );
877
878 assert_eq!(
879 AckedTestDgm::members_view(&charlie_y, &charlie).unwrap(),
880 HashSet::from([alice, charlie])
881 );
882
883 let charlie_y = AckedTestDgm::add(
886 charlie_y,
887 alice,
888 bob,
889 MessageId {
890 sender: alice,
891 seq: 1,
892 },
893 )
894 .unwrap();
895
896 let alice_y = AckedTestDgm::ack(
897 alice_y,
898 charlie,
899 MessageId {
900 sender: alice,
901 seq: 1,
902 },
903 )
904 .unwrap();
905
906 let bob_y = AckedTestDgm::ack(
907 bob_y,
908 charlie,
909 MessageId {
910 sender: alice,
911 seq: 1,
912 },
913 )
914 .unwrap();
915
916 for id in [alice, bob, charlie] {
919 assert_eq!(
920 AckedTestDgm::members_view(&alice_y, &id).unwrap(),
921 HashSet::from([alice, bob, charlie])
922 );
923
924 assert_eq!(
925 AckedTestDgm::members_view(&bob_y, &id).unwrap(),
926 HashSet::from([alice, bob, charlie])
927 );
928
929 assert_eq!(
930 AckedTestDgm::members_view(&charlie_y, &id).unwrap(),
931 HashSet::from([alice, bob, charlie])
932 );
933 }
934
935 let charlie_y = AckedTestDgm::remove(
938 charlie_y,
939 charlie,
940 &bob,
941 MessageId {
942 sender: charlie,
943 seq: 2,
944 },
945 )
946 .unwrap();
947
948 let alice_y = AckedTestDgm::remove(
951 alice_y,
952 charlie,
953 &bob,
954 MessageId {
955 sender: charlie,
956 seq: 2,
957 },
958 )
959 .unwrap();
960
961 let bob_y = AckedTestDgm::remove(
962 bob_y,
963 charlie,
964 &bob,
965 MessageId {
966 sender: charlie,
967 seq: 2,
968 },
969 )
970 .unwrap();
971
972 assert_eq!(
976 AckedTestDgm::members_view(&charlie_y, &charlie).unwrap(),
977 HashSet::from([alice, charlie])
978 );
979
980 for id in [alice, charlie] {
981 assert_eq!(
982 AckedTestDgm::members_view(&alice_y, &id).unwrap(),
983 HashSet::from([alice, charlie])
984 );
985 }
986
987 for id in [bob, charlie] {
988 assert_eq!(
989 AckedTestDgm::members_view(&bob_y, &id).unwrap(),
990 HashSet::from([alice, charlie])
991 );
992 }
993
994 assert_eq!(
998 AckedTestDgm::members_view(&alice_y, &bob).unwrap(),
999 HashSet::from([alice, bob, charlie]),
1000 );
1001
1002 assert_eq!(
1003 AckedTestDgm::members_view(&bob_y, &alice).unwrap(),
1004 HashSet::from([alice, bob, charlie]),
1005 );
1006
1007 for id in [alice, bob] {
1008 assert_eq!(
1009 AckedTestDgm::members_view(&charlie_y, &id).unwrap(),
1010 HashSet::from([alice, bob, charlie]),
1011 "invalid members view from 2's perspective for {id}",
1012 );
1013 }
1014 }
1015
1016 #[test]
1017 fn strong_removal() {
1018 let alice = 0;
1019 let bob = 1;
1020 let charlie = 2;
1021 let daphne = 3;
1022
1023 let alice_y = AckedTestDgm::create(alice, &[alice]).unwrap();
1026
1027 let alice_y = AckedTestDgm::add(
1030 alice_y,
1031 alice,
1032 bob,
1033 MessageId {
1034 sender: alice,
1035 seq: 0,
1036 },
1037 )
1038 .unwrap();
1039
1040 let bob_y = AckedTestDgm::init(bob);
1041 let bob_y = AckedTestDgm::from_welcome(bob_y, alice_y.clone()).unwrap();
1042
1043 let alice_y = AckedTestDgm::remove(
1046 alice_y,
1047 alice,
1048 &bob,
1049 MessageId {
1050 sender: alice,
1051 seq: 0,
1052 },
1053 )
1054 .unwrap();
1055
1056 let bob_y = AckedTestDgm::add(
1059 bob_y,
1060 bob,
1061 charlie,
1062 MessageId {
1063 sender: bob,
1064 seq: 0,
1065 },
1066 )
1067 .unwrap();
1068
1069 let bob_y = AckedTestDgm::add(
1070 bob_y,
1071 bob,
1072 daphne,
1073 MessageId {
1074 sender: bob,
1075 seq: 1,
1076 },
1077 )
1078 .unwrap();
1079
1080 let alice_y = AckedTestDgm::add(
1083 alice_y,
1084 bob,
1085 charlie,
1086 MessageId {
1087 sender: bob,
1088 seq: 0,
1089 },
1090 )
1091 .unwrap();
1092
1093 let alice_y = AckedTestDgm::add(
1094 alice_y,
1095 bob,
1096 daphne,
1097 MessageId {
1098 sender: bob,
1099 seq: 1,
1100 },
1101 )
1102 .unwrap();
1103
1104 let bob_y = AckedTestDgm::remove(
1107 bob_y,
1108 alice,
1109 &bob,
1110 MessageId {
1111 sender: alice,
1112 seq: 1,
1113 },
1114 )
1115 .unwrap();
1116
1117 assert_eq!(
1118 AckedTestDgm::members_view(&alice_y, &alice).unwrap(),
1119 AckedTestDgm::members_view(&bob_y, &bob).unwrap(),
1120 );
1121 assert!(
1122 !AckedTestDgm::members_view(&alice_y, &alice)
1123 .unwrap()
1124 .contains(&bob)
1125 );
1126 assert!(
1127 !AckedTestDgm::members_view(&alice_y, &alice)
1128 .unwrap()
1129 .contains(&charlie)
1130 );
1131 assert!(
1132 !AckedTestDgm::members_view(&alice_y, &alice)
1133 .unwrap()
1134 .contains(&daphne)
1135 );
1136 }
1137}