@@ -23,72 +23,27 @@ const path = require('node:path');
2323const { spawnSync } = require ( 'node:child_process' ) ;
2424
2525const HELPERS = path . join ( __dirname , '..' , 'lib' , 'validation-helpers.js' ) ;
26+ const FAKE_AZ_PRELOAD = path . join ( __dirname , 'helpers' , 'fake-az-preload.js' ) ;
2627// Connection to port 1 is refused immediately, so the challenge probe (when it
2728// is reached at all) resolves fast and deterministically offline.
2829const UNREACHABLE_ENV_URL = 'https://127.0.0.1:1' ;
2930
30- // The fake `az` writes one line per invocation to $FAKE_AZ_LOG, then emulates
31- // just the two subcommands getAuthToken uses:
31+ // The Node preload intercepts `execFileSync('az', ...)`, writes one line per
32+ // invocation to $FAKE_AZ_LOG, then emulates the two subcommands getAuthToken uses:
3233// az account show --query tenantId -o tsv
3334// az account get-access-token --resource <url> [--tenant <id>] ...
3435// $FAKE_AZ_FAIL_TENANTS is a comma-separated list of tenants for which token
3536// acquisition should fail (exit 1), letting a test drive the fallback chain.
36- const FAKE_AZ = `#!/usr/bin/env node
37- const fs = require('fs');
38- const args = process.argv.slice(2);
39- fs.appendFileSync(process.env.FAKE_AZ_LOG, args.join(' ') + '\\n');
40-
41- if (args[0] === 'account' && args[1] === 'show') {
42- process.stdout.write((process.env.FAKE_AZ_ACCOUNT_TENANT || '') + '\\n');
43- process.exit(0);
44- }
45-
46- if (args[0] === 'account' && args[1] === 'get-access-token') {
47- const tenantIndex = args.indexOf('--tenant');
48- const tenant = tenantIndex === -1 ? '' : args[tenantIndex + 1];
49- const failing = (process.env.FAKE_AZ_FAIL_TENANTS || '').split(',').filter(Boolean);
50- if (tenant && failing.includes(tenant)) process.exit(1);
51- process.stdout.write('token-for:' + (tenant || 'active-account') + '\\n');
52- process.exit(0);
53- }
54-
55- process.exit(1);
56- ` ;
57-
58- function makeFakeAz ( t ) {
37+ function makeFakeAzLog ( t ) {
5938 const dir = fs . mkdtempSync ( path . join ( os . tmpdir ( ) , 'fake-az-' ) ) ;
6039 t . after ( ( ) => fs . rmSync ( dir , { recursive : true , force : true } ) ) ;
61- const scriptPath = path . join ( dir , 'az.js' ) ;
62- const azPath = path . join ( dir , 'az' ) ;
63- const azCmdPath = path . join ( dir , 'az.cmd' ) ;
64- fs . writeFileSync ( scriptPath , FAKE_AZ . replace ( / ^ # ! [ ^ \n ] * \n / , '' ) ) ;
65- fs . writeFileSync (
66- azPath ,
67- `#!${ process . execPath } \nrequire(${ JSON . stringify ( scriptPath ) } );\n` ,
68- { mode : 0o755 } ,
69- ) ;
70- fs . writeFileSync (
71- azCmdPath ,
72- `@echo off\r\n"${ process . execPath } " "${ scriptPath } " %*\r\n` ,
73- ) ;
74- return { dir, logPath : path . join ( dir , 'az.log' ) } ;
40+ return path . join ( dir , 'az.log' ) ;
7541}
7642
77- function withPrependedPath ( dir , overrides = { } ) {
78- const env = { ...process . env , ...overrides } ;
79- const pathEntry = Object . entries ( env ) . find ( ( [ key ] ) => key . toLowerCase ( ) === 'path' ) ;
80- const currentPath = pathEntry ?. [ 1 ] ?? '' ;
81- for ( const key of Object . keys ( env ) ) {
82- if ( key . toLowerCase ( ) === 'path' ) delete env [ key ] ;
83- }
84- env . PATH = `${ dir } ${ path . delimiter } ${ currentPath } ` ;
85- return env ;
86- }
87-
88- // Runs getAuthToken in a child process so PATH/env manipulation cannot leak
43+ // Runs getAuthToken in a child process so preload/env manipulation cannot leak
8944// into the test runner, and returns both the token and the az invocation log.
9045function runGetAuthToken ( t , env = { } , explicitTenantId = null ) {
91- const { dir , logPath } = makeFakeAz ( t ) ;
46+ const logPath = makeFakeAzLog ( t ) ;
9247 const script = `
9348 const { getAuthToken } = require(${ JSON . stringify ( HELPERS ) } );
9449 getAuthToken(${ JSON . stringify ( UNREACHABLE_ENV_URL ) } , ${ JSON . stringify ( explicitTenantId ) } )
@@ -98,15 +53,17 @@ function runGetAuthToken(t, env = {}, explicitTenantId = null) {
9853
9954 const result = spawnSync ( process . execPath , [ '-e' , script ] , {
10055 encoding : 'utf8' ,
101- env : withPrependedPath ( dir , {
56+ env : {
57+ ...process . env ,
58+ NODE_OPTIONS : `--require=${ FAKE_AZ_PRELOAD } ` ,
10259 FAKE_AZ_LOG : logPath ,
10360 // Cleared unless a test opts in — the ambient shell may have them set.
10461 POWER_PLATFORM_TENANT_ID : '' ,
10562 DATAVERSE_TENANT_ID : '' ,
10663 FAKE_AZ_ACCOUNT_TENANT : '' ,
10764 FAKE_AZ_FAIL_TENANTS : '' ,
10865 ...env ,
109- } ) ,
66+ } ,
11067 } ) ;
11168
11269 assert . equal ( result . status , 0 , `getAuthToken failed: ${ result . stderr } ` ) ;
0 commit comments