125 行
5.6 KiB
JavaScript
125 行
5.6 KiB
JavaScript
const fs = require('fs');
|
||
const path = require('path');
|
||
const vm = require('vm');
|
||
|
||
let passed = 0;
|
||
let failed = 0;
|
||
|
||
function assert(condition, testName, detail = '') {
|
||
if (condition) {
|
||
console.log(` [PASS] ${testName}`);
|
||
passed++;
|
||
} else {
|
||
console.error(` [FAIL] ${testName} - ${detail}`);
|
||
failed++;
|
||
}
|
||
}
|
||
|
||
console.log("========================================================");
|
||
console.log(" 身份等级 (shopro/commission/level) 页面与权限 TDD 测试");
|
||
console.log("========================================================\n");
|
||
|
||
// 1. 验证 Level.php 控制器存在与安全性
|
||
const levelPhpPath = path.resolve(__dirname, '../application/admin/controller/shopro/commission/Level.php');
|
||
assert(fs.existsSync(levelPhpPath), 'Level.php 控制器文件存在');
|
||
|
||
const levelPhpContent = fs.readFileSync(levelPhpPath, 'utf-8');
|
||
|
||
// 1.1 验证无临时 $noNeedLogin 遗留(后台安全保障)
|
||
assert(!levelPhpContent.includes('protected $noNeedLogin'), '无临时 $noNeedLogin 遗留(必须验证登录态)');
|
||
|
||
// 1.2 验证 $noNeedRight 包含 index 和 select(允许登录管理员正常进入列表)
|
||
const hasNoNeedRightIndex = /protected\s+\$noNeedRight\s*=\s*\[.*'index'.*\];/s.test(levelPhpContent);
|
||
const hasNoNeedRightSelect = /protected\s+\$noNeedRight\s*=\s*\[.*'select'.*\];/s.test(levelPhpContent);
|
||
assert(hasNoNeedRightIndex && hasNoNeedRightSelect, '$noNeedRight 必须包含 index 和 select', '缺少 index 或 select');
|
||
|
||
// 1.3 验证移除所有临时调试探针代码
|
||
assert(!levelPhpContent.includes('test_admin'), '已彻底清除 test_admin 模拟登录调试代码');
|
||
assert(!levelPhpContent.includes('read_err_log'), '已彻底清除 read_err_log 日志探测代码');
|
||
assert(!levelPhpContent.includes('INIT_ERROR'), '已彻底清除临时 INIT_ERROR 诊断代码');
|
||
assert(!levelPhpContent.includes('INDEX_ERROR'), '已彻底清除临时 INDEX_ERROR 诊断代码');
|
||
|
||
// 1.4 验证核心方法完整性
|
||
const requiredMethods = ['_initialize', 'index', 'add', 'edit', 'detail', 'delete', 'select'];
|
||
requiredMethods.forEach(method => {
|
||
assert(levelPhpContent.includes(`public function ${method}`), `包含动作方法: ${method}`);
|
||
});
|
||
|
||
// 2. 验证前端 JS (level.js) 语法与逻辑完整性
|
||
const levelJsPath = path.resolve(__dirname, '../public/assets/js/backend/shopro/commission/level.js');
|
||
assert(fs.existsSync(levelJsPath), 'level.js 前端脚本存在');
|
||
|
||
const levelJsContent = fs.readFileSync(levelJsPath, 'utf-8');
|
||
|
||
let jsSyntaxOk = false;
|
||
let jsSyntaxError = '';
|
||
try {
|
||
new vm.Script(levelJsContent, { filename: 'level.js' });
|
||
jsSyntaxOk = true;
|
||
} catch (e) {
|
||
jsSyntaxError = e.message;
|
||
}
|
||
assert(jsSyntaxOk, 'level.js 语法无 SyntaxError', jsSyntaxError);
|
||
|
||
// 2.1 模拟执行并检查 Controller 结构
|
||
const dummyDefine = function (deps, factory) {
|
||
const $ = () => ({});
|
||
const Backend = { api: {} };
|
||
const Table = {};
|
||
const Form = {};
|
||
return factory($, undefined, Backend, Table, Form);
|
||
};
|
||
|
||
let moduleLoaded = false;
|
||
let controllerObj = null;
|
||
try {
|
||
const sandbox = {
|
||
define: dummyDefine,
|
||
console: console,
|
||
location: { search: '?ref=addtabs' },
|
||
window: {},
|
||
Fast: { api: { ajax: () => {}, open: () => {}, fixurl: (u) => u } },
|
||
Vue: {
|
||
ref: (v) => ({ value: v }),
|
||
reactive: (o) => o,
|
||
onMounted: (cb) => cb(),
|
||
},
|
||
};
|
||
vm.createContext(sandbox);
|
||
controllerObj = vm.runInContext(levelJsContent, sandbox);
|
||
moduleLoaded = !!(controllerObj && controllerObj.index);
|
||
} catch (e) {
|
||
console.error("level.js 模拟加载异常:", e.message);
|
||
}
|
||
assert(moduleLoaded, 'level.js 包含 Controller.index 动作');
|
||
|
||
// 3. 验证视图模板存在且无语法残缺
|
||
const levelHtmlPath = path.resolve(__dirname, '../application/admin/view/shopro/commission/level/index.html');
|
||
assert(fs.existsSync(levelHtmlPath), 'index.html 视图模板文件存在');
|
||
const levelHtmlContent = fs.readFileSync(levelHtmlPath, 'utf-8');
|
||
assert(levelHtmlContent.includes('身份等级'), '模板包含身份等级标题');
|
||
assert(levelHtmlContent.includes('commission_rules?.commission_1'), '模板具备佣金比例安全可选链展示');
|
||
assert(levelHtmlContent.includes('state.makerBonus'), '模板具备创客进阶基数展示');
|
||
|
||
// 4. 验证 deploy.sh 对 runtime 目录的权限保障
|
||
const deployShPath = path.resolve(__dirname, '../scripts/deploy.sh');
|
||
assert(fs.existsSync(deployShPath), 'deploy.sh 部署脚本存在');
|
||
const deployShContent = fs.readFileSync(deployShPath, 'utf-8');
|
||
assert(deployShContent.includes('chown -R ${WEB_USER}:${WEB_USER} "${PROJECT_DIR}/runtime"'), '部署脚本包含 runtime 目录所有权赋予 www 用户');
|
||
assert(deployShContent.includes('chmod -R 777 "${PROJECT_DIR}/runtime"') || deployShContent.includes('chmod -R 775 "${PROJECT_DIR}/runtime"'), '部署脚本包含 runtime 目录写权限');
|
||
|
||
// 5. 验证生产异常模板 think_exception.tpl 干净无调试代码泄露
|
||
const exceptionTplPath = path.resolve(__dirname, '../application/common/view/tpl/think_exception.tpl');
|
||
assert(fs.existsSync(exceptionTplPath), 'think_exception.tpl 存在');
|
||
const exceptionTplContent = fs.readFileSync(exceptionTplPath, 'utf-8');
|
||
assert(!exceptionTplContent.includes('$exception instanceof \\Throwable'), 'think_exception.tpl 已恢复干净,无临时注入调试代码');
|
||
|
||
console.log(`\n测试统计: 通过: ${passed}, 失败: ${failed}`);
|
||
if (failed > 0) {
|
||
console.error(" 自动化测试未全部通过,需要继续修复!");
|
||
process.exit(1);
|
||
} else {
|
||
console.log(" 全部自动化测试通过!");
|
||
process.exit(0);
|
||
}
|