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/registry.rs" ] 2mod fixture; 3 4use std:: fs; 5use std:: path:: Path ; 6use std:: process::{ Command , Output }; 7 8use fixture:: Registry ; 9use serde_json:: json; 10 11fn run ( server : & Registry , directory : & Path , args : & [ & str ]) ->Output { 12let options = server. options ( directory); 13Command :: new ( env! ( "CARGO_BIN_EXE_avril" )) 14. current_dir ( directory) 15// Options alone run the entry; a leading command selects another. 16. args ( 17 args. first () 18. is_none_or ( |arg| arg. starts_with ( '-' )) 19. then_some ( "run" ), 20) 21. args ( args) 22. arg ( format! ( "--jsr-registry={}" , options. jsr_registry )) 23. arg ( format! ( "--cache-dir={}" , options. cache . display ())) 24. arg ( "main.ts" ) 25. output () 26. unwrap () 27} 28 29fn success ( output : & Output ) ->String { 30assert! ( 31 output. status . success (), 32"{}" , 33String :: from_utf8_lossy ( & output. stderr ) 34); 35String :: from_utf8 ( output. stdout . clone ()). unwrap () 36} 37 38# [ test ] 39fn runs_native_jsr_graphs_with_typescript_cycles_exports_and_package_private_dependencies () { 40let server =Registry :: new (); 41 server. package ( 42"@test/value" , 43"1.0.0" , 44& [( "mod.ts" , "export const value = 'one';" )], 45& [( "." , "./mod.ts" )], 46); 47 server. package ( 48"@test/value" , 49"2.0.0" , 50& [( "mod.ts" , "export const value = 'two';" )], 51& [( "." , "./mod.ts" )], 52); 53 server. package ( "@test/library" , "1.0.0" , & [ 54( "mod.ts" , "import type {Missing} from 'npm:missing'; import {value} from 'jsr:/@test/value@^1'; import './cycle.ts'; export {value}; export const count: number = globalThis.count = (globalThis.count ?? 0) + 1; export const main = import.meta.main; export const url = import.meta.url; export const self = () => import(import.meta.url);" ), 55( "cycle.ts" , "import {value} from './mod.ts'; export const read = () => value;" ), 56( "sub.mts" , "export const value: number = 42;" ), 57], & [( "." , "./mod.ts" ), ( "./sub" , "./sub.mts" )]); 58let dir = tempfile:: tempdir (). unwrap (); 59 fs:: write ( dir. path (). join ( "avril.conf" ), "imports: {lib: 'jsr:@test/library@^1', 'lib/': 'jsr:@test/library@^1/', value: 'jsr:@test/value@^2'}" ). unwrap (); 60 fs:: write ( dir. path (). join ( "main.ts" ), "import * as a from 'lib'; import * as b from 'jsr:@test/library@^1'; import {value} from 'value'; import {value as sub} from 'lib/sub'; console.log(a === b, a.value, value, sub, a.count, a.main, (await a.self()) === a, a.url);" ). unwrap (); 61let output =run ( & server, dir. path (), & []); 62let stdout =success ( & output); 63assert_eq! ( 64 stdout, 65format! ( 66"true one two 42 1 false true {}@test/library/1.0.0/mod.ts\n" , 67 server. url 68) 69); 70let lock = fixture:: read_lock ( dir. path ()); 71assert_eq! ( lock[ "specifiers" ][ "jsr:@test/value@^1" ], "1.0.0" ); 72assert_eq! ( lock[ "specifiers" ][ "jsr:@test/value@^2" ], "2.0.0" ); 73assert! ( 74 !server 75. requests 76. lock () 77. unwrap () 78. iter () 79. any ( |path| path. contains ( "missing" )) 80); 81} 82 83# [ test ] 84fn computed_dynamic_imports_acquire_static_closures_and_share_evaluation () { 85let server =Registry :: new (); 86 server. package ( "@test/lazy" , "1.0.0" , & [ 87( "mod.ts" , "import {value} from './dep.ts'; export {value}; export const count = globalThis.count = (globalThis.count ?? 0) + 1; export const main = import.meta.main;" ), 88( "dep.ts" , "export const value: string = 'acquired';" ), 89], & [( "." , "./mod.ts" )]); 90let mut metadata: serde_json:: Value = serde_json:: from_slice ( 91 server 92. files 93. lock () 94. unwrap () 95. get ( "/@test/lazy/1.0.0_meta.json" ) 96. unwrap (), 97) 98. unwrap (); 99 metadata[ "moduleGraph2" ] =json! ({ "/mod.ts" : { "dependencies" : [{ "type" : "static" , "kind" : "import" , "specifier" : "./dep.ts" }]}}); 100 server. files . lock (). unwrap (). insert ( 101"/@test/lazy/1.0.0_meta.json" . into (), 102 serde_json:: to_vec ( & metadata). unwrap (), 103); 104let dir = tempfile:: tempdir (). unwrap (); 105 fs:: write ( dir. path (). join ( "main.ts" ), "const name = 'jsr:@test/' + 'lazy@1'; const timer = new Promise(resolve => setTimeout(resolve, 0)); const [a,b,c] = await Promise.all([import(name),import(name),import(name)]); await timer; console.log(a === b, b === c, a.value, a.count, a.main); try { await import('jsr:@test/missing@1'); throw new Error('success'); } catch(e) { console.log(e.message.includes('404')); } console.log((await import(name)) === a);" ). unwrap (); 106assert_eq! ( 107success ( & run ( & server, dir. path (), & [])), 108"true true acquired 1 false\ntrue\ntrue\n" 109); 110let requests = server. requests . lock (). unwrap (); 111for pathin [ 112"/@test/lazy/meta.json" , 113"/@test/lazy/1.0.0_meta.json" , 114"/@test/lazy/1.0.0/mod.ts" , 115"/@test/lazy/1.0.0/dep.ts" , 116] { 117assert_eq! ( 118 requests 119. iter () 120. filter ( |request| request. as_str () == path) 121. count (), 1221 , 123"{requests:?}" 124); 125} 126} 127 128# [ test ] 129fn published_packages_do_not_inherit_application_maps_or_escape_their_packages () { 130let server =Registry :: new (); 131 server. package ( 132"@test/bare" , 133"1.0.0" , 134& [( "mod.ts" , "import 'dep';" )], 135& [( "." , "./mod.ts" )], 136); 137 server. package ( 138"@test/escape" , 139"1.0.0" , 140& [( "mod.ts" , "import '../../other/1.0.0/private.ts';" )], 141& [( "." , "./mod.ts" )], 142); 143 server. package ( 144"@test/builtin" , 145"1.0.0" , 146& [( "mod.ts" , "import 'node:fs';" )], 147& [( "." , "./mod.ts" )], 148); 149let dir = tempfile:: tempdir (). unwrap (); 150 fs:: write ( dir. path (). join ( "avril.conf" ), "imports: {dep: './dep.ts'}" ). unwrap (); 151 fs:: write ( 152 dir. path (). join ( "dep.ts" ), 153"throw new Error('inherited app map');" , 154) 155. unwrap (); 156for ( package, expected) in [ 157( "bare" , "Unsupported module specifier: dep" ), 158( "escape" , "escapes its registry package" ), 159( "builtin" , "node: builtin modules are not supported" ), 160] { 161 fs:: write ( 162 dir. path (). join ( "main.ts" ), 163format! ( "import 'jsr:@test/{package}@1';" ), 164) 165. unwrap (); 166let output =run ( & server, dir. path (), & []); 167assert! ( !output. status . success ()); 168assert! ( 169String :: from_utf8_lossy ( & output. stderr ). contains ( expected), 170"{}" , 171String :: from_utf8_lossy ( & output. stderr ) 172); 173} 174assert! ( 175 !server 176. requests 177. lock () 178. unwrap () 179. iter () 180. any ( |path| path. contains ( "other" )) 181); 182} 183 184# [ test ] 185fn package_acquisition_does_not_grant_guest_network_or_filesystem_access () { 186let server =Registry :: new (); 187 server. package ( 188"@test/pkg" , 189"1.0.0" , 190& [( "mod.ts" , "export const value = 42;" )], 191& [( "." , "./mod.ts" )], 192); 193let dir = tempfile:: tempdir (). unwrap (); 194 fs:: write ( dir. path (). join ( "main.ts" ), format! ( "import {{value}} from 'jsr:@test/pkg@1'; import {{open,errors}} from 'avril:std'; console.log(value); try {{await fetch('{}'); throw new Error('net allowed');}} catch(e) {{console.log(e.message.includes('PermissionDenied'));}} try {{await open('./cache'); throw new Error('read allowed');}} catch(e) {{console.log(e instanceof errors.PermissionDenied);}}" , server. url )). unwrap (); 195let grant =format! ( "--allow-js-exec={}" , server. url ); 196let output =run ( & server, dir. path (), & [ "-0" , & grant]); 197assert_eq! ( success ( & output), "42\ntrue\ntrue\n" ); 198} 199 200# [ test ] 201fn denied_or_revoked_remote_imports_do_not_download_code () { 202let server =Registry :: new (); 203 server. package ( 204"@test/pkg" , 205"1.0.0" , 206& [( "mod.ts" , "export {};" )], 207& [( "." , "./mod.ts" )], 208); 209let dir = tempfile:: tempdir (). unwrap (); 210 fs:: write ( 211 dir. path (). join ( "main.ts" ), 212"await import('jsr:@test/pkg@1');" , 213) 214. unwrap (); 215let output =run ( & server, dir. path (), & [ "-0" ]); 216assert! ( !output. status . success ()); 217assert! ( String :: from_utf8_lossy ( & output. stderr ). contains ( "PermissionDenied: js-exec" )); 218assert! ( server. requests . lock (). unwrap (). is_empty ()); 219 fs:: write ( dir. path (). join ( "main.ts" ), "import {pledge} from 'avril:std'; const pending = import('jsr:@test/pkg@1'); pledge({jsExec: false}); try {await pending; throw new Error('allowed');} catch(e) {console.log(e.message.includes('PermissionDenied'));}" ). unwrap (); 220assert_eq! ( success ( & run ( & server, dir. path (), & [])), "true\n" ); 221assert! ( server. requests . lock (). unwrap (). is_empty ()); 222} 223 224# [ test ] 225fn cached_remote_modules_obey_live_file_denials_and_reject_encoded_aliases () { 226let server =Registry :: new (); 227 server. package ( 228"@test/pkg" , 229"1.0.0" , 230& [( 231"mod.ts" , 232"export const url = import.meta.url; export const alias = () => import('./mod%2Ets');" , 233)], 234& [( "." , "./mod.ts" )], 235); 236let dir = tempfile:: tempdir (). unwrap (); 237 fs:: write ( dir. path (). join ( "main.ts" ), "import {pledge} from 'avril:std'; const a = await import('jsr:@test/pkg@1'); try {await a.alias(); throw new Error('alias allowed');} catch(e) {console.log(e.message.includes('canonical'));} pledge({jsExec: {deny: [a.url]}}); try {await import('jsr:@test/pkg@1'); throw new Error('allowed');} catch(e) {console.log(e.message.includes('PermissionDenied'));} try {await import(a.url); throw new Error('allowed');} catch(e) {console.log(e.message.includes('PermissionDenied'));}" ). unwrap (); 238assert_eq! ( 239success ( & run ( & server, dir. path (), & [])), 240"true\ntrue\ntrue\n" 241); 242} 243 244# [ test ] 245fn metadata_version_denials_prevent_fetching_that_versions_manifest () { 246let server =Registry :: new (); 247 server. package ( 248"@test/pkg" , 249"1.0.0" , 250& [( "mod.ts" , "export {};" )], 251& [( "." , "./mod.ts" )], 252); 253let dir = tempfile:: tempdir (). unwrap (); 254 fs:: write ( dir. path (). join ( "main.ts" ), "import 'jsr:@test/pkg@1';" ). unwrap (); 255let deny =format! ( "--deny-js-exec={}@test/pkg/1.0.0/" , server. url ); 256let output =run ( & server, dir. path (), & [ & deny]); 257assert! ( !output. status . success ()); 258assert_eq! ( 259* server. requests . lock (). unwrap (), 260vec! [ "/@test/pkg/meta.json" ] 261); 262} 263 264# [ test ] 265fn tooling_prepares_without_execution_and_vendored_runs_are_offline_reproducible () { 266let server =Registry :: new (); 267 server. package ( 268"@test/pkg" , 269"1.0.0" , 270& [( "mod.ts" , "export const value = 1;" )], 271& [( "." , "./mod.ts" )], 272); 273 server. package ( 274"@test/lazy" , 275"1.0.0" , 276& [( "mod.ts" , "export const value = 'lazy';" )], 277& [( "." , "./mod.ts" )], 278); 279let dir = tempfile:: tempdir (). unwrap (); 280 fs:: write ( dir. path (). join ( "main.ts" ), "import {value} from 'jsr:@test/pkg@^1'; console.log(value, (await import('jsr:@test/' + 'lazy@1')).value);" ). unwrap (); 281assert_eq! ( success ( & run ( & server, dir. path (), & [ "cache" ])), "" ); 282let output =run ( & server, dir. path (), & [ "info" ]); 283let graph: serde_json:: Value = serde_json:: from_str ( & success ( & output)). unwrap (); 284assert_eq! ( graph[ "modules" ]. as_object (). unwrap (). len (), 2 ); 285 server. package ( 286"@test/pkg" , 287"1.1.0" , 288& [( "mod.ts" , "export const value = 2;" )], 289& [( "." , "./mod.ts" )], 290); 291assert_eq! ( success ( & run ( & server, dir. path (), & [ "update" ])), "" ); 292assert_eq! ( 293success ( & run ( 294& server, 295 dir. path (), 296& [ "vendor" , "--include=jsr:@test/lazy@1" ] 297)), 298"" 299); 300assert! ( dir. path (). join ( "vendor/@test/pkg/1.1.0/mod.ts" ). is_file ()); 301assert! ( dir. path (). join ( "vendor/@test/lazy/1.0.0/mod.ts" ). is_file ()); 302 fs:: remove_dir_all ( dir. path (). join ( "cache" )). unwrap (); 303 server. requests . lock (). unwrap (). clear (); 304assert_eq! ( 305success ( & run ( 306& server, 307 dir. path (), 308& [ "--offline" , "--frozen" , "--vendor=vendor" ] 309)), 310"2 lazy\n" 311); 312assert! ( server. requests . lock (). unwrap (). is_empty ()); 313} 314 315# [ test ] 316fn remote_typescript_stacks_use_registry_source_urls_and_original_lines () { 317let server =Registry :: new (); 318 server. package ( "@test/throws" , "1.0.0" , & [( "mod.ts" , "type Erased = {\n field: string;\n};\nexport function fail(): never {\n throw new Error('remote');\n}\n" )], & [( "." , "./mod.ts" )]); 319let dir = tempfile:: tempdir (). unwrap (); 320 fs:: write ( 321 dir. path (). join ( "main.ts" ), 322"import {fail} from 'jsr:@test/throws@1'; fail();" , 323) 324. unwrap (); 325let output =run ( & server, dir. path (), & []); 326assert! ( !output. status . success ()); 327assert! ( 328String :: from_utf8_lossy ( & output. stderr ). contains ( & format! ( 329"at fail ({}@test/throws/1.0.0/mod.ts:5:8)" , 330 server. url 331)), 332"{}" , 333String :: from_utf8_lossy ( & output. stderr ) 334); 335} 336 337# [ test ] 338fn unchanged_locks_are_not_rewritten_and_guest_grants_do_not_move_the_root () { 339let server =Registry :: new (); 340 server. package ( 341"@test/pkg" , 342"1.0.0" , 343& [( "mod.ts" , "export const value = 1;" )], 344& [( "." , "./mod.ts" )], 345); 346let dir = tempfile:: tempdir (). unwrap (); 347 fs:: create_dir ( dir. path (). join ( "src" )). unwrap (); 348 fs:: write ( dir. path (). join ( "avril.conf" ), "" ). unwrap (); 349 fs:: write ( 350 dir. path (). join ( "src/main.ts" ), 351"import {value} from 'jsr:@test/pkg@^1'; console.log(value);" , 352) 353. unwrap (); 354let execute = |args : & [ & str ] |{ 355Command :: new ( env! ( "CARGO_BIN_EXE_avril" )) 356. current_dir ( dir. path ()) 357. args ( 358 args. first () 359. is_none_or ( |arg| arg. starts_with ( '-' )) 360. then_some ( "run" ), 361) 362. args ( args) 363. arg ( format! ( "--jsr-registry={}" , server. url )) 364. arg ( "--cache-dir=cache" ) 365. arg ( "src/main.ts" ) 366. output () 367. unwrap () 368}; 369assert_eq! ( success ( & execute ( & [])), "1\n" ); 370let lock = dir. path (). join ( "avril.lock" ); 371let original = fs:: read ( & lock). unwrap (); 372let modified = fs:: metadata ( & lock). unwrap (). modified (). unwrap (); 373 server. package ( 374"@test/pkg" , 375"1.1.0" , 376& [( "mod.ts" , "export const value = 2;" )], 377& [( "." , "./mod.ts" )], 378); 379 server. requests . lock (). unwrap (). clear (); 380let grant =format! ( "--allow-js-exec={}" , server. url ); 381assert_eq! ( success ( & execute ( & [ "-0" , & grant])), "1\n" ); 382assert_eq! ( success ( & execute ( & [ "-0" , & grant, "--frozen" ])), "1\n" ); 383assert! ( !dir. path (). join ( "src/avril.lock" ). exists ()); 384assert_eq! ( fs:: read ( & lock). unwrap (), original); 385assert_eq! ( fs:: metadata ( & lock). unwrap (). modified (). unwrap (), modified); 386assert! ( server. requests . lock (). unwrap (). is_empty ()); 387} 388 389# [ test ] 390fn vendoring_can_seed_private_computed_files_and_ignores_unusable_graph_hints () { 391let server =Registry :: new (); 392 server. package ( 393"@test/private" , 394"1.0.0" , 395& [ 396( 397"mod.ts" , 398"export const load = () => import('./' + 'hidden.ts');" , 399), 400( "hidden.ts" , "export const value = 42;" ), 401], 402& [( "." , "./mod.ts" )], 403); 404let mut metadata: serde_json:: Value = serde_json:: from_slice ( 405 server 406. files 407. lock () 408. unwrap () 409. get ( "/@test/private/1.0.0_meta.json" ) 410. unwrap (), 411) 412. unwrap (); 413 metadata[ "moduleGraph2" ] =json! ([ "unknown hint format" ]); 414 metadata[ "moduleGraph1" ] =json! ({ "ignored" : true }); 415 server. files . lock (). unwrap (). insert ( 416"/@test/private/1.0.0_meta.json" . into (), 417 serde_json:: to_vec ( & metadata). unwrap (), 418); 419let dir = tempfile:: tempdir (). unwrap (); 420 fs:: write ( 421 dir. path (). join ( "main.ts" ), 422"import {load} from 'jsr:@test/private@1'; console.log((await load()).value);" , 423) 424. unwrap (); 425success ( & run ( 426& server, 427 dir. path (), 428& [ 429"vendor" , 430"--include-from=jsr:@test/private@1" , 431"--include=./hidden.ts" , 432], 433)); 434assert! ( 435 dir. path () 436. join ( "vendor/@test/private/1.0.0/hidden.ts" ) 437. exists () 438); 439 fs:: remove_dir_all ( dir. path (). join ( "cache" )). unwrap (); 440 server. requests . lock (). unwrap (). clear (); 441assert_eq! ( 442success ( & run ( 443& server, 444 dir. path (), 445& [ "--offline" , "--frozen" , "--vendor=vendor" ] 446)), 447"42\n" 448); 449assert! ( server. requests . lock (). unwrap (). is_empty ()); 450} 451 452# [ test ] 453fn named_local_packages_do_not_override_explicit_jsr_registry_requests () { 454let server =Registry :: new (); 455 server. package ( 456"@test/library" , 457"1.0.0" , 458& [( "mod.ts" , "export const value = 'registry';" )], 459& [( "." , "./mod.ts" )], 460); 461let dir = tempfile:: tempdir (). unwrap (); 462 fs:: create_dir ( dir. path (). join ( "lib" )). unwrap (); 463 fs:: write ( 464 dir. path (). join ( "avril.conf" ), 465"deps: [{from: './lib', name: '@test/library'}]" , 466) 467. unwrap (); 468 fs:: write ( 469 dir. path (). join ( "lib/avril.conf" ), 470"name: '@test/library', version: '9.0.0', exports: './mod.ts'" , 471) 472. unwrap (); 473 fs:: write ( 474 dir. path (). join ( "lib/mod.ts" ), 475"export const value = 'local';" , 476) 477. unwrap (); 478 fs:: write ( dir. path (). join ( "main.ts" ), "import * as local from '@test/library'; import * as remote from 'jsr:@test/library@1'; console.log(local.value, remote.value, local !== remote);" ). unwrap (); 479assert_eq! ( 480success ( & run ( & server, dir. path (), & [])), 481"local registry true\n" 482); 483} 484 485# [ test ] 486fn jsr_dependencies_bind_their_package_name_or_override_and_stay_private () { 487let server =Registry :: new (); 488 server. package ( 489"@test/library" , 490"1.0.0" , 491& [ 492( "mod.ts" , "export const value = 'root';" ), 493( "sub.ts" , "export const value = 'sub';" ), 494], 495& [( "." , "./mod.ts" ), ( "./sub" , "./sub.ts" )], 496); 497 server. package ( 498"@test/value" , 499"2.0.0" , 500& [( "mod.ts" , "export const value = 'two';" )], 501& [( "." , "./mod.ts" )], 502); 503let dir = tempfile:: tempdir (). unwrap (); 504 fs:: create_dir ( dir. path (). join ( "local" )). unwrap (); 505 fs:: write ( 506 dir. path (). join ( "avril.conf" ), 507"deps: ['jsr:@test/library@^1', {from: 'jsr:@test/value@^2', name: 'value'}, './local']" , 508) 509. unwrap (); 510 fs:: write ( 511 dir. path (). join ( "local/avril.conf" ), 512"name: 'local', exports: './mod.ts'" , 513) 514. unwrap (); 515 fs:: write ( 516 dir. path (). join ( "local/mod.ts" ), 517"export async function isolated() {try {await import('value'); return false;} catch(e) {return e.message.includes('Unsupported module specifier');}}" , 518) 519. unwrap (); 520 fs:: write ( dir. path (). join ( "main.ts" ), "import {value as root} from '@test/library'; import {value as sub} from '@test/library/sub'; import * as direct from 'jsr:@test/library@^1'; import {value} from 'value'; import {isolated} from 'local'; console.log(root, sub, value, direct.value === root, await isolated()); for (const name of ['@test/value', '@test/library/missing']) {try {await import(name); console.log('allowed');} catch(e) {console.log(e.message.includes(name === '@test/value' ? 'Unsupported module specifier' : 'no export'));}}" ). unwrap (); 521assert_eq! ( 522success ( & run ( & server, dir. path (), & [])), 523"root sub two true true\ntrue\ntrue\n" 524); 525let lock = fixture:: read_lock ( dir. path ()); 526assert_eq! ( lock[ "specifiers" ][ "jsr:@test/library@^1" ], "1.0.0" ); 527let info: serde_json:: Value = 528 serde_json:: from_str ( & success ( & run ( & server, dir. path (), & [ "info" ]))). unwrap (); 529let main =& info[ "modules" ][ dir. path (). join ( "main.ts" ). to_str (). unwrap ()]; 530assert_eq! ( 531 main[ "package" ][ "dependencies" ][ "@test/library" ], 532"jsr:@test/library@^1" 533); 534assert_eq! ( 535 main[ "package" ][ "dependencies" ][ "value" ], 536"jsr:@test/value@^2" 537); 538assert_eq! ( 539 main[ "imports" ][ "@test/library/sub" ], 540format! ( "{}@test/library/1.0.0/sub.ts" , server. url ) 541); 542} 543 544# [ test ] 545fn direct_and_binding_imports_agree_on_selection_locking_and_modules () { 546let server =Registry :: new (); 547for ( version, value, sub) in [( "1.0.0" , "one" , "sub-one" ), ( "1.1.0" , "two" , "sub-two" )] { 548 server. package ( 549"@test/parity" , 550 version, 551& [ 552( "mod.ts" , & format! ( "export const value = '{value}';" )), 553( "sub.ts" , & format! ( "export const sub = '{sub}';" )), 554], 555& [( "." , "./mod.ts" ), ( "./sub" , "./sub.ts" )], 556); 557} 558let direct = tempfile:: tempdir (). unwrap (); 559 fs:: write ( 560 direct. path (). join ( "main.ts" ), 561"import {value} from 'jsr:@test/parity@^1'; import {sub} from 'jsr:@test/parity@^1/sub'; console.log(value, sub);" , 562) 563. unwrap (); 564let bound = tempfile:: tempdir (). unwrap (); 565 fs:: write ( 566 bound. path (). join ( "avril.conf" ), 567"deps: ['jsr:@test/parity@^1']" , 568) 569. unwrap (); 570 fs:: write ( 571 bound. path (). join ( "main.ts" ), 572"import {value} from '@test/parity'; import {sub} from '@test/parity/sub'; console.log(value, sub);" , 573) 574. unwrap (); 575let direct_stdout =success ( & run ( & server, direct. path (), & [])); 576let bound_stdout =success ( & run ( & server, bound. path (), & [])); 577// The caret range resolves identically through both routes. 578assert_eq! ( direct_stdout, bound_stdout); 579assert_eq! ( direct_stdout, "two sub-two\n" ); 580let direct_lock = fixture:: read_lock ( direct. path ()); 581let bound_lock = fixture:: read_lock ( bound. path ()); 582assert_eq! ( direct_lock[ "specifiers" ], bound_lock[ "specifiers" ]); 583assert_eq! ( direct_lock[ "specifiers" ][ "jsr:@test/parity@^1" ], "1.1.0" ); 584assert_eq! ( direct_lock, bound_lock); 585let direct_info: serde_json:: Value = 586 serde_json:: from_str ( & success ( & run ( & server, direct. path (), & [ "info" ]))). unwrap (); 587let bound_info: serde_json:: Value = 588 serde_json:: from_str ( & success ( & run ( & server, bound. path (), & [ "info" ]))). unwrap (); 589let direct_main = 590& direct_info[ "modules" ][ direct. path (). join ( "main.ts" ). to_str (). unwrap ()][ "imports" ]; 591let bound_main = 592& bound_info[ "modules" ][ bound. path (). join ( "main.ts" ). to_str (). unwrap ()][ "imports" ]; 593assert_eq! ( 594 direct_main[ "jsr:@test/parity@^1" ], 595 bound_main[ "@test/parity" ] 596); 597assert_eq! ( 598 direct_main[ "jsr:@test/parity@^1/sub" ], 599 bound_main[ "@test/parity/sub" ] 600); 601}