authored_draft_all_schemas.rs (7985B)
1 use futures_executor::block_on; 2 use radroots_storage::{ 3 authored_draft::{AuthoredDraft, AuthoredDraftId, AuthoredDraftStage, AuthoredDraftStore}, 4 authored_draft_query::{ 5 AuthoredDraftCursor, AuthoredDraftPage, AuthoredDraftQuery, AuthoredDraftQueryRecord, 6 AuthoredDraftScope, 7 }, 8 memory::MemoryStorage, 9 }; 10 11 fn draft(id: u128, author: u8, schema: &str, payload: Vec<u8>) -> AuthoredDraft { 12 let value = AuthoredDraft::initial( 13 AuthoredDraftId::new(id.to_be_bytes()).unwrap(), 14 [author; 32], 15 schema, 16 payload, 17 AuthoredDraftStage::Draft, 18 None, 19 10, 20 ) 21 .unwrap(); 22 if id.is_multiple_of(2) { 23 value 24 .with_scope(AuthoredDraftScope::new([3; 32]).unwrap()) 25 .unwrap() 26 } else { 27 value 28 } 29 } 30 31 #[test] 32 fn all_schema_cursor_has_explicit_independent_authority_and_no_wildcard_schema() { 33 let q = AuthoredDraftQuery::for_author_all_schemas([7; 32], 37).unwrap(); 34 assert!(q.is_author_wide()); 35 assert_eq!(q.scope(), None); 36 assert_eq!(q.payload_schema(), None); 37 for (author, limit) in [([0; 32], 1), ([7; 32], 0), ([7; 32], 257)] { 38 assert!(AuthoredDraftQuery::for_author_all_schemas(author, limit).is_err()); 39 } 40 let cursor = q.cursor_after([0; 16]); 41 let encoded = serde_json::to_string(&cursor).unwrap(); 42 let expected = format!( 43 "{{\"schema_version\":3,\"author\":{},\"payload_schema\":null,\"scope\":null,\"after_id\":{},\"selection\":\"author_all_schemas\"}}", 44 serde_json::to_string(&[7; 32]).unwrap(), 45 serde_json::to_string(&[0; 16]).unwrap() 46 ); 47 assert_eq!(encoded, expected); 48 let decoded: AuthoredDraftCursor = serde_json::from_str(&encoded).unwrap(); 49 assert_eq!(decoded, cursor); 50 assert_eq!( 51 q.clone().with_cursor(&decoded).unwrap().after(), 52 Some([0; 16]) 53 ); 54 assert_eq!(serde_json::to_string(&decoded).unwrap(), encoded); 55 for other in [ 56 AuthoredDraftQuery::new([7; 32], "known.v1", None, 1).unwrap(), 57 AuthoredDraftQuery::for_author([7; 32], "known.v1", 1).unwrap(), 58 AuthoredDraftQuery::for_author_all_schemas([8; 32], 1).unwrap(), 59 ] { 60 assert!(q.clone().with_cursor(&other.cursor_after([0; 16])).is_err()); 61 assert!(other.with_cursor(&cursor).is_err()); 62 } 63 let wire = serde_json::to_value(&cursor).unwrap(); 64 for (field, value) in [ 65 ("schema_version", serde_json::json!(1)), 66 ("schema_version", serde_json::json!(2)), 67 ("schema_version", serde_json::json!(4)), 68 ("selection", serde_json::json!("author_schema")), 69 ("selection", serde_json::Value::Null), 70 ("selection", serde_json::json!("future")), 71 ("payload_schema", serde_json::json!("known.v1")), 72 ("payload_schema", serde_json::json!("")), 73 ("scope", serde_json::json!([3; 32].to_vec())), 74 ("author", serde_json::json!([0; 32].to_vec())), 75 ("after_id", serde_json::json!([0; 15].to_vec())), 76 ("unexpected", serde_json::json!(true)), 77 ] { 78 let mut changed = wire.clone(); 79 changed[field] = value; 80 assert!( 81 serde_json::from_value::<AuthoredDraftCursor>(changed).is_err(), 82 "{field}" 83 ); 84 } 85 for field in [ 86 "schema_version", 87 "author", 88 "payload_schema", 89 "scope", 90 "after_id", 91 "selection", 92 ] { 93 let mut changed = wire.clone(); 94 changed.as_object_mut().unwrap().remove(field); 95 assert!( 96 serde_json::from_value::<AuthoredDraftCursor>(changed).is_err(), 97 "missing {field}" 98 ); 99 } 100 let duplicate = encoded.replace("\"scope\":null", "\"scope\":null,\"scope\":null"); 101 assert!(serde_json::from_str::<AuthoredDraftCursor>(&duplicate).is_err()); 102 let exact = AuthoredDraftQuery::for_author([7; 32], "known.v1", 1).unwrap(); 103 let mut wrong_marker = serde_json::to_value(exact.cursor_after([0; 16])).unwrap(); 104 wrong_marker["selection"] = serde_json::json!("author_all_schemas"); 105 assert!(serde_json::from_value::<AuthoredDraftCursor>(wrong_marker).is_err()); 106 assert_eq!(exact.payload_schema(), Some("known.v1")); 107 assert!(AuthoredDraftQuery::new([7; 32], "", None, 1).is_err()); 108 } 109 110 #[test] 111 fn all_schema_memory_inventory_covers_unknown_schemas_and_scopes_beyond_one_thousand() { 112 let store = MemoryStorage::default(); 113 for id in (1..=1001).rev() { 114 let schema = if id % 3 == 0 { 115 "future.unknown.v999" 116 } else { 117 "known.v1" 118 }; 119 block_on(store.append_authored_draft(draft(id, 7, schema, vec![1]), None)).unwrap(); 120 } 121 block_on(store.append_authored_draft(draft(1002, 8, "future.unknown.v999", vec![1]), None)) 122 .unwrap(); 123 let q = AuthoredDraftQuery::for_author_all_schemas([7; 32], 37).unwrap(); 124 let mut next = None; 125 let mut found = Vec::new(); 126 loop { 127 let query = next.as_ref().map_or_else( 128 || q.clone(), 129 |cursor| q.clone().with_cursor(cursor).unwrap(), 130 ); 131 let page = block_on(store.query_authored_drafts(query)).unwrap(); 132 assert!(page.records().len() <= 37); 133 for record in page.records() { 134 let AuthoredDraftQueryRecord::Draft(value) = record else { 135 panic!("valid fixture") 136 }; 137 let id = u128::from_be_bytes(record.draft_key()); 138 found.push(id); 139 assert_eq!( 140 value.payload_schema(), 141 if id % 3 == 0 { 142 "future.unknown.v999" 143 } else { 144 "known.v1" 145 } 146 ); 147 if id == 1001 { 148 assert_eq!(value.revision().get(), 2); 149 } 150 } 151 next = page.next_cursor().cloned(); 152 if found.len() == 37 { 153 for id in [1_u128, 1001] { 154 let value = block_on( 155 store.authored_draft_head(AuthoredDraftId::new(id.to_be_bytes()).unwrap()), 156 ) 157 .unwrap() 158 .unwrap(); 159 let revised = value 160 .successor(vec![2], AuthoredDraftStage::Draft, None, 11) 161 .unwrap(); 162 block_on(store.append_authored_draft(revised, Some(value.revision()))).unwrap(); 163 } 164 } 165 if next.is_none() { 166 break; 167 } 168 } 169 assert_eq!(found, (1..=1001).collect::<Vec<_>>()); 170 let fresh = block_on(store.query_authored_drafts(q)).unwrap(); 171 assert_eq!(fresh.records()[0].revision().get(), 2); 172 } 173 174 #[test] 175 fn all_schema_memory_pages_keep_payload_bounds_and_validate_authority() { 176 let store = MemoryStorage::default(); 177 let first = draft(1, 7, "known.v1", vec![1; 3 * 1024 * 1024]); 178 let second = draft(2, 7, "future.v999", vec![2; 3 * 1024 * 1024]); 179 for value in [first.clone(), second.clone()] { 180 block_on(store.append_authored_draft(value, None)).unwrap(); 181 } 182 let q = AuthoredDraftQuery::for_author_all_schemas([7; 32], 256).unwrap(); 183 let page = block_on(store.query_authored_drafts(q.clone())).unwrap(); 184 assert_eq!( 185 page.records(), 186 [AuthoredDraftQueryRecord::Draft(first.clone())] 187 ); 188 let next = block_on( 189 store.query_authored_drafts(q.clone().with_cursor(page.next_cursor().unwrap()).unwrap()), 190 ) 191 .unwrap(); 192 assert_eq!( 193 next.records(), 194 [AuthoredDraftQueryRecord::Draft(second.clone())] 195 ); 196 assert!(next.next_cursor().is_none()); 197 assert!( 198 AuthoredDraftPage::new( 199 &q, 200 vec![ 201 AuthoredDraftQueryRecord::Draft(first), 202 AuthoredDraftQueryRecord::Draft(second) 203 ], 204 false 205 ) 206 .is_err() 207 ); 208 let foreign = AuthoredDraftQueryRecord::Draft(draft(3, 8, "known.v1", vec![1])); 209 assert!(AuthoredDraftPage::new(&q, vec![foreign], false).is_err()); 210 }