import assert from 'node:assert/strict'; import { pathToFileURL } from 'node:url'; const repoRoot = process.cwd(); globalThis.__TARO_PUBLIC_BUILD_CONFIG__ = { portal: 'student', target: 'weapp', releaseMode: 'production', weappTenantMode: 'fixed', apiBaseUrl: 'https://compiled-api.gongxue100.com', supabaseUrl: 'https://compiled-auth.gongxue100.com', supabasePublishableKey: 'sb_publishable_compiled_key', tenantCode: 'compiled-tenant', }; const envModule = await import(pathToFileURL(`${repoRoot}/apps/taro/src/env.ts`).href); delete globalThis.__TARO_PUBLIC_BUILD_CONFIG__; assert.equal(envModule.appEnv.portal, 'student'); assert.equal(envModule.appEnv.apiBaseUrl, 'https://compiled-api.gongxue100.com'); assert.equal(envModule.appEnv.supabaseUrl, 'https://compiled-auth.gongxue100.com'); assert.equal(envModule.appEnv.supabasePublishableKey, 'sb_publishable_compiled_key'); assert.equal(envModule.appEnv.tenantCode, 'compiled-tenant'); assert.equal(envModule.taroRuntimeEnv(), 'weapp'); assert.equal(envModule.isWeappRuntime(), true); assert.equal(envModule.taroWeappTenantMode(), 'fixed'); envModule.applyRuntimeConfig({ portal: 'tenant-admin', apiBaseUrl: 'https://api.gongxue100.com///', supabaseUrl: 'https://auth.gongxue100.com///', supabasePublishableKey: 'sb_publishable_public_key', tenantCode: 'tenant-a', }); assert.equal(envModule.appEnv.portal, 'tenant-admin'); assert.equal(envModule.appEnv.apiBaseUrl, 'https://api.gongxue100.com'); assert.equal(envModule.appEnv.supabaseUrl, 'https://auth.gongxue100.com'); assert.equal(envModule.appEnv.supabasePublishableKey, 'sb_publishable_public_key'); assert.equal(envModule.appEnv.tenantCode, 'tenant-a'); envModule.applyRuntimeConfig({ tenantCode: '' }); assert.equal(envModule.appEnv.tenantCode, '', 'An empty runtime tenantCode must clear a compiled tenant fallback'); envModule.applyRuntimeConfig({ TARO_APP_PORTAL: 'platform-admin', TARO_APP_API_BASE_URL: 'https://api2.gongxue100.com', }); assert.equal(envModule.appEnv.portal, 'platform-admin'); assert.equal(envModule.appEnv.apiBaseUrl, 'https://api2.gongxue100.com'); envModule.applyRuntimeConfig({ portal: 'invalid-portal', apiBaseUrl: '', }); assert.equal(envModule.appEnv.portal, 'platform-admin'); assert.equal(envModule.appEnv.apiBaseUrl, 'https://api2.gongxue100.com'); assert.throws( () => envModule.applyRuntimeConfig({ apiBaseUrl: 'https://api.gongxue100.com', SUPABASE_SERVICE_ROLE_KEY: 'must-not-ship-to-browser', }), /Forbidden secret key in runtime config: SUPABASE_SERVICE_ROLE_KEY/, ); assert.throws( () => envModule.applyRuntimeConfig({ apiBaseUrl: 'https://api.gongxue100.com', unexpectedFeatureFlag: 'unsafe-drift', }), /Unknown key in runtime config: unexpectedFeatureFlag/, ); globalThis.__TARO_PUBLIC_BUILD_CONFIG__ = { portal: 'student', target: 'h5', releaseMode: 'production', weappTenantMode: '', apiBaseUrl: '', supabaseUrl: '', supabasePublishableKey: '', tenantCode: '', }; globalThis.window = { location: { origin: 'https://student.example.test' }, fetch: async () => { throw new Error('network unavailable'); }, }; const productionH5Url = pathToFileURL(`${repoRoot}/apps/taro/src/env.ts`); productionH5Url.searchParams.set('runtime-config-test', 'production-h5'); const productionH5Env = await import(productionH5Url.href); assert.equal(productionH5Env.appEnv.apiBaseUrl, '', 'Production H5 must not compile a local API fallback'); assert.equal(productionH5Env.taroReleaseMode(), 'production'); await assert.rejects( () => productionH5Env.loadRuntimeConfig(), /Production H5 runtime-config\.json request failed: network unavailable/, 'Production H5 must fail closed when runtime config cannot be fetched', ); globalThis.window.fetch = async () => ({ ok: false, status: 404 }); await assert.rejects( () => productionH5Env.loadRuntimeConfig(), /Production H5 runtime-config\.json request failed with status 404/, 'Production H5 must fail closed when runtime config is missing', ); globalThis.window.fetch = async () => ({ ok: true, status: 200, text: async () => '' }); await assert.rejects( () => productionH5Env.loadRuntimeConfig(), /Production H5 runtime-config\.json is empty or unreadable/, 'Production H5 must fail closed when runtime config is unreadable', ); globalThis.window.fetch = async () => ({ ok: true, status: 200, text: async () => JSON.stringify({ portal: 'student', apiBaseUrl: '' }), }); await assert.rejects( () => productionH5Env.loadRuntimeConfig(), /Production H5 runtime-config\.json apiBaseUrl must be an absolute HTTPS URL and must not use localhost or loopback/, 'Production H5 must fail closed when runtime config omits its API endpoint', ); for (const apiBaseUrl of [ 'http://api.gongxue100.com', 'https://localhost:8787', 'https://127.0.0.1:8787', 'https://[::1]:8787', ]) { globalThis.window.fetch = async () => ({ ok: true, status: 200, text: async () => JSON.stringify({ portal: 'student', apiBaseUrl }), }); await assert.rejects( () => productionH5Env.loadRuntimeConfig(), /must be an absolute HTTPS URL and must not use localhost or loopback/, `Production H5 must reject unsafe API endpoint ${apiBaseUrl}`, ); } globalThis.window.fetch = async () => ({ ok: true, status: 200, text: async () => JSON.stringify({ portal: 'student', apiBaseUrl: 'https://api.gongxue100.com/', supabaseUrl: 'https://auth.gongxue100.com/', supabasePublishableKey: 'sb_publishable_runtime_test', tenantCode: '', }), }); await productionH5Env.loadRuntimeConfig(); assert.equal(productionH5Env.appEnv.apiBaseUrl, 'https://api.gongxue100.com'); assert.equal(productionH5Env.appEnv.tenantCode, '', 'Production H5 must remain domain resolved'); globalThis.window.fetch = async () => ({ ok: true, status: 200, text: async () => JSON.stringify({ portal: 'student', apiBaseUrl: 'https://api.gongxue100.com', tenantCode: 'campus-north', }), }); await assert.rejects( () => productionH5Env.loadRuntimeConfig(), /tenantCode must be empty; tenant is resolved from the browser origin/, 'Production H5 must reject a fixed tenant override', ); delete globalThis.window; delete globalThis.__TARO_PUBLIC_BUILD_CONFIG__; console.log('[PASS] Taro runtime config guardrails');