Files
gongxue-base/scripts/taro-h5-release-manifest-test.js
2026-07-12 19:26:57 +08:00

110 lines
4.4 KiB
JavaScript

import assert from 'node:assert/strict';
import fs from 'node:fs';
import os from 'node:os';
import path from 'node:path';
import { spawnSync } from 'node:child_process';
const repoRoot = process.cwd();
const scriptPath = path.join(repoRoot, 'scripts', 'taro-h5-release-manifest.js');
const distRoot = path.join(repoRoot, 'apps', 'taro', 'dist');
function run(args = []) {
return spawnSync(process.execPath, [scriptPath, '--json', ...args], {
cwd: repoRoot,
encoding: 'utf8',
env: {
PATH: process.env.PATH || '',
Path: process.env.Path || '',
SystemRoot: process.env.SystemRoot || '',
ComSpec: process.env.ComSpec || '',
TEMP: process.env.TEMP || os.tmpdir(),
TMP: process.env.TMP || os.tmpdir(),
},
});
}
function parseJson(result) {
return JSON.parse(result.stdout || '{}');
}
function withRuntimeConfig(portal, config, fn) {
const filePath = path.join(distRoot, portal, 'runtime-config.json');
const existed = fs.existsSync(filePath);
const previous = existed ? fs.readFileSync(filePath, 'utf8') : '';
fs.mkdirSync(path.dirname(filePath), { recursive: true });
fs.writeFileSync(filePath, `${JSON.stringify(config, null, 2)}\n`, 'utf8');
try {
return fn(filePath);
} finally {
if (existed) fs.writeFileSync(filePath, previous, 'utf8');
else fs.rmSync(filePath, { force: true });
}
}
const loose = run();
assert.equal(loose.status, 0, loose.stderr);
const loosePayload = parseJson(loose);
assert.equal(loosePayload.schemaVersion, 1);
assert.equal(loosePayload.summary.portals, 3);
assert.equal(loosePayload.portals.length, 3);
assert.ok(loosePayload.portals.every(item => item.buildCommand.startsWith('npm run build:taro:h5:')));
assert.ok(loosePayload.portals.filter(item => item.dist.exists).every(item => /^[0-9a-f]{64}$/.test(item.dist.treeSha256)));
assert.ok(loosePayload.checks.some(item => item.id === 'build.student.portal_env' && item.status === 'pass'));
const strictMissingRuntime = run(['--require-runtime-config']);
assert.notEqual(strictMissingRuntime.status, 0, 'strict manifest should fail when runtime-config files are missing');
const strictPayload = parseJson(strictMissingRuntime);
assert.ok(
strictPayload.checks.some(item => item.id === 'runtime.student.exists' && item.status === 'fail'),
'missing student runtime config should be a failure',
);
const validConfig = {
portal: 'student',
apiBaseUrl: 'https://api.example.com',
supabaseUrl: 'https://auth.example.com',
supabasePublishableKey: 'sb_publishable_test_key',
tenantCode: '',
};
withRuntimeConfig('h5-student', validConfig, () => {
const withStudentRuntime = run();
assert.equal(withStudentRuntime.status, 0, withStudentRuntime.stderr);
const payload = parseJson(withStudentRuntime);
const student = payload.portals.find(item => item.portal === 'student');
assert.equal(student.runtimeConfig.exists, true);
assert.equal(student.runtimeConfig.tenantCodeMode, 'domain-resolved');
assert.ok(payload.checks.some(item => item.id === 'runtime.student.api_https' && item.status === 'pass'));
});
withRuntimeConfig('h5-student', {
...validConfig,
supabasePublishableKey: 'replace-with-supabase-publishable-key',
}, () => {
const placeholder = run();
assert.notEqual(placeholder.status, 0, 'production manifest should reject placeholder publishable key in dist runtime config');
const payload = parseJson(placeholder);
assert.ok(payload.checks.some(item => item.id === 'runtime.student.publishable_key' && item.status === 'fail'));
});
withRuntimeConfig('h5-student', {
...validConfig,
DATABASE_URL: `postgres${'ql'}://postgres:postgres@example.com/postgres`,
}, () => {
const leakedSecret = run();
assert.notEqual(leakedSecret.status, 0, 'manifest should reject forbidden runtime config keys');
const payload = parseJson(leakedSecret);
assert.ok(payload.checks.some(item => item.id === 'runtime.student.keys' && item.status === 'fail'));
assert.ok(payload.checks.some(item => item.id === 'runtime.student.secrets' && item.status === 'fail'));
});
const outputPath = path.join(os.tmpdir(), `taro-release-manifest-${Date.now()}.json`);
const written = run(['--write', outputPath]);
assert.equal(written.status, 0, written.stderr);
assert.equal(fs.existsSync(outputPath), true);
const writtenPayload = JSON.parse(fs.readFileSync(outputPath, 'utf8'));
assert.equal(writtenPayload.schemaVersion, 1);
fs.rmSync(outputPath, { force: true });
console.log('[PASS] Taro H5 release manifest');