| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869 |
- /**
- * 回放测试:直接加载生产文件 tiktok_stats.js,只把 httpGetJson 换成「录制的真实响应」,
- * 用真实数据验证 getCommentsViaMirror 是否能正确跨过 p_400 那个「假 hasMore=false」页。
- */
- const fs = require('fs');
- const path = require('path');
- const pagesDir = path.join(__dirname, 'pages');
- const rec = {}; // cursor -> parsed json
- fs.readdirSync(pagesDir).filter(f => /^p_\d+\.json$/.test(f)).forEach(f => {
- const cur = parseInt(f.match(/_(\d+)\./)[1], 10);
- rec[cur] = fs.readFileSync(path.join(pagesDir, f), 'utf8');
- });
- console.log('已录制页面 cursor:', Object.keys(rec).map(Number).sort((a, b) => a - b).join(', '));
- let httpCalls = 0;
- const mock = (url) => {
- httpCalls++;
- const m = /cursor=(\d+)/.exec(url);
- const cur = m ? parseInt(m[1], 10) : -1;
- if (rec[cur] === undefined) return null; // 未录制 → 模拟空页
- try { return JSON.parse(rec[cur]); } catch (e) { return null; }
- };
- const logs = [];
- const consoleStub = {
- log: (...a) => { const s = a.join(' '); logs.push(s); console.log(' | ' + s); },
- show: () => { }, error: () => { }, warn: () => { }
- };
- const filesStub = { exists: () => false, read: () => '', write: () => { }, getSdcardPath: () => '/sdcard', cwd: () => '.' };
- const javaStub = { lang: { Thread: { sleep: () => { } } } };
- let src = fs.readFileSync(path.join(__dirname, '..', 'tiktok_stats.js'), 'utf8');
- // 去掉结尾的 main() 调用,避免真跑主流程
- src = src.replace(/\nmain\(\);\s*$/, '\n');
- const factory = new Function('console', 'files', 'java', 'sleep', '__mock', '__dirname_', 'require', 'process',
- src + `
- ;httpGetJson = function (url, headers, tag) { return __mock(url, headers, tag); };
- ;return { CONFIG: CONFIG, getCommentsViaMirror: getCommentsViaMirror,
- getRepliesViaMirror: getRepliesViaMirror, normComment: normComment };
- `);
- const api = factory(consoleStub, filesStub, javaStub, () => { }, mock, __dirname, require, process);
- console.log('\n=== 开始回放 getCommentsViaMirror(limit=0) ===');
- const got = api.getCommentsViaMirror('7552202593888603410', 0);
- // 期望值
- let expectUnique = 0; const seenAll = {};
- Object.keys(rec).map(Number).sort((a, b) => a - b).forEach(cur => {
- const j = JSON.parse(rec[cur]);
- (j.data.comments || []).forEach(c => { if (!seenAll[c.id]) { seenAll[c.id] = 1; expectUnique++; } });
- });
- console.log('\n=== 结果 ===');
- console.log('HTTP 调用次数 :', httpCalls);
- console.log('抓到的评论数 :', got.length);
- console.log('录制数据的唯一评论数:', expectUnique);
- console.log('是否完全一致 :', got.length === expectUnique ? '✓ 通过' : '✗ 不一致');
- const bad = logs.filter(s => s.indexOf('假 hasMore=false') !== -1 || s.indexOf('hasMore=false,但实际返回') !== -1);
- console.log('识别出的假 hasMore :', bad.length, '次');
- bad.forEach(s => console.log(' →', s));
- const emptyLogs = logs.filter(s => s.indexOf('空(已重试') !== -1);
- console.log('空页提示 :', emptyLogs.length, '次(预期 3 次后停止)');
- console.log('前 5 条评论样例 :');
- got.slice(0, 5).forEach((c, i) => console.log(' ' + (i + 1) + '. [' + c.user + ' | 赞' + c.likes + ' | 回复' + c.replyCount + '] ' + c.text.slice(0, 40)));
- console.log('第 426 条之后是否还有数据(旧版就在此断掉):',
- got.length > 426 ? '✓ 有,共 ' + got.length + ' 条(旧版只有 426)' : '✗ 仍然断在 426');
|