char/avril
(wip) why'd you have to go and make things so complicaaaaated?
git clone https://git.t4t.associates/char/avril
688e96e
main
1# [ path = "common/http.rs" ] 2mod http_fixture; 3# [ path = "common/registry.rs" ] 4mod registry_fixture; 5 6use http_fixture:: Http ; 7 8fn cli ( directory : & std:: path:: Path , args : & [ & str ]) -> std:: process:: Output { 9 std:: process:: Command :: new ( env! ( "CARGO_BIN_EXE_avril" )) 10. current_dir ( directory) 11. arg ( args[ 0 ]) 12. arg ( "--cache-dir=cache" ) 13. args ( & args[ 1 ..]) 14. output () 15. unwrap () 16} 17 18fn success ( output : std:: process:: Output ) ->String { 19assert! ( 20 output. status . success (), 21"{}" , 22String :: from_utf8_lossy ( & output. stderr ) 23); 24String :: from_utf8 ( output. stdout ). unwrap () 25} 26 27fn failure ( output : std:: process:: Output , message : & str ) { 28assert! ( !output. status . success ()); 29assert! ( 30String :: from_utf8_lossy ( & output. stderr ). contains ( message), 31"{}" , 32String :: from_utf8_lossy ( & output. stderr ) 33); 34} 35 36fn write ( directory : & std:: path:: Path , file : & str , source : impl AsRef <[ u8 ]>) { 37 std:: fs:: write ( directory. join ( file), source). unwrap (); 38} 39 40# [ test ] 41fn packages_have_private_maps_dependencies_self_imports_and_cyclic_headers () { 42let http =Http :: new (); 43let dir = tempfile:: tempdir (). unwrap (); 44 http. source ( "math/avril.conf" , "name: '@acme/math', version: '1.2.3', description: 'Math', exports: {'.': './mod.ts', './vector': './src/vector.ts'}, deps: ['../numeric/', {from: '../unused/', name: 'unused'}], imports: {helper: './helper.ts'}, scopes: {'./src/': {helper: './scoped.ts'}}" ); 45 http. source ( 46"numeric/avril.conf" , 47"name: 'numeric', exports: './mod.ts', deps: ['../math/']" , 48); 49 http. source ( "numeric/mod.ts" , "import {vector} from '@acme/math/vector'; export const number = 40; export const read = () => vector;" ); 50 http. source ( "math/mod.ts" , "import {number, read} from 'numeric'; import {helper} from 'helper'; export {vector} from '@acme/math/vector'; export const value: number = number + helper; export const lazy = () => import('numeric'); export {read};" ); 51 http. source ( 52"math/src/vector.ts" , 53"import {helper} from 'helper'; export const vector = helper;" , 54); 55 http. source ( "math/helper.ts" , "export const helper = 2;" ); 56 http. source ( "math/scoped.ts" , "export const helper = 7;" ); 57write ( 58 dir. path (), 59"avril.conf" , 60format! ( 61"deps: ['{}', {{from: '{}', name: 'renamed'}}], imports: {{helper: './wrong.ts'}}" , 62 http. name ( "math/" ), 63 http. name ( "math/" ) 64), 65); 66write ( 67 dir. path (), 68"wrong.ts" , 69"throw new Error('consumer alias leaked');" , 70); 71write ( 72 dir. path (), 73"main.ts" , 74format! ( 75"import * as a from '@acme/math'; import * as b from 'renamed'; import * as c from '{}'; import {{vector}} from 'renamed/vector'; const [x,y] = await Promise.all([a.lazy(), a.lazy()]); console.log(a === b, a === c, a.value, vector, a.read(), x === y, x.number);" , 76 http. name ( "math/mod.ts" ) 77), 78); 79let expected ="true true 42 7 7 true 40\n" ; 80assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), expected); 81let requests = http. requests . lock (). unwrap (). clone (); 82assert! ( !requests. iter (). any ( |url| url. contains ( "unused" ))); 83assert_eq! ( 84 requests 85. iter () 86. filter ( |url| url. ends_with ( "avril.conf" )) 87. count (), 882 89); 90assert_eq! ( 91success ( cli ( 92 dir. path (), 93& [ "run" , "--offline" , "--frozen" , "main.ts" ] 94)), 95 expected 96); 97assert_eq! ( * http. requests . lock (). unwrap (), requests); 98let info: serde_json:: Value = 99 serde_json:: from_str ( & success ( cli ( dir. path (), & [ "info" , "--offline" , "main.ts" ]))). unwrap (); 100let package =& info[ "modules" ][ http. name ( "math/mod.ts" )][ "package" ]; 101assert_eq! ( package[ "name" ], "@acme/math" ); 102assert_eq! ( package[ "config" ], http. name ( "math/avril.conf" )); 103assert_eq! ( package[ "dependencies" ][ "numeric" ], http. name ( "numeric/" )); 104assert_eq! ( 105 package[ "exports" ][ "./vector" ], 106 http. name ( "math/src/vector.ts" ) 107); 108write ( dir. path (), "main.ts" , "import 'numeric';" ); 109failure ( 110cli ( dir. path (), & [ "run" , "--offline" , "main.ts" ]), 111"Unsupported module specifier" , 112); 113} 114 115# [ test ] 116fn configs_and_sources_are_pinned_updated_and_vendored_together () { 117let http =Http :: new (); 118let dir = tempfile:: tempdir (). unwrap (); 119 http. source ( "pkg/avril.conf" , "name: 'pkg', exports: './v1.ts'" ); 120 http. source ( "pkg/v1.ts" , "export const value = 1;" ); 121 http. source ( "pkg/v2.ts" , "export const value = 2;" ); 122write ( 123 dir. path (), 124"avril.conf" , 125format! ( "deps: ['{}']" , http. name ( "pkg/" )), 126); 127write ( 128 dir. path (), 129"main.ts" , 130"import {value} from 'pkg'; console.log(value);" , 131); 132write ( 133 dir. path (), 134"avril.lock" , 135"version: 1\njsrRegistry: \"https://jsr.io/\"\nspecifiers: {}\npackages: {}\nremote: {}\n" , 136); 137failure ( 138cli ( dir. path (), & [ "cache" , "--frozen" , "main.ts" ]), 139"missing remote source" , 140); 141assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "1\n" ); 142let lock = registry_fixture:: read_lock ( dir. path ()); 143assert! ( lock[ "remote" ][ http. name ( "pkg/avril.conf" )][ "checksum" ]. is_string ()); 144 http. source ( "pkg/avril.conf" , "name: 'pkg', exports: './v2.ts'" ); 145assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "1\n" ); 146 std:: fs:: remove_dir_all ( dir. path (). join ( "cache/http/blobs" )). unwrap (); 147failure ( cli ( dir. path (), & [ "run" , "main.ts" ]), "integrity mismatch" ); 148success ( cli ( dir. path (), & [ "update" , "--prune" , "main.ts" ])); 149assert_eq! ( 150success ( cli ( 151 dir. path (), 152& [ "run" , "--offline" , "--frozen" , "main.ts" ] 153)), 154"2\n" 155); 156success ( cli ( dir. path (), & [ "vendor" , "main.ts" ])); 157 std:: fs:: remove_dir_all ( dir. path (). join ( "cache" )). unwrap (); 158let requests = http. requests . lock (). unwrap (). len (); 159assert_eq! ( 160success ( cli ( 161 dir. path (), 162& [ "run" , "--offline" , "--frozen" , "--vendor=vendor" , "main.ts" ] 163)), 164"2\n" 165); 166assert_eq! ( http. requests . lock (). unwrap (). len (), requests); 167let lock = registry_fixture:: read_lock ( dir. path ()); 168assert! ( lock[ "remote" ]. get ( http. name ( "pkg/v1.ts" )). is_none ()); 169assert! ( lock[ "remote" ][ http. name ( "pkg/avril.conf" )]. is_object ()); 170} 171 172# [ test ] 173fn redirected_configs_resolve_exports_and_private_dependencies_at_the_final_url () { 174let http =Http :: new (); 175let target =Http :: new (); 176let dir = tempfile:: tempdir (). unwrap (); 177 http. redirect ( "pkg/avril.conf" , & target. name ( "nested/pkg/avril.conf" )); 178 target. source ( 179"nested/pkg/avril.conf" , 180"name: 'pkg', exports: './mod.ts', deps: ['../dep/']" , 181); 182 target. source ( "nested/dep/avril.conf" , "name: 'dep', exports: './mod.ts'" ); 183 target. source ( "nested/dep/mod.ts" , "export const value = 9;" ); 184 target. source ( "nested/pkg/mod.ts" , "export {value} from 'dep';" ); 185write ( 186 dir. path (), 187"avril.conf" , 188format! ( "deps: ['{}']" , http. name ( "pkg/" )), 189); 190write ( 191 dir. path (), 192"main.ts" , 193"import {value} from 'pkg'; console.log(value);" , 194); 195let allow_local ="--allow-js-exec=." ; 196let allow_config =format! ( "--allow-js-exec={}" , http. name ( "pkg/avril.conf" )); 197let allow_target =format! ( "--allow-js-exec={}" , target. name ( "nested/" )); 198let deny_destination_config = 199format! ( "--deny-js-exec={}" , target. name ( "nested/pkg/avril.conf" )); 200let args =[ 201"run" , 202"-0" , 203 allow_local, 204& allow_config, 205& allow_target, 206& deny_destination_config, 207"main.ts" , 208]; 209assert_eq! ( success ( cli ( dir. path (), & args)), "9\n" ); 210let mut offline = args. to_vec (); 211 offline. insert ( 1 , "--offline" ); 212 offline. insert ( 1 , "--frozen" ); 213assert_eq! ( success ( cli ( dir. path (), & offline)), "9\n" ); 214} 215 216# [ test ] 217fn configs_and_exports_require_execution_authority_even_on_cache_hits () { 218let http =Http :: new (); 219let dir = tempfile:: tempdir (). unwrap (); 220 http. source ( "pkg/avril.conf" , "name: 'pkg', exports: './mod.ts'" ); 221 http. source ( "pkg/mod.ts" , "console.log('loaded');" ); 222write ( 223 dir. path (), 224"avril.conf" , 225format! ( "deps: [{{from: '{}', name: 'pkg'}}]" , http. name ( "pkg/" )), 226); 227write ( dir. path (), "main.ts" , "import 'pkg';" ); 228let allow =format! ( "--allow-js-exec={}" , http. name ( "pkg/" )); 229success ( cli ( 230 dir. path (), 231& [ "run" , "-0" , "--allow-js-exec=." , & allow, "main.ts" ], 232)); 233for deniedin [ "pkg/avril.conf" , "pkg/mod.ts" ] { 234let deny =format! ( "--deny-js-exec={}" , http. name ( denied)); 235let requests = http. requests . lock (). unwrap (). len (); 236failure ( 237cli ( 238 dir. path (), 239& [ 240"run" , 241"-0" , 242"--allow-js-exec=." , 243& allow, 244& deny, 245"--offline" , 246"main.ts" , 247], 248), 249"PermissionDenied" , 250); 251assert_eq! ( http. requests . lock (). unwrap (). len (), requests); 252} 253} 254 255# [ test ] 256fn dependency_headers_bind_names_and_interfaces_without_fallbacks () { 257let http =Http :: new (); 258let dir = tempfile:: tempdir (). unwrap (); 259write ( dir. path (), "main.ts" , "import 'pkg/private';" ); 260 http. source ( 261"pkg/avril.conf" , 262"name: 'pkg', exports: {'./other': './other.ts'}" , 263); 264 http. source ( "pkg/private.ts" , "throw new Error('private fallback');" ); 265write ( 266 dir. path (), 267"avril.conf" , 268format! ( "deps: ['{}']" , http. name ( "pkg/" )), 269); 270failure ( cli ( dir. path (), & [ "run" , "main.ts" ]), "no export" ); 271assert! ( 272 !http 273. requests 274. lock () 275. unwrap () 276. iter () 277. any ( |url| url. ends_with ( "private.ts" )) 278); 279write ( 280 dir. path (), 281"avril.conf" , 282format! ( 283"deps: ['{}', {{from: '{}', name: 'pkg'}}]" , 284 http. name ( "pkg/" ), 285 http. name ( "another/" ) 286), 287); 288failure ( 289cli ( dir. path (), & [ "run" , "main.ts" ]), 290"Duplicate dependency binding" , 291); 292 http. source ( "anonymous/avril.conf" , "exports: './mod.ts'" ); 293write ( 294 dir. path (), 295"avril.conf" , 296format! ( "deps: ['{}']" , http. name ( "anonymous/" )), 297); 298failure ( cli ( dir. path (), & [ "run" , "main.ts" ]), "declares no name" ); 299 http. source ( "anonymous/mod.ts" , "console.log('anonymous');" ); 300write ( 301 dir. path (), 302"avril.conf" , 303format! ( 304"deps: [{{from: '{}', name: 'pkg'}}]" , 305 http. name ( "anonymous/" ) 306), 307); 308write ( dir. path (), "main.ts" , "import 'pkg';" ); 309assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "anonymous\n" ); 310} 311 312# [ test ] 313fn cached_package_bindings_obey_live_config_revocation () { 314let http =Http :: new (); 315let dir = tempfile:: tempdir (). unwrap (); 316 http. source ( "pkg/avril.conf" , "name: 'pkg', exports: './mod.ts'" ); 317 http. source ( "pkg/mod.ts" , "export const value = 42;" ); 318write ( 319 dir. path (), 320"avril.conf" , 321format! ( "deps: ['{}']" , http. name ( "pkg/" )), 322); 323write ( 324 dir. path (), 325"main.ts" , 326format! ( 327"import {{pledge}} from 'avril:std'; const a = await import('pkg'); console.log((await import('pkg')) === a); pledge({{jsExec: {{deny: ['{}']}}}}); try {{await import('pkg'); console.log('allowed');}} catch (e) {{console.log(e.message.includes('PermissionDenied'));}}" , 328 http. name ( "pkg/avril.conf" ) 329), 330); 331assert_eq! ( 332success ( cli ( dir. path (), & [ "run" , "main.ts" ])), 333"true\ntrue\n" 334); 335} 336 337# [ test ] 338fn declared_nested_packages_own_exported_files () { 339let http =Http :: new (); 340let dir = tempfile:: tempdir (). unwrap (); 341 http. source ( 342"pkg/avril.conf" , 343"name: 'outer', exports: './child/mod.ts', deps: [{from: './child/', name: 'nested'}], imports: {helper: './wrong.ts'}" , 344); 345 http. source ( 346"pkg/child/avril.conf" , 347"name: 'child', exports: './mod.ts', imports: {helper: './helper.ts'}" , 348); 349 http. source ( "pkg/child/mod.ts" , "export {value} from 'helper';" ); 350 http. source ( "pkg/child/helper.ts" , "export const value = 7;" ); 351 http. source ( "pkg/wrong.ts" , "throw new Error('parent map leaked');" ); 352write ( 353 dir. path (), 354"avril.conf" , 355format! ( 356"deps: ['{}', '{}']" , 357 http. name ( "pkg/" ), 358 http. name ( "pkg/child/" ) 359), 360); 361write ( 362 dir. path (), 363"main.ts" , 364"import * as a from 'outer'; import * as b from 'child'; console.log(a === b, a.value);" , 365); 366assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "true 7\n" ); 367} 368 369# [ test ] 370fn remote_packages_can_bind_private_jsr_dependencies () { 371let http =Http :: new (); 372let registry = registry_fixture:: Registry :: new (); 373let dir = tempfile:: tempdir (). unwrap (); 374 registry. package ( 375"@test/numeric" , 376"1.0.0" , 377& [( "mod.ts" , "export const number: number = 42;" )], 378& [( "." , "./mod.ts" )], 379); 380 http. source ( 381"pkg/avril.conf" , 382"name: 'pkg', exports: './mod.ts', deps: [{from: 'jsr:@test/numeric@1', name: 'numeric'}]" , 383); 384 http. source ( "pkg/mod.ts" , "export {number} from 'numeric';" ); 385write ( 386 dir. path (), 387"avril.conf" , 388format! ( 389"deps: ['{}'], imports: {{numeric: './wrong.ts'}}" , 390 http. name ( "pkg/" ) 391), 392); 393write ( 394 dir. path (), 395"wrong.ts" , 396"throw new Error('consumer alias leaked');" , 397); 398write ( 399 dir. path (), 400"main.ts" , 401"import {number} from 'pkg'; console.log(number);" , 402); 403let registry_arg =format! ( 404"--jsr-registry={}" , 405 registry. options ( dir. path ()). jsr_registry 406); 407assert_eq! ( 408success ( cli ( dir. path (), & [ "run" , & registry_arg, "main.ts" ])), 409"42\n" 410); 411let requests = registry. requests . lock (). unwrap (). len (); 412assert_eq! ( 413success ( cli ( 414 dir. path (), 415& [ "run" , & registry_arg, "--offline" , "--frozen" , "main.ts" ] 416)), 417"42\n" 418); 419assert_eq! ( registry. requests . lock (). unwrap (). len (), requests); 420} 421 422# [ test ] 423fn nested_ownership_and_metadata_are_independent_of_graph_traversal_order () { 424let http =Http :: new (); 425let dir = tempfile:: tempdir (). unwrap (); 426 http. source ( "pkg/avril.conf" , "name: 'outer', exports: './child/mod.ts', deps: [{from: './child/', name: 'nested'}], imports: {helper: './wrong.ts'}" ); 427 http. source ( 428"pkg/child/avril.conf" , 429"name: 'child', exports: './mod.ts', imports: {helper: './helper.ts'}" , 430); 431 http. source ( "pkg/child/mod.ts" , "export {value} from 'helper';" ); 432 http. source ( "pkg/child/helper.ts" , "export const value = 7;" ); 433 http. source ( "pkg/wrong.ts" , "throw new Error('parent map leaked');" ); 434write ( 435 dir. path (), 436"avril.conf" , 437format! ( 438"deps: ['{}', '{}']" , 439 http. name ( "pkg/" ), 440 http. name ( "pkg/child/" ) 441), 442); 443write ( 444 dir. path (), 445"x.ts" , 446"import * as a from 'outer'; export {a};" , 447); 448write ( 449 dir. path (), 450"y.ts" , 451"import * as b from 'child'; export {b};" , 452); 453for importsin [ 454"import {a} from './x.ts'; import {b} from './y.ts';" , 455"import {b} from './y.ts'; import {a} from './x.ts';" , 456] { 457write ( 458 dir. path (), 459"main.ts" , 460format! ( "{imports} console.log(a === b, a.value);" ), 461); 462assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "true 7\n" ); 463let info: serde_json:: Value = 464 serde_json:: from_str ( & success ( cli ( dir. path (), & [ "info" , "--offline" , "main.ts" ]))) 465. unwrap (); 466let module =& info[ "modules" ][ http. name ( "pkg/child/mod.ts" )]; 467assert_eq! ( module[ "package" ][ "name" ], "child" ); 468assert_eq! ( 469 module[ "imports" ][ "helper" ], 470 http. name ( "pkg/child/helper.ts" ) 471); 472} 473} 474 475# [ test ] 476fn conflicting_owners_fail_in_both_graph_orders_instead_of_rebinding_modules () { 477let http =Http :: new (); 478let dir = tempfile:: tempdir (). unwrap (); 479 http. source ( 480"pkg/avril.conf" , 481"name: 'outer', exports: './child/mod.ts', imports: {helper: './helper.ts'}" , 482); 483 http. source ( 484"pkg/child/avril.conf" , 485"name: 'child', exports: './mod.ts', imports: {helper: './helper.ts'}" , 486); 487 http. source ( "pkg/child/mod.ts" , "export {value} from 'helper';" ); 488 http. source ( "pkg/helper.ts" , "export const value = 1;" ); 489 http. source ( "pkg/child/helper.ts" , "export const value = 7;" ); 490write ( 491 dir. path (), 492"avril.conf" , 493format! ( 494"deps: ['{}', '{}']" , 495 http. name ( "pkg/" ), 496 http. name ( "pkg/child/" ) 497), 498); 499write ( dir. path (), "x.ts" , "import 'outer';" ); 500write ( dir. path (), "y.ts" , "import 'child';" ); 501for importsin [ 502"import './x.ts'; import './y.ts';" , 503"import './y.ts'; import './x.ts';" , 504] { 505write ( dir. path (), "main.ts" , imports); 506failure ( 507cli ( dir. path (), & [ "run" , "main.ts" ]), 508"Conflicting remote package ownership" , 509); 510} 511} 512 513# [ test ] 514fn selecting_a_private_dependency_does_not_change_existing_modules_maps () { 515let http =Http :: new (); 516let dir = tempfile:: tempdir (). unwrap (); 517 http. source ( 518"pkg/avril.conf" , 519"name: 'a', exports: './child/util.ts', imports: {helper: './helper.ts'}" , 520); 521 http. source ( "pkg/helper.ts" , "export const value = 1;" ); 522 http. source ( 523"pkg/child/util.ts" , 524"export const lazy = async () => (await import('helper')).value;" , 525); 526 http. source ( 527"pkg/child/avril.conf" , 528"name: 'child', exports: './other.ts', imports: {helper: './helper.ts'}" , 529); 530 http. source ( "pkg/child/helper.ts" , "export const value = 99;" ); 531 http. source ( "pkg/child/other.ts" , "export const value = 2;" ); 532 http. source ( 533"b/avril.conf" , 534format! ( 535"name: 'b', exports: './mod.ts', deps: [{{from: '{}', name: 'private'}}]" , 536 http. name ( "pkg/child/" ) 537) 538. as_str (), 539); 540 http. source ( "b/mod.ts" , "export {value} from 'private';" ); 541write ( 542 dir. path (), 543"avril.conf" , 544format! ( "deps: ['{}', '{}']" , http. name ( "pkg/" ), http. name ( "b/" )), 545); 546write ( 547 dir. path (), 548"main.ts" , 549"const a = await import('a'); const before = await a.lazy(); const b = await import('b'); console.log(before, await a.lazy(), b.value);" , 550); 551assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "1 1 2\n" ); 552} 553 554# [ test ] 555fn a_plain_module_stays_plain_after_a_conflicting_package_import () { 556let http =Http :: new (); 557let dir = tempfile:: tempdir (). unwrap (); 558 http. source ( 559"pkg/avril.conf" , 560"name: 'pkg', exports: './util.ts', imports: {helper: './helper.ts'}" , 561); 562 http. source ( "pkg/util.ts" , "export const lazy = () => import('helper');" ); 563 http. source ( "pkg/helper.ts" , "export const value = 7;" ); 564 std:: fs:: create_dir ( dir. path (). join ( "plain" )). unwrap (); 565write ( 566 dir. path (), 567"avril.conf" , 568format! ( "deps: ['{}']" , http. name ( "pkg/" )), 569); 570write ( dir. path (), "plain/avril.conf" , "" ); 571write ( 572 dir. path (), 573"plain/mod.ts" , 574format! ( "export {{lazy}} from '{}';" , http. name ( "pkg/util.ts" )), 575); 576write ( 577 dir. path (), 578"main.ts" , 579"import {lazy} from './plain/mod.ts'; for (let i = 0; i < 2; i++) {try {await lazy();} catch(e) {console.log(e.message.includes('Unsupported module specifier'));} if (i === 0) {try {await import('pkg');} catch(e) {console.log(e.message.includes('Conflicting remote package ownership'));}}}" , 580); 581assert_eq! ( 582success ( cli ( dir. path (), & [ "run" , "main.ts" ])), 583"true\ntrue\ntrue\n" 584); 585} 586 587# [ test ] 588fn registry_prefixed_configs_are_rejected_without_fetching_or_changing_the_lock () { 589let http =Http :: new (); 590let dir = tempfile:: tempdir (). unwrap (); 591let registry = http. name ( "registry/" ); 592let args_registry =format! ( "--jsr-registry={registry}" ); 593let lock =format! ( 594"version: 1\njsrRegistry: \"{registry}\"\nspecifiers: {{}}\npackages: {{}}\nremote: {{}}\n" 595); 596write ( dir. path (), "avril.lock" , & lock); 597 http. source ( 598"registry/pkg/avril.conf" , 599"name: 'pkg', exports: './mod.ts'" , 600); 601for dependencyin [ 602format! ( "'{}'" , http. name ( "registry/pkg/" )), 603format! ( "{{from: '{}', name: 'pkg'}}" , http. name ( "registry/pkg/" )), 604] { 605write ( dir. path (), "avril.conf" , format! ( "deps: [{dependency}]" )); 606write ( dir. path (), "main.ts" , "import 'pkg';" ); 607failure ( 608cli ( dir. path (), & [ "run" , & args_registry, "main.ts" ]), 609"configured JSR registry" , 610); 611assert_eq! ( 612 std:: fs:: read_to_string ( dir. path (). join ( "avril.lock" )). unwrap (), 613 lock 614); 615assert! ( http. requests . lock (). unwrap (). is_empty ()); 616} 617} 618 619# [ test ] 620fn a_failed_config_fetch_does_not_snapshot_plain_ownership () { 621let http =Http :: new (); 622let dir = tempfile:: tempdir (). unwrap (); 623 http. source ( "pkg/mod.ts" , "export const value = 7;" ); 624let routes = http. routes . clone (); 625* http. hook . lock (). unwrap () =Some ( Box :: new ( move ||{ 626 routes. lock (). unwrap (). insert ( 627"/pkg/avril.conf" . into (), 628 http_fixture:: Response { 629status : 200 , 630headers : "Content-Type: text/plain\r\n" . into (), 631body : b"name: 'pkg', exports: './mod.ts'" . to_vec (), 632}, 633); 634})); 635write ( 636 dir. path (), 637"avril.conf" , 638format! ( "deps: [{{from: '{}', name: 'pkg'}}]" , http. name ( "pkg/" )), 639); 640write ( 641 dir. path (), 642"main.ts" , 643format! ( 644"try {{await import('pkg');}} catch(e) {{console.log(e.message.includes('404'));}} console.log((await import('{}')).value);" , 645 http. name ( "pkg/mod.ts" ) 646), 647); 648assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "true\n7\n" ); 649assert_eq! ( 650 http. requests 651. lock () 652. unwrap () 653. iter () 654. filter ( |url| url. ends_with ( "avril.conf" )) 655. count (), 6562 657); 658} 659 660# [ test ] 661fn redirected_aliases_share_modules_only_when_config_pins_agree () { 662for changedin [ false , true ] { 663let http =Http :: new (); 664let target =Http :: new (); 665let dir = tempfile:: tempdir (). unwrap (); 666 http. redirect ( "a/avril.conf" , & target. name ( "pkg/avril.conf" )); 667 http. redirect ( "b/avril.conf" , & target. name ( "pkg/avril.conf" )); 668 target. source ( "pkg/avril.conf" , "exports: './mod.ts'" ); 669 target. source ( "pkg/mod.ts" , "export const value = 7;" ); 670if changed{ 671let routes = target. routes . clone (); 672* target. hook . lock (). unwrap () =Some ( Box :: new ( move ||{ 673 routes. lock (). unwrap (). insert ( 674"/pkg/avril.conf" . into (), 675 http_fixture:: Response { 676status : 200 , 677headers : "Content-Type: text/plain\r\n" . into (), 678body : b"exports: './mod.ts', description: 'changed'" . to_vec (), 679}, 680); 681})); 682} 683write ( 684 dir. path (), 685"avril.conf" , 686format! ( 687"deps: [{{from: '{}', name: 'a'}}, {{from: '{}', name: 'b'}}]" , 688 http. name ( "a/" ), 689 http. name ( "b/" ) 690), 691); 692write ( 693 dir. path (), 694"main.ts" , 695"import * as a from 'a'; import * as b from 'b'; console.log(a === b, a.value);" , 696); 697if changed{ 698failure ( 699cli ( dir. path (), & [ "run" , "main.ts" ]), 700"Conflicting remote package ownership" , 701); 702} else { 703assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "true 7\n" ); 704assert_eq! ( 705success ( cli ( 706 dir. path (), 707& [ "run" , "--offline" , "--frozen" , "main.ts" ] 708)), 709"true 7\n" 710); 711} 712} 713} 714 715# [ test ] 716fn equivalent_aliases_use_the_canonical_module_region_in_both_orders () { 717for root_aliasin [ false , true ] { 718let http =Http :: new (); 719let dir = tempfile:: tempdir (). unwrap (); 720let ( a, b, outside) =if root_alias{ 721 http. redirect ( "avril.conf" , & http. name ( "f/avril.conf" )); 722( http. url . to_string (), http. name ( "f/" ), "other.ts" ) 723} else { 724 http. redirect ( "a/avril.conf" , & http. name ( "f/avril.conf" )); 725 http. redirect ( "b/avril.conf" , & http. name ( "f/avril.conf" )); 726( http. name ( "a/" ), http. name ( "b/" ), "b/extra.ts" ) 727}; 728 http. source ( 729"f/avril.conf" , 730"name: 'shared', exports: './mod.ts', imports: {helper: './helper.ts'}" , 731); 732 http. source ( 733"f/mod.ts" , 734format! ( 735"export const lazy = () => import('{}');" , 736 http. name ( outside) 737) 738. as_str (), 739); 740 http. source ( outside, "export {value} from 'helper';" ); 741 http. source ( "f/helper.ts" , "export const value = 7;" ); 742write ( 743 dir. path (), 744"avril.conf" , 745format! ( "deps: [{{from: '{a}', name: 'a'}}, {{from: '{b}', name: 'b'}}]" ), 746); 747for importsin [ 748"import * as a from 'a'; import * as b from 'b';" , 749"import * as b from 'b'; import * as a from 'a';" , 750] { 751write ( 752 dir. path (), 753"main.ts" , 754format! ( 755"{imports} try {{await a.lazy();}} catch(e) {{console.log(a === b, e.message.includes('Unsupported module specifier'));}}" 756), 757); 758assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "true true\n" ); 759let info: serde_json:: Value = 760 serde_json:: from_str ( & success ( cli ( dir. path (), & [ "info" , "main.ts" ]))). unwrap (); 761assert_eq! ( 762 info[ "modules" ][ http. name ( "f/mod.ts" )][ "package" ][ "config" ], 763 http. name ( "f/avril.conf" ) 764); 765} 766} 767} 768 769# [ test ] 770fn revoking_any_claimed_alias_blocks_all_aliases_in_both_orders () { 771let http =Http :: new (); 772let dir = tempfile:: tempdir (). unwrap (); 773 http. redirect ( "a/avril.conf" , & http. name ( "f/avril.conf" )); 774 http. redirect ( "b/avril.conf" , & http. name ( "f/avril.conf" )); 775 http. source ( "f/avril.conf" , "name: 'shared', exports: './mod.ts'" ); 776 http. source ( "f/mod.ts" , "export const lazy = () => import('shared');" ); 777write ( 778 dir. path (), 779"avril.conf" , 780format! ( 781"deps: [{{from: '{}', name: 'a'}}, {{from: '{}', name: 'b'}}]" , 782 http. name ( "a/" ), 783 http. name ( "b/" ) 784), 785); 786for importsin [ 787"import * as a from 'a'; import * as b from 'b';" , 788"import * as b from 'b'; import * as a from 'a';" , 789] { 790write ( 791 dir. path (), 792"main.ts" , 793format! ( 794"{imports} import {{pledge}} from 'avril:std'; console.log(a === b); pledge({{jsExec: {{deny: ['{}']}}}}); try {{await import('b');}} catch(e) {{console.log(e.message.includes('PermissionDenied'));}} try {{await a.lazy();}} catch(e) {{console.log(e.message.includes('PermissionDenied'));}}" , 795 http. name ( "a/avril.conf" ) 796), 797); 798assert_eq! ( 799success ( cli ( dir. path (), & [ "run" , "main.ts" ])), 800"true\ntrue\ntrue\n" 801); 802} 803} 804 805# [ test ] 806fn rejected_registry_redirects_are_not_pinned_after_a_later_successful_graph () { 807let http =Http :: new (); 808let dir = tempfile:: tempdir (). unwrap (); 809let registry = http. name ( "registry/" ); 810let registry_arg =format! ( "--jsr-registry={registry}" ); 811let lock =format! ( 812"version: 1\njsrRegistry: \"{registry}\"\nspecifiers: {{}}\npackages: {{}}\nremote: {{}}\n" 813); 814write ( dir. path (), "avril.lock" , & lock); 815 http. redirect ( "alias/avril.conf" , & http. name ( "registry/pkg/avril.conf" )); 816 http. source ( 817"registry/pkg/avril.conf" , 818"name: 'pkg', exports: './mod.ts'" , 819); 820write ( 821 dir. path (), 822"avril.conf" , 823format! ( "deps: [{{from: '{}', name: 'pkg'}}]" , http. name ( "alias/" )), 824); 825write ( dir. path (), "other.ts" , "console.log('done');" ); 826write ( 827 dir. path (), 828"main.ts" , 829"try {await import('pkg');} catch(e) {console.log(e.message.includes('configured JSR registry'));} await import('./other.ts');" , 830); 831assert_eq! ( 832success ( cli ( dir. path (), & [ "run" , & registry_arg, "main.ts" ])), 833"true\ndone\n" 834); 835assert_eq! ( 836 std:: fs:: read_to_string ( dir. path (). join ( "avril.lock" )). unwrap (), 837 lock 838); 839} 840 841# [ test ] 842fn nested_config_redirect_cycles_are_reported_instead_of_looping () { 843let http =Http :: new (); 844let dir = tempfile:: tempdir (). unwrap (); 845 http. source ( 846"pkg/avril.conf" , 847"name: 'pkg', exports: './child/mod.ts', deps: [{from: './child/', name: 'nested'}]" , 848); 849 http. redirect ( "pkg/child/avril.conf" , & http. name ( "pkg/avril.conf" )); 850 http. source ( "pkg/child/mod.ts" , "export {};" ); 851write ( 852 dir. path (), 853"avril.conf" , 854format! ( "deps: ['{}']" , http. name ( "pkg/" )), 855); 856write ( dir. path (), "main.ts" , "import 'pkg';" ); 857failure ( 858cli ( dir. path (), & [ "run" , "main.ts" ]), 859"Cyclic remote package boundary" , 860); 861} 862 863# [ test ] 864fn remote_package_maps_never_treat_file_or_absolute_targets_as_host_files () { 865for filesystem_urlin [ false , true ] { 866let http =Http :: new (); 867let dir = tempfile:: tempdir (). unwrap (); 868write ( 869 dir. path (), 870"secret.ts" , 871"throw new Error('host file was read');" , 872); 873let target =if filesystem_url{ 874 url:: Url :: from_file_path ( dir. path (). join ( "secret.ts" )) 875. unwrap () 876. to_string () 877} else { 878"/secret.ts" . into () 879}; 880 http. source ( 881"pkg/avril.conf" , 882format! ( "name: 'pkg', exports: './mod.ts', imports: {{escape: '{target}'}}" ). as_str (), 883); 884 http. source ( "pkg/mod.ts" , "export {value} from 'escape';" ); 885 http. source ( "secret.ts" , "export const value = 7;" ); 886write ( 887 dir. path (), 888"avril.conf" , 889format! ( "deps: ['{}']" , http. name ( "pkg/" )), 890); 891write ( 892 dir. path (), 893"main.ts" , 894"import {value} from 'pkg'; console.log(value);" , 895); 896if filesystem_url{ 897failure ( 898cli ( dir. path (), & [ "run" , "main.ts" ]), 899"Unsupported module specifier" , 900); 901} else { 902assert_eq! ( success ( cli ( dir. path (), & [ "run" , "main.ts" ])), "7\n" ); 903} 904} 905} 906 907# [ test ] 908fn redirected_export_modules_match_scopes_and_relative_imports_at_the_final_url () { 909let http =Http :: new (); 910let dir = tempfile:: tempdir (). unwrap (); 911 http. source ( "pkg/avril.conf" , "name: 'pkg', exports: './src/mod.ts', imports: {helper: './default.ts'}, scopes: {'./src/': {helper: './scoped.ts'}}" ); 912 http. redirect ( "pkg/src/mod.ts" , & http. name ( "pkg/final/mod.ts" )); 913 http. source ( "pkg/final/mod.ts" , "import {value} from 'helper'; import {extra} from './dep.ts'; export const result = value + extra; export const url = import.meta.url;" ); 914 http. source ( "pkg/default.ts" , "export const value = 1;" ); 915 http. source ( 916"pkg/scoped.ts" , 917"throw new Error('requested URL scope leaked');" , 918); 919 http. source ( "pkg/final/dep.ts" , "export const extra = 2;" ); 920write ( 921 dir. path (), 922"avril.conf" , 923format! ( "deps: ['{}']" , http. name ( "pkg/" )), 924); 925write ( 926 dir. path (), 927"main.ts" , 928"import {result,url} from 'pkg'; console.log(result,url);" , 929); 930let deny =format! ( "--deny-js-exec={}" , http. name ( "pkg/final/mod.ts" )); 931assert_eq! ( 932success ( cli ( dir. path (), & [ "run" , & deny, "main.ts" ])), 933format! ( "3 {}\n" , http. name ( "pkg/src/mod.ts" )) 934); 935let info: serde_json:: Value = serde_json:: from_str ( & success ( cli ( 936 dir. path (), 937& [ "info" , "--offline" , & deny, "main.ts" ], 938))) 939. unwrap (); 940assert_eq! ( 941 info[ "modules" ][ http. name ( "pkg/src/mod.ts" )][ "imports" ][ "helper" ], 942 http. name ( "pkg/default.ts" ) 943); 944}