This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / knot2 / crates / knot-cobs / tests / cobs.rs
8.2 kB 310 lines
1mod common; 2 3use common::{account, at, did, fixture, grant, home, registration, rename, reopen, rkey, signer}; 4use knot_cob::CobStore; 5use knot_cobs::{ 6 CollaboratorsChange, MembersChange, MembersCob, RegistryChange, RegistryError, Removal, 7 RepoRegistryCob, register_repo, rename_repo, 8}; 9use knot_types::{AccountDid, OwnerDid, RepoDid}; 10 11#[test] 12fn members_roundtrip_and_reload_is_identical() { 13 let (_dir, repo) = fixture(); 14 let key = signer(1); 15 let store = CobStore::new(&repo); 16 17 let created = store 18 .create( 19 &home(), 20 &MembersChange::Add(grant("nel", "nel", 1)), 21 &key, 22 at(1), 23 ) 24 .unwrap(); 25 store 26 .update( 27 &home(), 28 created.object, 29 &MembersChange::Add(grant("olaren", "nel", 2)), 30 &key, 31 at(2), 32 ) 33 .unwrap(); 34 store 35 .update( 36 &home(), 37 created.object, 38 &MembersChange::Add(grant("teq", "nel", 3)), 39 &key, 40 at(3), 41 ) 42 .unwrap(); 43 store 44 .update( 45 &home(), 46 created.object, 47 &MembersChange::Remove(Removal { 48 subject: account("teq"), 49 }), 50 &key, 51 at(4), 52 ) 53 .unwrap(); 54 55 let object = store.get::<MembersCob>(created.object).unwrap(); 56 let state = object.state(); 57 assert!(state.contains(&account("nel"))); 58 assert_eq!( 59 state.get(&account("olaren")).unwrap().added_by, 60 account("nel") 61 ); 62 assert!(!state.contains(&account("teq"))); 63 assert_eq!(state.len(), 2); 64 65 let listed: Vec<&AccountDid> = state.entries().map(|(subject, _)| subject).collect(); 66 assert_eq!(listed, vec![&account("nel"), &account("olaren")]); 67 68 let reopened = reopen(repo); 69 let reloaded = CobStore::new(&reopened) 70 .get::<MembersCob>(created.object) 71 .unwrap(); 72 assert_eq!(state, reloaded.state()); 73 74 let (_dup_dir, dup_repo) = fixture(); 75 let dup_store = CobStore::new(&dup_repo); 76 let dup = dup_store 77 .create( 78 &home(), 79 &MembersChange::Add(grant("nel", "olaren", 7)), 80 &key, 81 at(1), 82 ) 83 .unwrap(); 84 dup_store 85 .update( 86 &home(), 87 dup.object, 88 &MembersChange::Add(grant("nel", "olaren", 7)), 89 &key, 90 at(2), 91 ) 92 .unwrap(); 93 let replayed = dup_store 94 .get::<MembersCob>(dup.object) 95 .unwrap() 96 .into_state(); 97 assert_eq!(replayed.len(), 1, "replaying an identical add is a no-op"); 98 let entry = replayed.get(&account("nel")).unwrap(); 99 assert_eq!(entry.added_by, account("olaren")); 100 assert_eq!(entry.created_at, at(7)); 101 102 let converge = |order: [(&str, &str, i64); 2]| { 103 let (_dir, repo) = fixture(); 104 let store = CobStore::new(&repo); 105 let [first, second] = order; 106 let created = store 107 .create( 108 &home(), 109 &MembersChange::Add(grant(first.0, first.1, first.2)), 110 &key, 111 at(1), 112 ) 113 .unwrap(); 114 store 115 .update( 116 &home(), 117 created.object, 118 &MembersChange::Add(grant(second.0, second.1, second.2)), 119 &key, 120 at(2), 121 ) 122 .unwrap(); 123 store 124 .get::<MembersCob>(created.object) 125 .unwrap() 126 .into_state() 127 }; 128 assert_eq!( 129 converge([("nel", "nel", 10), ("olaren", "nel", 20)]), 130 converge([("olaren", "nel", 20), ("nel", "nel", 10)]), 131 "independent grants converge across write order" 132 ); 133} 134 135#[test] 136fn collaborators_live_in_a_per_repo_cob_namespace() { 137 let (_dir, repo) = fixture(); 138 let key = signer(2); 139 let store = CobStore::new(&repo); 140 store 141 .create( 142 &home(), 143 &CollaboratorsChange::Add(grant("lyna", "nel", 1)), 144 &key, 145 at(1), 146 ) 147 .unwrap(); 148 149 let cob_ref = repo.references().unwrap().into_iter().find(|record| { 150 record 151 .name 152 .as_str() 153 .contains("sh.tangled.repo.collaborator") 154 }); 155 assert!(cob_ref.is_some(), "collaborators live under refs/cobs"); 156 assert!(repo.advertised_refs().unwrap().is_empty()); 157} 158 159#[test] 160fn registry_handler_semantics() { 161 let (_dir, repo) = fixture(); 162 let key = signer(21); 163 let store = CobStore::new(&repo); 164 let nel = did::<OwnerDid>("nel"); 165 166 let created = store 167 .create( 168 &home(), 169 &RegistryChange::Register(registration("nel", "anemone", "squid", 1)), 170 &key, 171 at(1), 172 ) 173 .unwrap(); 174 let object = created.object; 175 176 let clash = register_repo( 177 &store, 178 &home(), 179 object, 180 registration("nel", "anemone", "whelk", 2), 181 &key, 182 at(2), 183 ); 184 assert!( 185 matches!(clash, Err(RegistryError::RkeyTaken { .. })), 186 "register cannot claim the canonical rkey of a live repo under the same owner" 187 ); 188 assert_eq!( 189 register_repo( 190 &store, 191 &home(), 192 object, 193 registration("nel", "anemone", "squid", 2), 194 &key, 195 at(2), 196 ) 197 .unwrap(), 198 None, 199 "re-registering identical owner, rkey, and repo appends nothing" 200 ); 201 202 let renamed = rename_repo( 203 &store, 204 &home(), 205 object, 206 rename("nel", "barnacle", "squid"), 207 &key, 208 at(2), 209 ) 210 .unwrap(); 211 assert!(renamed.is_some(), "real rename appends a change"); 212 let state = store.get::<RepoRegistryCob>(object).unwrap().into_state(); 213 assert_eq!( 214 state.resolve(&nel, &rkey("anemone")), 215 Some(&did::<RepoDid>("squid")), 216 "prior rkey keeps resolving as an alias" 217 ); 218 assert_eq!( 219 state.resolve(&nel, &rkey("barnacle")), 220 Some(&did::<RepoDid>("squid")) 221 ); 222 assert_eq!( 223 state.record_of(&did("squid")).unwrap().rkey, 224 rkey("barnacle") 225 ); 226 227 let redundant = register_repo( 228 &store, 229 &home(), 230 object, 231 registration("nel", "barnacle", "squid", 3), 232 &key, 233 at(3), 234 ) 235 .unwrap(); 236 assert_eq!( 237 redundant, None, 238 "re-register matching canonical owner and rkey appends nothing" 239 ); 240 assert_eq!( 241 store 242 .get::<RepoRegistryCob>(object) 243 .unwrap() 244 .into_state() 245 .resolve(&nel, &rkey("anemone")), 246 Some(&did::<RepoDid>("squid")), 247 "retained alias survives a redundant re-register" 248 ); 249 250 store 251 .update( 252 &home(), 253 object, 254 &RegistryChange::Register(registration("nel", "kelp", "whelk", 4)), 255 &key, 256 at(4), 257 ) 258 .unwrap(); 259 260 let taken = rename_repo( 261 &store, 262 &home(), 263 object, 264 rename("nel", "kelp", "squid"), 265 &key, 266 at(5), 267 ); 268 assert!( 269 matches!(taken, Err(RegistryError::RkeyTaken { .. })), 270 "rename cannot take canonical rkey of another live repo" 271 ); 272 273 let unhosted = rename_repo( 274 &store, 275 &home(), 276 object, 277 rename("nel", "limpet", "conch"), 278 &key, 279 at(6), 280 ); 281 assert!( 282 matches!(unhosted, Err(RegistryError::NotHosted { .. })), 283 "rename of a repo with no registration is refused" 284 ); 285 286 let moved = rename_repo( 287 &store, 288 &home(), 289 object, 290 rename("olaren", "uni", "squid"), 291 &key, 292 at(7), 293 ); 294 assert!( 295 matches!(moved, Err(RegistryError::OwnerMoved { .. })), 296 "rename under an owner the repo no longer belongs to is refused" 297 ); 298 299 assert_eq!( 300 store 301 .get::<RepoRegistryCob>(object) 302 .unwrap() 303 .into_state() 304 .record_of(&did("squid")) 305 .unwrap() 306 .rkey, 307 rkey("barnacle"), 308 "refused renames left the canonical rkey untouched" 309 ); 310}