authored_draft.rs (4476B)
1 use futures_executor::block_on; 2 use radroots_storage::{ 3 Error, 4 authored_draft::{ 5 AUTHORED_DRAFT_QUERY_LIMIT_MAX, AuthoredDraft, AuthoredDraftId, AuthoredDraftRevision, 6 AuthoredDraftStage, AuthoredDraftStore, DraftAppendDisposition, 7 }, 8 memory::MemoryStorage, 9 }; 10 11 fn draft(id: u8, author: u8, created_at_unix_ms: u64) -> AuthoredDraft { 12 AuthoredDraft::initial( 13 AuthoredDraftId::new([id; 16]).expect("draft ID"), 14 [author; 32], 15 "radroots.phase1-draft.v1", 16 vec![id], 17 AuthoredDraftStage::Draft, 18 None, 19 created_at_unix_ms, 20 ) 21 .expect("draft") 22 } 23 24 #[test] 25 fn memory_draft_store_replays_conflicts_and_queries_exact_heads() { 26 fn accepts_dyn(_: &dyn AuthoredDraftStore) {} 27 28 let store = MemoryStorage::default(); 29 accepts_dyn(&store); 30 let first = draft(1, 9, 10); 31 assert_eq!( 32 block_on(store.append_authored_draft(first.clone(), None)) 33 .expect("insert") 34 .disposition(), 35 DraftAppendDisposition::Inserted 36 ); 37 let replay = block_on(store.append_authored_draft(first.clone(), None)).expect("replay"); 38 assert_eq!(replay.disposition(), DraftAppendDisposition::Replay); 39 assert_eq!(replay.draft(), &first); 40 41 let conflicting = AuthoredDraft::initial( 42 first.draft_id(), 43 *first.author(), 44 first.payload_schema(), 45 b"conflict".to_vec(), 46 AuthoredDraftStage::Draft, 47 None, 48 first.created_at_unix_ms(), 49 ) 50 .expect("conflicting revision"); 51 assert_eq!( 52 block_on(store.append_authored_draft(conflicting, None)), 53 Err(Error::DraftRevisionConflict) 54 ); 55 56 let second = first 57 .successor( 58 b"second".to_vec(), 59 AuthoredDraftStage::MediaPreparing, 60 None, 61 11, 62 ) 63 .expect("successor"); 64 assert_eq!( 65 block_on(store.append_authored_draft(second.clone(), None)), 66 Err(Error::DraftRevisionConflict) 67 ); 68 assert_eq!( 69 block_on(store.append_authored_draft( 70 second.clone(), 71 Some(AuthoredDraftRevision::new(2).expect("revision")), 72 )), 73 Err(Error::DraftRevisionConflict) 74 ); 75 block_on(store.append_authored_draft(second.clone(), Some(AuthoredDraftRevision::INITIAL))) 76 .expect("successor insert"); 77 78 assert_eq!( 79 block_on(store.authored_draft_head(first.draft_id())) 80 .expect("head") 81 .expect("stored head"), 82 second 83 ); 84 assert_eq!( 85 block_on(store.authored_draft_revision(first.draft_id(), first.revision())) 86 .expect("revision") 87 .expect("stored revision"), 88 first 89 ); 90 assert!( 91 block_on(store.authored_draft_revision( 92 AuthoredDraftId::new([8; 16]).expect("missing ID"), 93 AuthoredDraftRevision::INITIAL, 94 )) 95 .expect("missing revision query") 96 .is_none() 97 ); 98 } 99 100 #[test] 101 fn memory_draft_head_query_is_bounded_filtered_sorted_and_truncated() { 102 let store = MemoryStorage::default(); 103 let first = draft(1, 9, 10); 104 let second = draft(2, 9, 20); 105 let other_author = draft(3, 8, 30); 106 for value in [&first, &second, &other_author] { 107 block_on(store.append_authored_draft(value.clone(), None)).expect("insert"); 108 } 109 let first_head = first 110 .successor( 111 b"new head".to_vec(), 112 AuthoredDraftStage::MediaPreparing, 113 None, 114 40, 115 ) 116 .expect("new head"); 117 block_on(store.append_authored_draft(first_head.clone(), Some(first.revision()))) 118 .expect("head insert"); 119 120 let heads = block_on(store.authored_draft_heads([9; 32], 2)).expect("heads"); 121 assert_eq!(heads, vec![first_head.clone(), second]); 122 assert_eq!( 123 block_on(store.authored_draft_heads([9; 32], 1)).expect("truncated heads"), 124 vec![first_head] 125 ); 126 assert!( 127 block_on(store.authored_draft_heads([7; 32], 2)) 128 .expect("unmatched author") 129 .is_empty() 130 ); 131 assert_eq!( 132 block_on(store.authored_draft_heads([0; 32], 1)), 133 Err(Error::InvalidAuthoredDraft) 134 ); 135 assert_eq!( 136 block_on(store.authored_draft_heads([9; 32], 0)), 137 Err(Error::InvalidAuthoredDraft) 138 ); 139 assert_eq!( 140 block_on(store.authored_draft_heads([9; 32], AUTHORED_DRAFT_QUERY_LIMIT_MAX + 1,)), 141 Err(Error::InvalidAuthoredDraft) 142 ); 143 }