forked from wangziqi/gongxue-base
feat: establish production SaaS foundation
This commit is contained in:
@@ -2,7 +2,27 @@ 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',
|
||||
@@ -18,6 +38,9 @@ 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',
|
||||
@@ -50,4 +73,100 @@ assert.throws(
|
||||
/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');
|
||||
|
||||
Reference in New Issue
Block a user