char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
7b01146
main
1# [ path = "common/registry.rs" ] 2mod fixture; 3 4use avril_runtime:: caps::{ Capabilities , Category , Initial }; 5use avril_runtime:: registry::{ PackageStore , ProgressEvent , artifact_key}; 6use fixture:: Registry ; 7use futures:: executor:: block_on; 8use serde_json:: json; 9use std:: sync::{ Arc , Mutex }; 10 11# [ test ] 12fn selects_versions_exports_and_verifies_manifest_sources () { 13let server =Registry :: new (); 14 server. package ( 15"@test/pkg" , 16"1.0.0" , 17& [( "mod.ts" , "export const value = 1;" )], 18& [( "." , "./mod.ts" )], 19); 20 server. package ( 21"@test/pkg" , 22"1.1.0" , 23& [ 24( "mod.ts" , "export const value = 2;" ), 25( "sub.ts" , "export {};" ), 26], 27& [( "." , "./mod.ts" ), ( "./sub" , "./sub.ts" )], 28); 29 server. package ( 30"@test/pkg" , 31"2.0.0" , 32& [( "mod.ts" , "export const value = 3;" )], 33& [( "." , "./mod.ts" )], 34); 35let dir = tempfile:: tempdir (). unwrap (); 36let store =PackageStore :: new ( fixture:: runtime_handle (), server. options ( dir. path ())). unwrap (); 37let module =block_on ( store. resolve ( "jsr:@test/pkg@^1" )). unwrap (); 38assert! ( module. ends_with ( "/@test/pkg/1.1.0/mod.ts" )); 39assert_eq! ( 40block_on ( store. source ( & module)). unwrap (), 41"export const value = 2;" 42); 43assert! ( 44block_on ( store. resolve ( "jsr:@test/pkg@^1/sub" )) 45. unwrap () 46. ends_with ( "/sub.ts" ) 47); 48assert! ( 49block_on ( store. resolve ( "jsr:@test/pkg@^2" )) 50. unwrap () 51. contains ( "/2.0.0/" ) 52); 53assert! ( 54block_on ( store. resolve ( "jsr:@test/pkg@^1/private" )) 55. unwrap_err () 56. to_string () 57. contains ( "no export" ) 58); 59assert! ( block_on ( store. resolve ( "jsr:@test/pkg@latest" )). is_err ()); 60assert! ( block_on ( store. resolve ( "jsr:unscoped@1" )). is_err ()); 61 store. save (). unwrap (); 62let lock = fixture:: read_lock ( dir. path ()); 63assert_eq! ( lock[ "version" ], 1 ); 64assert_eq! ( lock[ "specifiers" ][ "jsr:@test/pkg@^1" ], "1.1.0" ); 65assert_eq! ( lock[ "specifiers" ][ "jsr:@test/pkg@^2" ], "2.0.0" ); 66} 67 68# [ test ] 69fn written_locks_are_plain_fjson_with_a_final_newline () { 70let server =Registry :: new (); 71 server. package ( 72"@test/pkg" , 73"1.0.0" , 74& [( "mod.js" , "export {};" )], 75& [( "." , "./mod.js" )], 76); 77let dir = tempfile:: tempdir (). unwrap (); 78let store =PackageStore :: new ( fixture:: runtime_handle (), server. options ( dir. path ())). unwrap (); 79block_on ( store. resolve ( "jsr:@test/pkg@1" )). unwrap (); 80 store. save (). unwrap (); 81let text = std:: fs:: read_to_string ( dir. path (). join ( "avril.lock" )). unwrap (); 82let lock = fixture:: read_lock ( dir. path ()); 83assert_eq! ( 84 text, 85format! ( "{}\n" , serde_fjson:: stringify ( & lock). unwrap ()) 86); 87} 88 89# [ test ] 90fn ephemeral_stores_resolve_and_save_without_writing_a_lock () { 91let server =Registry :: new (); 92 server. package ( 93"@test/pkg" , 94"1.0.0" , 95& [( "mod.ts" , "export const value = 1;" )], 96& [( "." , "./mod.ts" )], 97); 98 server. package ( 99"@test/pkg" , 100"1.1.0" , 101& [( "mod.ts" , "export const value = 2;" )], 102& [( "." , "./mod.ts" )], 103); 104let dir = tempfile:: tempdir (). unwrap (); 105let mut options = server. options ( dir. path ()); 106 options. ephemeral_lock =true ; 107let store =PackageStore :: new ( fixture:: runtime_handle (), options). unwrap (); 108let module =block_on ( store. resolve ( "jsr:@test/pkg@^1" )). unwrap (); 109assert! ( module. ends_with ( "/@test/pkg/1.1.0/mod.ts" )); 110assert_eq! ( 111block_on ( store. source ( & module)). unwrap (), 112"export const value = 2;" 113); 114 store. save (). unwrap (); 115 store 116. save_graph ( & [ "jsr:@test/pkg@^1" . into ()]. into (), & [ module]. into ()) 117. unwrap (); 118assert! ( !dir. path (). join ( "avril.lock" ). exists ()); 119} 120 121# [ test ] 122fn pinned_versions_are_stable_until_an_explicit_update_and_yanks_are_skipped () { 123let server =Registry :: new (); 124 server. package ( 125"@test/pkg" , 126"1.0.0" , 127& [( "mod.js" , "export {};" )], 128& [( "." , "./mod.js" )], 129); 130let dir = tempfile:: tempdir (). unwrap (); 131let options = server. options ( dir. path ()); 132let store =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 133assert! ( 134block_on ( store. resolve ( "jsr:@test/pkg@^1" )) 135. unwrap () 136. contains ( "/1.0.0/" ) 137); 138 store. save (). unwrap (); 139drop ( store); 140 server. package ( 141"@test/pkg" , 142"1.1.0" , 143& [( "mod.js" , "export {};" )], 144& [( "." , "./mod.js" )], 145); 146 server. package ( 147"@test/pkg" , 148"1.2.0" , 149& [( "mod.js" , "export {};" )], 150& [( "." , "./mod.js" )], 151); 152let mut index: serde_json:: Value = serde_json:: from_slice ( 153 server 154. files 155. lock () 156. unwrap () 157. get ( "/@test/pkg/meta.json" ) 158. unwrap (), 159) 160. unwrap (); 161 index[ "versions" ][ "1.2.0" ] =json! ({ "yanked" : true }); 162 server. files . lock (). unwrap (). insert ( 163"/@test/pkg/meta.json" . into (), 164 serde_json:: to_vec ( & index). unwrap (), 165); 166let store =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 167assert! ( 168block_on ( store. resolve ( "jsr:@test/pkg@^1" )) 169. unwrap () 170. contains ( "/1.0.0/" ) 171); 172drop ( store); 173let mut options = options; 174 options. update =true ; 175let store =PackageStore :: new ( fixture:: runtime_handle (), options). unwrap (); 176assert! ( 177block_on ( store. resolve ( "jsr:@test/pkg@^1" )) 178. unwrap () 179. contains ( "/1.1.0/" ) 180); 181 store. save (). unwrap (); 182} 183 184# [ test ] 185fn concurrent_acquisitions_share_downloads_and_independent_saves_merge () { 186let server =Registry :: new (); 187for namein [ "@test/a" , "@test/b" ] { 188 server. package ( 189 name, 190"1.0.0" , 191& [( "mod.ts" , "export {};" )], 192& [( "." , "./mod.ts" )], 193); 194} 195let dir = tempfile:: tempdir (). unwrap (); 196let options = server. options ( dir. path ()); 197let a =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 198let b =PackageStore :: new ( fixture:: runtime_handle (), options). unwrap (); 199let ( first, second) =block_on ( futures:: future:: join ( 200 a. resolve ( "jsr:@test/a@1" ), 201 a. resolve ( "jsr:@test/a@1" ), 202)); 203assert_eq! ( first. as_ref (). unwrap (), second. as_ref (). unwrap ()); 204let name = first. unwrap (); 205let ( first, second) =block_on ( futures:: future:: join ( a. source ( & name), a. source ( & name))); 206assert_eq! ( first. unwrap (), second. unwrap ()); 207block_on ( b. resolve ( "jsr:@test/b@1" )). unwrap (); 208 a. save (). unwrap (); 209 b. save (). unwrap (); 210let requests = server. requests . lock (). unwrap (); 211for pathin [ 212"/@test/a/meta.json" , 213"/@test/a/1.0.0_meta.json" , 214"/@test/a/1.0.0/mod.ts" , 215] { 216assert_eq! ( 217 requests 218. iter () 219. filter ( |request| request. as_str () == path) 220. count (), 2211 , 222"{requests:?}" 223); 224} 225let lock = fixture:: read_lock ( dir. path ()); 226assert_eq! ( lock[ "specifiers" ]. as_object (). unwrap (). len (), 2 ); 227} 228 229# [ test ] 230fn offline_frozen_and_vendored_runs_need_no_network () { 231let server =Registry :: new (); 232 server. package ( 233"@test/pkg" , 234"1.0.0" , 235& [( "mod.ts" , "export {};" )], 236& [( "." , "./mod.ts" )], 237); 238let dir = tempfile:: tempdir (). unwrap (); 239let mut options = server. options ( dir. path ()); 240let store =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 241let module =block_on ( store. resolve ( "jsr:@test/pkg@1" )). unwrap (); 242block_on ( store. source ( & module)). unwrap (); 243 store. save (). unwrap (); 244let vendor = dir. path (). join ( "vendor" ); 245 store. vendor ( & vendor). unwrap (); 246drop ( store); 247 server. requests . lock (). unwrap (). clear (); 248 options. offline =true ; 249 options. frozen =true ; 250 options. vendor =Some ( vendor); 251 options. cache = dir. path (). join ( "empty-cache" ); 252let store =PackageStore :: new ( fixture:: runtime_handle (), options). unwrap (); 253assert_eq! ( block_on ( store. resolve ( "jsr:@test/pkg@1" )). unwrap (), module); 254assert_eq! ( block_on ( store. source ( & module)). unwrap (), "export {};" ); 255assert! ( 256block_on ( store. resolve ( "jsr:@test/pkg@2" )) 257. unwrap_err () 258. to_string () 259. contains ( "Frozen" ) 260); 261assert! ( server. requests . lock (). unwrap (). is_empty ()); 262} 263 264# [ test ] 265fn corrupted_sources_and_locked_metadata_fail_closed () { 266let server =Registry :: new (); 267 server. package ( 268"@test/pkg" , 269"1.0.0" , 270& [( "mod.ts" , "export {};" )], 271& [( "." , "./mod.ts" )], 272); 273let dir = tempfile:: tempdir (). unwrap (); 274let options = server. options ( dir. path ()); 275let store =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 276let name =block_on ( store. resolve ( "jsr:@test/pkg@1" )). unwrap (); 277block_on ( store. source ( & name)). unwrap (); 278 store. save (). unwrap (); 279drop ( store); 280 std:: fs:: write ( 281 options. cache . join ( "files" ). join ( artifact_key ( & name)), 282"console.log('tampered');" , 283) 284. unwrap (); 285let store =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 286assert! ( 287block_on ( store. source ( & name)) 288. unwrap_err () 289. to_string () 290. contains ( "integrity mismatch" ) 291); 292drop ( store); 293let metadata_url = server. url . join ( "@test/pkg/1.0.0_meta.json" ). unwrap (); 294 std:: fs:: write ( 295 options 296. cache 297. join ( "files" ) 298. join ( artifact_key ( metadata_url. as_str ())), 299"{}" , 300) 301. unwrap (); 302let store =PackageStore :: new ( fixture:: runtime_handle (), options). unwrap (); 303assert! ( 304block_on ( store. resolve ( "jsr:@test/pkg@1" )) 305. unwrap_err () 306. to_string () 307. contains ( "integrity mismatch" ) 308); 309} 310 311# [ test ] 312fn rejects_unexported_or_escaping_files_and_registry_changes () { 313let server =Registry :: new (); 314 server. package ( 315"@test/pkg" , 316"1.0.0" , 317& [( "mod.ts" , "export {};" )], 318& [( "." , "../other.ts" )], 319); 320let dir = tempfile:: tempdir (). unwrap (); 321let options = server. options ( dir. path ()); 322let store =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 323assert! ( block_on ( store. resolve ( "jsr:@test/pkg@1" )). is_err ()); 324assert! ( 325block_on ( 326 store. source ( 327 server 328. url 329. join ( "@test/pkg/1.0.0/private.ts" ) 330. unwrap () 331. as_str () 332) 333) 334. unwrap_err () 335. to_string () 336. contains ( "manifest" ) 337); 338assert! ( store. module_base ( "https://example.com/a.js" ). is_err ()); 339assert! ( 340 store 341. module_base ( & format! ( "{}@test/pkg/1.0.0/mod.ts?query" , server. url )) 342. is_err () 343); 344let mut invalid = options. clone (); 345 invalid. jsr_registry = url:: Url :: parse ( "http://example.com/" ). unwrap (); 346assert! ( 347PackageStore :: new ( fixture:: runtime_handle (), invalid) 348. err () 349. unwrap () 350. to_string () 351. contains ( "HTTPS" ) 352); 353let mut frozen = options; 354 frozen. frozen =true ; 355assert! ( 356PackageStore :: new ( fixture:: runtime_handle (), frozen) 357. err () 358. unwrap () 359. to_string () 360. contains ( "does not exist" ) 361); 362} 363 364# [ test ] 365fn lock_saves_ignore_untouched_stale_entries_but_detect_used_conflicts () { 366let server =Registry :: new (); 367for namein [ "@test/a" , "@test/b" ] { 368 server. package ( 369 name, 370"1.0.0" , 371& [( "mod.js" , "export {};" )], 372& [( "." , "./mod.js" )], 373); 374} 375let dir = tempfile:: tempdir (). unwrap (); 376let options = server. options ( dir. path ()); 377let initial =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 378block_on ( initial. resolve ( "jsr:@test/a@1" )). unwrap (); 379 initial. save (). unwrap (); 380let stale =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 381let used =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 382block_on ( used. resolve ( "jsr:@test/a@1" )). unwrap (); 383 server. package ( 384"@test/a" , 385"1.1.0" , 386& [( "mod.js" , "export {};" )], 387& [( "." , "./mod.js" )], 388); 389let mut update_options = options. clone (); 390 update_options. update =true ; 391let update =PackageStore :: new ( fixture:: runtime_handle (), update_options). unwrap (); 392block_on ( update. resolve ( "jsr:@test/a@1" )). unwrap (); 393 update. save (). unwrap (); 394block_on ( stale. resolve ( "jsr:@test/b@1" )). unwrap (); 395 stale. save (). unwrap (); 396assert! ( 397 used. save () 398. unwrap_err () 399. to_string () 400. contains ( "changed concurrently" ) 401); 402let lock = fixture:: read_lock ( dir. path ()); 403assert_eq! ( lock[ "specifiers" ][ "jsr:@test/a@1" ], "1.1.0" ); 404assert_eq! ( lock[ "specifiers" ][ "jsr:@test/b@1" ], "1.0.0" ); 405} 406 407# [ test ] 408fn relative_registry_imports_cannot_cross_package_or_version_boundaries () { 409let server =Registry :: new (); 410let dir = tempfile:: tempdir (). unwrap (); 411let store =PackageStore :: new ( fixture:: runtime_handle (), server. options ( dir. path ())). unwrap (); 412let referrer = server 413. url 414. join ( "@test/pkg/1.0.0/sub/main.ts" ) 415. unwrap () 416. to_string (); 417assert! ( 418 store 419. resolve_relative ( "../dep.ts" , & referrer) 420. unwrap () 421. ends_with ( "/1.0.0/dep.ts" ) 422); 423for specifierin [ 424"../../2.0.0/mod.ts" , 425"../../../other/1.0.0/mod.ts" , 426"../../../../@other/pkg/1.0.0/mod.ts" , 427"../..%2fsecret.ts" , 428"https://example.com/module.ts" , 429"./dep.ts?query" , 430] { 431assert! ( 432 store. resolve_relative ( specifier, & referrer). is_err (), 433"{specifier}" 434); 435} 436} 437 438# [ cfg ( unix )] 439# [ test ] 440fn atomic_outputs_preserve_existing_permissions_and_reject_special_files () { 441use std:: os:: unix:: fs:: PermissionsExt ; 442let dir = tempfile:: tempdir (). unwrap (); 443let output = dir. path (). join ( "avril.lock" ); 444 std:: fs:: write ( & output, "old" ). unwrap (); 445 std:: fs:: set_permissions ( & output, std:: fs:: Permissions :: from_mode ( 0o640 )). unwrap (); 446 avril_runtime:: registry:: atomic_write ( & output, b"new" ). unwrap (); 447assert_eq! ( 448 std:: fs:: metadata ( & output). unwrap (). permissions (). mode () & 0o777 , 4490o640 450); 451let link = dir. path (). join ( "link" ); 452 std:: os:: unix:: fs:: symlink ( & output, & link). unwrap (); 453assert! ( avril_runtime:: registry:: atomic_write ( & link, b"replaced" ). is_err ()); 454assert_eq! ( std:: fs:: read ( & output). unwrap (), b"new" ); 455use std:: os:: unix:: ffi:: OsStrExt ; 456let fifo = dir. path (). join ( "fifo" ); 457let path = std:: ffi:: CString :: new ( fifo. as_os_str (). as_bytes ()). unwrap (); 458assert_eq! ( unsafe { libc:: mkfifo ( path. as_ptr (), 0o600 ) }, 0 ); 459let server =Registry :: new (); 460let mut options = server. options ( dir. path ()); 461 options. lock = fifo; 462assert! ( 463PackageStore :: new ( fixture:: runtime_handle (), options) 464. err () 465. unwrap () 466. to_string () 467. contains ( "regular" ) 468); 469} 470 471# [ test ] 472fn graph_scoped_saves_ignore_prior_walks_changed_by_other_processes () { 473let server =Registry :: new (); 474for namein [ "@test/a" , "@test/b" ] { 475 server. package ( 476 name, 477"1.0.0" , 478& [( "mod.ts" , "export {};" )], 479& [( "." , "./mod.ts" )], 480); 481} 482let dir = tempfile:: tempdir (). unwrap (); 483let options = server. options ( dir. path ()); 484let store =PackageStore :: new ( fixture:: runtime_handle (), options. clone ()). unwrap (); 485let a =block_on ( store. resolve ( "jsr:@test/a@^1" )). unwrap (); 486 store 487. save_graph ( & [ "jsr:@test/a@^1" . into ()]. into (), & [ a]. into ()) 488. unwrap (); 489 server. package ( 490"@test/a" , 491"1.1.0" , 492& [( "mod.ts" , "export {};" )], 493& [( "." , "./mod.ts" )], 494); 495let mut update_options = options. clone (); 496 update_options. update =true ; 497let update =PackageStore :: new ( fixture:: runtime_handle (), update_options). unwrap (); 498block_on ( update. resolve ( "jsr:@test/a@^1" )). unwrap (); 499 update. save (). unwrap (); 500 store 501. save_graph ( & Default :: default (), & [ "/local/mod.ts" . into ()]. into ()) 502. unwrap (); 503let b =block_on ( store. resolve ( "jsr:@test/b@^1" )). unwrap (); 504 store 505. save_graph ( & [ "jsr:@test/b@^1" . into ()]. into (), & [ b. clone ()]. into ()) 506. unwrap (); 507let lock = fixture:: read_lock ( dir. path ()); 508assert_eq! ( lock[ "specifiers" ][ "jsr:@test/a@^1" ], "1.1.0" ); 509assert_eq! ( lock[ "specifiers" ][ "jsr:@test/b@^1" ], "1.0.0" ); 510 std:: fs:: remove_file ( options. lock ). unwrap (); 511 store 512. save_graph ( & [ "jsr:@test/b@^1" . into ()]. into (), & [ b]. into ()) 513. unwrap (); 514} 515 516# [ test ] 517fn shared_caller_runtime_survives_sibling_drop () { 518let server =Registry :: new (); 519 server. package ( 520"@test/pkg" , 521"1.0.0" , 522& [( "mod.ts" , "export {};" )], 523& [( "." , "./mod.ts" )], 524); 525let dir = tempfile:: tempdir (). unwrap (); 526let options = server. options ( dir. path ()); 527let runtime = tokio:: runtime:: Builder :: new_multi_thread () 528. worker_threads ( 2 ) 529. enable_all () 530. build () 531. unwrap (); 532let handle = runtime. handle (). clone (); 533let first =PackageStore :: new ( handle. clone (), options. clone ()). unwrap (); 534let second =PackageStore :: new ( handle, options). unwrap (); 535drop ( first); 536let module =block_on ( second. resolve ( "jsr:@test/pkg@1" )). unwrap (); 537assert_eq! ( block_on ( second. source ( & module)). unwrap (), "export {};" ); 538} 539 540# [ test ] 541fn source_download_callbacks_cannot_bypass_live_authority () { 542let registry =Registry :: new (); 543 registry. package ( 544"@test/progress" , 545"1.0.0" , 546& [( "mod.ts" , "export {};" )], 547& [( "." , "./mod.ts" )], 548); 549let module =format! ( "{}@test/progress/1.0.0/mod.ts" , registry. url ); 550for jsrin [ false , true ] { 551let directory = tempfile:: tempdir (). unwrap (); 552let mut options = registry. options ( directory. path ()); 553if !jsr{ 554 options. jsr_registry = url:: Url :: parse ( "https://jsr.example/" ). unwrap (); 555} 556let caps =Capabilities :: new ( Initial :: new ( false ), directory. path (). to_owned ()); 557let revoke = caps. clone (); 558let events =Arc :: new ( Mutex :: new ( Vec :: new ())); 559let recorded = events. clone (); 560let store =PackageStore :: new ( fixture:: runtime_handle (), options) 561. unwrap () 562. with_capabilities ( caps) 563. with_progress ( Arc :: new ( move |event|{ 564 recorded. lock (). unwrap (). push ( event. clone ()); 565 revoke 566. pledge ( vec! [( Category :: JsExec , Some ( vec! []), vec! [])]) 567. unwrap (); 568})); 569assert! ( 570block_on ( store. remote_source ( & module)) 571. unwrap_err () 572. to_string () 573. contains ( "PermissionDenied" ) 574); 575assert! ( matches! ( 576 events. lock (). unwrap (). as_slice (), 577[ ProgressEvent :: SourceDownload { ..}] 578)); 579assert! ( registry. requests . lock (). unwrap (). is_empty ()); 580} 581}