Files
web_hgcg_h5/tests/environment.test.mjs
T
谢宇宁andClaude Opus 5.5 8cdbfee08f Initial commit
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 16:23:48 +07:00

112 lines
8.5 KiB
JavaScript

import {test} from 'node:test';
import assert from 'node:assert/strict';
import {mkdtemp,mkdir,writeFile,readFile,realpath,rm} from 'node:fs/promises';
import {tmpdir} from 'node:os';
import {join} from 'node:path';
import {spawnSync} from 'node:child_process';
import ts from 'typescript';
import vm from 'node:vm';
import {nextCommand,runNext} from '../scripts/next-env.mjs';
async function fixture(t){
const root=await mkdtemp(join(tmpdir(),'91-environments-'));t.after(()=>rm(root,{recursive:true,force:true}));
await mkdir(join(root,'config'));
for(const environment of ['development','test','production'])await writeFile(join(root,'config',environment+'.env'),`API_BASE_URL=https://${environment}.api.test\nPORT=21021\nNEXT_PUBLIC_TRACKER_APP_ID=1047\n`);
return root;
}
test('environment defaults, personal overrides and process values use the reference precedence without changing NODE_ENV semantics',async t=>{
const root=await fixture(t);
await writeFile(join(root,'config/test.local.env'),'API_BASE_URL=https://override.api.test\nPORT=21022\nLOCAL_MARKER=yes\n');
const base=nextCommand('test','dev',[],{root,variables:{}});
assert.equal(base.env.API_BASE_URL,'https://override.api.test');assert.equal(base.env.PORT,'21022');assert.equal(base.env.LOCAL_MARKER,'yes');
assert.equal(base.env.APP_ENV,'test');assert.equal(base.env.NODE_ENV,'development');assert.equal(base.env.APP_DIST_DIR,'.next-test');
const processOverride=nextCommand('test','start',[],{root,variables:{API_BASE_URL:'https://process.api.test/',PORT:'31021',APP_ENV:'production',NODE_ENV:'test',APP_DIST_DIR:'outside'}});
assert.equal(processOverride.env.API_BASE_URL,'https://process.api.test');assert.equal(processOverride.env.PORT,'31021');
assert.equal(processOverride.env.APP_ENV,'test');assert.equal(processOverride.env.NODE_ENV,'production');assert.equal(processOverride.env.APP_DIST_DIR,'.next-test');
});
test('development supports legacy .env.local while test and production isolate local overrides',async t=>{
const root=await fixture(t);
await writeFile(join(root,'.env.local'),'API_BASE_URL=https://legacy.api.test\nLEGACY_MARKER=dev-only\n');
await writeFile(join(root,'config/development.local.env'),'API_BASE_URL=https://development-local.api.test\n');
const dev=nextCommand('development','dev',[],{root,variables:{}});
assert.equal(dev.env.API_BASE_URL,'https://development-local.api.test');assert.equal(dev.env.LEGACY_MARKER,'dev-only');
for(const environment of ['test','production']){
const result=nextCommand(environment,'build',[],{root,variables:{}});
assert.equal(result.env.API_BASE_URL,`https://${environment}.api.test`);assert.equal(result.env.LEGACY_MARKER,undefined);
}
});
test('UPSTREAM_API_URL follows profile, local and process precedence while preserving legacy process overrides',async t=>{
const root=await fixture(t);
await writeFile(join(root,'config/test.env'),'UPSTREAM_API_URL=http://127.0.0.1:21111\nPORT=21021\n');
await writeFile(join(root,'config/test.local.env'),'UPSTREAM_API_URL=http://127.0.0.1:21112/\n');
let result=nextCommand('test','start',[],{root,variables:{}});
assert.equal(result.env.UPSTREAM_API_URL,'http://127.0.0.1:21112');assert.equal(result.env.API_BASE_URL,result.env.UPSTREAM_API_URL);
result=nextCommand('test','start',[],{root,variables:{UPSTREAM_API_URL:'https://primary.api.test/',API_BASE_URL:'https://ignored.api.test'}});
assert.equal(result.env.UPSTREAM_API_URL,'https://primary.api.test');assert.equal(result.env.API_BASE_URL,result.env.UPSTREAM_API_URL);
result=nextCommand('test','start',[],{root,variables:{API_BASE_URL:'https://legacy-process.api.test'}});
assert.equal(result.env.UPSTREAM_API_URL,'https://legacy-process.api.test');
for(const UPSTREAM_API_URL of ['', 'javascript:bad', 'https://user:password@api.test', 'https://api.test/?query=1', 'https://api.test/#fragment']){
assert.throws(()=>nextCommand('test','start',[],{root,variables:{UPSTREAM_API_URL}}),/UPSTREAM_API_URL/);
}
});
test('each environment selects its own build directory and supports port/hostname command overrides',async t=>{
const root=await fixture(t);
for(const [environment,directory] of [['development','.next-dev'],['test','.next-test'],['production','.next']]){
for(const command of ['dev','build','start']){
const result=nextCommand(environment,command,[],{root,variables:{}});
assert.equal(result.env.APP_DIST_DIR,directory);assert.equal(result.cwd,root);
assert.equal(result.env.NODE_ENV,command==='dev'?'development':'production');
if(command==='build')assert.deepEqual(result.args.slice(1),['build']);
else assert.deepEqual(result.args.slice(2),['--hostname','0.0.0.0','--port','21021']);
}
}
const explicit=nextCommand('test','dev',['--port','31022','--hostname=127.0.0.1','--webpack'],{root,variables:{}});
assert.deepEqual(explicit.args.slice(1),['dev','--port','31022','--hostname=127.0.0.1','--webpack']);
});
test('current-domain profiles preserve runtime resolution instead of requiring a build-time URL',async t=>{
const root=await fixture(t);
for(const environment of ['test','production']){
await writeFile(join(root,'config',environment+'.env'),'UPSTREAM_API_URL=current\nPORT=21021\n');
for(const command of ['dev','build','start']){
const result=nextCommand(environment,command,[],{root,variables:{}});
assert.equal(result.env.UPSTREAM_API_URL,'current');assert.equal(result.env.API_BASE_URL,'current');
}
const override=nextCommand(environment,'start',[],{root,variables:{UPSTREAM_API_URL:'http://127.0.0.1:21111'}});
assert.equal(override.env.UPSTREAM_API_URL,'http://127.0.0.1:21111');
}
});
test('invalid environment, command, API or port fails before a child process is started',async t=>{
const root=await fixture(t);
for(const [environment,command] of [['staging','dev'],['../test','dev'],['development','deploy']])assert.throws(()=>nextCommand(environment,command,[],{root,variables:{}}),/Usage:/);
for(const API_BASE_URL of ['', 'javascript:bad', 'https://name:password@api.test','https://api.test/#fragment'])assert.throws(()=>nextCommand('test','dev',[],{root,variables:{API_BASE_URL}}),/API_BASE_URL/);
for(const PORT of ['0','65536','not-a-port','-1'])assert.throws(()=>nextCommand('test','dev',[],{root,variables:{PORT}}),/PORT/);
await rm(join(root,'config/test.env'));assert.throws(()=>nextCommand('test','dev',[],{root,variables:{}}),/缺少环境配置/);
});
test('real child execution receives the selected environment, preserves args and propagates its exit status',async t=>{
const root=await fixture(t),bin=join(root,'node_modules/next/dist/bin');await mkdir(bin,{recursive:true});
const output=join(root,'captured.json');
await writeFile(join(bin,'next'),`require('node:fs').writeFileSync(process.env.CAPTURE,JSON.stringify({app:process.env.APP_ENV,node:process.env.NODE_ENV,api:process.env.API_BASE_URL,args:process.argv.slice(2),cwd:process.cwd()}));process.exit(7);`);
assert.equal(await runNext('test','start',['--port','31021'],{root,variables:{CAPTURE:output}}),7);
const saved=JSON.parse(await readFile(output,'utf8'));
assert.deepEqual(saved,{app:'test',node:'production',api:'https://test.api.test',args:['start','--hostname','0.0.0.0','--port','31021'],cwd:await realpath(root)});
});
test('project profiles and Next configuration keep the requested API and port with distinct artifacts',async()=>{
const source=ts.transpileModule(await readFile(new URL('../next.config.ts',import.meta.url),'utf8'),{compilerOptions:{target:ts.ScriptTarget.ES2022,module:ts.ModuleKind.CommonJS}}).outputText;
for(const [environment,directory] of [['development','.next-dev'],['test','.next-test'],['production','.next']]){
const command=nextCommand(environment,'build',[],{variables:{}}),exports={};
vm.runInNewContext(source,{exports,process:{env:command.env,cwd:()=>process.cwd()}});
assert.equal(exports.default.distDir,directory);assert.equal(command.env.UPSTREAM_API_URL,environment==='production'?'http://172.104.56.28:26125':'http://172.104.181.99:21022');assert.equal(command.env.API_BASE_URL,command.env.UPSTREAM_API_URL);assert.equal(command.env.PORT,'21021');assert.equal(command.env.NEXT_PUBLIC_TRACKER_APP_ID,'1047');
const ignored=spawnSync('git',['check-ignore','--no-index',`config/${environment}.local.env`],{encoding:'utf8'});assert.equal(ignored.status,0);
}
const pkg=JSON.parse(await readFile(new URL('../package.json',import.meta.url),'utf8'));
for(const name of ['dev','dev:test','build','build:dev','build:test','start','start:dev','start:test'])assert.match(pkg.scripts[name],/next-env\.mjs/);
});