Files
tvbox_collect/jaychouqq/yingshi/js7/demo_test.js
T
2026-06-29 03:41:14 +00:00

321 lines
12 KiB
JavaScript

// import * as drpyS from '../../libs/drpyS.js'; // REMOVED: Static import causes logs to print before we can intercept
import path from 'path';
import {fileURLToPath} from 'url';
import {existsSync} from 'fs';
import { fastify } from '../../controllers/fastlogger.js';
import { format } from 'util';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Mock server constants
const PORT = 5757;
const WsPORT = 57575;
const protocol = 'http';
const hostname = 'localhost:5757';
// Helper for clean output (bypassing fastify logger for report)
const print = (...args) => {
process.stdout.write(format(...args) + '\n');
};
// Override console and global log to use fastify to suppress interference logs
// This ensures that logs from imported modules (drpyS, etc.) are redirected to fastify log
globalThis.log = (...args) => fastify.log.info(args.join(' '));
console.log = (...args) => fastify.log.info(args.join(' '));
console.info = (...args) => fastify.log.info(args.join(' '));
console.debug = (...args) => fastify.log.debug(args.join(' '));
console.warn = (...args) => fastify.log.warn(args.join(' '));
console.error = (...args) => fastify.log.error(args.join(' '));
// Global drpyS variable to be initialized dynamically
let drpyS;
function getEnv(moduleName, query = {}) {
const moduleExt = query.extend || '';
const requestHost = `${protocol}://${hostname}`;
const publicUrl = `${protocol}://${hostname}/public/`;
const jsonUrl = `${protocol}://${hostname}/json/`;
const httpUrl = `${protocol}://${hostname}/http`;
const imageApi = `${protocol}://${hostname}/image`;
const mediaProxyUrl = `${protocol}://${hostname}/mediaProxy`;
const webdavProxyUrl = `${protocol}://${hostname}/webdav/`;
const ftpProxyUrl = `${protocol}://${hostname}/ftp/`;
const hostUrl = `${hostname.split(':')[0]}`;
const wsName = hostname.replace(`:${PORT}`, `:${WsPORT}`);
const fServer = null;
const proxyUrl = `${protocol}://${hostname}/proxy/${moduleName}/?do=${query.do || 'ds'}&extend=${encodeURIComponent(moduleExt)}`;
const getProxyUrl = function () {
return proxyUrl
};
const env = {
requestHost,
proxyUrl,
publicUrl,
jsonUrl,
httpUrl,
imageApi,
mediaProxyUrl,
webdavProxyUrl,
ftpProxyUrl,
hostUrl,
hostname,
wsName,
fServer,
getProxyUrl,
ext: moduleExt
};
env.getRule = async function (_moduleName) {
const _modulePath = path.join(__dirname, '../js', `${_moduleName}.js`);
if (!existsSync(_modulePath)) {
return null;
}
const _env = getEnv(_moduleName);
const RULE = await drpyS.getRuleObject(_modulePath, _env);
RULE.callRuleFn = async function (_method, _args) {
let invokeMethod = null;
switch (_method) {
case 'class_parse': invokeMethod = 'home'; break;
case '推荐': invokeMethod = 'homeVod'; break;
case '一级': invokeMethod = 'category'; break;
case '二级': invokeMethod = 'detail'; break;
case '搜索': invokeMethod = 'search'; break;
case 'lazy': invokeMethod = 'play'; break;
case 'proxy_rule': invokeMethod = 'proxy'; break;
case 'action': invokeMethod = 'action'; break;
}
if (!invokeMethod) {
if (typeof RULE[_method] !== 'function') {
return null
} else {
return await RULE[_method](..._args);
}
}
return await drpyS[invokeMethod](_modulePath, _env, ..._args);
};
return RULE;
}
return env;
}
// Test Status Reporter
const stats = {
results: [],
pass(name) { this.results.push({name, status: 'PASS'}); },
fail(name, err) { this.results.push({name, status: 'FAIL', error: err}); },
skip(name, reason) { this.results.push({name, status: 'SKIP', reason}); },
summary() {
const total = this.results.length;
const passed = this.results.filter(r => r.status === 'PASS').length;
const failed = this.results.filter(r => r.status === 'FAIL').length;
const skipped = this.results.filter(r => r.status === 'SKIP').length;
print('\n\n==============================================');
print(' TEST SUMMARY REPORT ');
print('==============================================');
print(`Total Steps : ${total}`);
print(`Passed : ${passed}`);
print(`Failed : ${failed}`);
print(`Skipped : ${skipped}`);
print('----------------------------------------------');
this.results.forEach(r => {
let statusIcon = r.status === 'PASS' ? '✅' : (r.status === 'FAIL' ? '❌' : '⚠️');
let msg = `${statusIcon} [${r.status}] ${r.name}`;
if (r.error) msg += ` - Error: ${r.error}`;
if (r.reason) msg += ` - Reason: ${r.reason}`;
print(msg);
});
print('==============================================\n');
}
};
(async () => {
// Dynamic import to ensure logs are intercepted
drpyS = await import('../../libs/drpyS.js');
// Parse command line arguments
const args = process.argv.slice(2);
const params = {};
for (let i = 0; i < args.length; i++) {
if (args[i] === '-m' || args[i] === '--module') {
params.module = args[i + 1];
i++;
} else if (args[i] === '-e' || args[i] === '--extend') {
params.extend = args[i + 1];
i++;
}
}
if (!params.module) {
print('Usage: node demo_test.js -m <module_name> [-e <extend_params>]');
print('Example: node demo_test.js -m "七猫小说[书]" -e "token=123"');
process.exit(1);
}
// 设置目标模块路径
const moduleName = params.module;
const modulePath = path.join(__dirname, `../js/${moduleName}.js`);
// Create environment
const env = getEnv(moduleName, { extend: params.extend });
// Shared variables for test dependencies
let homeResult;
let cateResult;
let searchResult;
let detailResult;
let detailUrl = '';
try {
fastify.log.info('Initializing module...'); // Changed from print to fastify.log.info
await drpyS.init(modulePath);
// 1. 测试 Home/Category (分类列表)
try {
print('\n=== Testing Home/Category ===');
homeResult = await drpyS.home(modulePath, env);
print('Home Result:', JSON.stringify(homeResult.class.slice(0, 3))); // 只打印前3个分类
if (homeResult && homeResult.class && homeResult.class.length > 0) {
stats.pass('Home/Category');
} else {
throw new Error('No classes returned');
}
} catch (e) {
stats.fail('Home/Category', e.message);
// If home fails, we can't test category, but maybe search works?
}
// 2. 测试 Category Content (一级 - 分类内容)
if (homeResult && homeResult.class && homeResult.class.length > 0) {
try {
// 使用第一个分类的 ID
const classId = homeResult.class[0].type_id;
print(`\n=== Testing Category Content (class_id=${classId}) ===`);
// category(filePath, env, tid, pg, filter, extend)
cateResult = await drpyS.category(modulePath, env, classId, 1);
print('Category Result Count:', cateResult.list.length);
if (cateResult.list.length > 0) {
print('First Item:', cateResult.list[0]);
stats.pass('Category Content');
} else {
stats.fail('Category Content', 'No items in category');
}
} catch (e) {
stats.fail('Category Content', e.message);
}
} else {
stats.skip('Category Content', 'Dependency failed: Home/Category');
}
// 3. 测试 Search (搜索)
try {
const keyword = '剑来';
print(`\n=== Testing Search (keyword=${keyword}) ===`);
searchResult = await drpyS.search(modulePath, env, keyword);
print('Search Result Count:', searchResult.list.length);
if (searchResult.list.length > 0) {
print('First Search Item:', searchResult.list[0]);
stats.pass('Search');
} else {
stats.fail('Search', 'No search results found');
}
} catch (e) {
stats.fail('Search', e.message);
}
// 4. 测试 Detail (二级 - 详情)
// 优先使用搜索结果中的 URL (vod_id),如果没有则尝试分类结果
if (searchResult && searchResult.list && searchResult.list.length > 0) {
detailUrl = searchResult.list[0].vod_id;
} else if (cateResult && cateResult.list && cateResult.list.length > 0) {
detailUrl = cateResult.list[0].vod_id;
}
if (detailUrl) {
try {
print(`\n=== Testing Detail (url=${detailUrl}) ===`);
const detailOrList = await drpyS.detail(modulePath, env, [detailUrl]);
if (detailOrList.list && Array.isArray(detailOrList.list)) {
detailResult = detailOrList.list[0];
} else if (Array.isArray(detailOrList)) {
detailResult = detailOrList[0];
} else {
detailResult = detailOrList;
}
if (!detailResult) {
throw new Error('Detail result is empty or invalid');
}
print('Detail Result:', {
vod_name: detailResult.vod_name,
vod_play_from: detailResult.vod_play_from,
// 截取 play_url 防止日志过长
vod_play_url: detailResult.vod_play_url ? (detailResult.vod_play_url.slice(0, 100) + '...') : 'N/A'
});
stats.pass('Detail');
} catch (e) {
stats.fail('Detail', e.message);
}
} else {
stats.skip('Detail', 'No valid URL found from Search or Category results');
}
// 5. 测试 Play (播放/阅读)
if (detailResult && detailResult.vod_play_url) {
try {
print('\n=== Testing Play ===');
// 解析 vod_play_url 获取播放链接
const flags = detailResult.vod_play_from.split('$$$');
const urls = detailResult.vod_play_url.split('$$$');
// 取第一个播放源的第一个章节
const firstFlagUrlList = urls[0].split('#');
const firstChapter = firstFlagUrlList[0];
// 格式: "章节名$参数" -> "第一章$1747899@@123456@@第一章"
const playUrl = firstChapter.split('$')[1];
print(`Playing Chapter: ${firstChapter.split('$')[0]}`);
print(`Play URL Params: ${playUrl}`);
const playResult = await drpyS.play(modulePath, env, flags[0], playUrl);
const logResult = {...playResult};
if (logResult.url && logResult.url.startsWith('novel://')) {
logResult.url = logResult.url.slice(0, 100) + '... (truncated content)';
}
print('Play Result:', logResult);
if (playResult.url || playResult.parse === 0) {
stats.pass('Play');
} else {
stats.fail('Play', 'No play URL or content returned');
}
} catch (e) {
stats.fail('Play', e.message);
}
} else {
const reason = !detailResult ? 'Dependency failed: Detail' : 'No vod_play_url in detail';
stats.skip('Play', reason);
}
} catch (error) {
print('Critical Error during test initialization:', error);
} finally {
stats.summary();
}
})();