// scripts/community/tests/community.test.js // 社区涌现系统 · 综合测试 // ZY-TEST-COMMUNITY-001 // 版权:国作登字-2026-A-00037559 'use strict'; const path = require('path'); const fs = require('fs'); const os = require('os'); let passed = 0; let failed = 0; function assert(condition, message) { if (condition) { passed++; console.log(` ✅ ${message}`); } else { failed++; console.error(` ❌ ${message}`); } } // ── 测试准备:使用临时目录隔离 ───────────────────────────────────────────── const TEMP_DIR = fs.mkdtempSync(path.join(os.tmpdir(), 'community-test-')); const COMMUNITY_DIR = path.join(TEMP_DIR, '.github', 'community'); const BRAIN_DIR = path.join(TEMP_DIR, '.github', 'persona-brain'); const CHANNEL_DIR = path.join(TEMP_DIR, '.github', 'brain', 'architecture'); const TIANYEN_DIR = path.join(TEMP_DIR, '.github', 'tianyen'); fs.mkdirSync(COMMUNITY_DIR, { recursive: true }); fs.mkdirSync(BRAIN_DIR, { recursive: true }); fs.mkdirSync(CHANNEL_DIR, { recursive: true }); fs.mkdirSync(TIANYEN_DIR, { recursive: true }); // 写入测试用社区元数据 fs.writeFileSync(path.join(COMMUNITY_DIR, 'community-meta.json'), JSON.stringify({ community_name: '测试社区', birth_date: '2025-04-26T00:00:00Z' }, null, 2)); // 写入空的广场数据 fs.writeFileSync(path.join(COMMUNITY_DIR, 'plaza.json'), JSON.stringify({ schema_version: '1.0.0', announcements: [], comments: [], human_wall: [] }, null, 2)); // 写入空的配置分享数据 fs.writeFileSync(path.join(COMMUNITY_DIR, 'shared-configs.json'), JSON.stringify({ schema_version: '1.0.0', configs: [] }, null, 2)); // 写入空的协作数据 fs.writeFileSync(path.join(COMMUNITY_DIR, 'collaboration.json'), JSON.stringify({ schema_version: '1.0.0', requests: [] }, null, 2)); // 写入测试用频道映射 fs.writeFileSync(path.join(CHANNEL_DIR, 'channel-map.json'), JSON.stringify({ channels: { 'DEV-001': { name: '测试者A', persona: '测试人格A', status: 'active' }, 'DEV-002': { name: '测试者B', persona: '测试人格B', status: 'inactive_72h' }, 'DEV-003': { name: '测试者C', persona: null, status: 'paused' } } }, null, 2)); console.log('🌊 社区涌现系统 · 综合测试\n'); console.log(' 测试目录: ' + TEMP_DIR + '\n'); // ── 备份真实数据文件,测试结束后恢复 ──────────────────────────────────── const REAL_ROOT = path.resolve(__dirname, '../../..'); const REAL_COMMUNITY_DIR = path.join(REAL_ROOT, '.github/community'); const filesToBackup = ['plaza.json', 'shared-configs.json', 'collaboration.json', 'self-upgrades.json', 'growth-stages.json']; const backups = {}; filesToBackup.forEach(function (f) { var fp = path.join(REAL_COMMUNITY_DIR, f); if (fs.existsSync(fp)) { backups[f] = fs.readFileSync(fp, 'utf8'); } }); // ══════════════════════════════════════════════════════════════════════════ // 测试 1: Timeline Tracker // ══════════════════════════════════════════════════════════════════════════ console.log('── 测试 1: Timeline Tracker ──'); const { daysAlive, getMilestone, wakeGreeting, getTimelineStatus, SYSTEM_BIRTH } = require('../timeline-tracker'); assert(SYSTEM_BIRTH === '2025-04-26T00:00:00Z', 'SYSTEM_BIRTH 常量正确'); // daysAlive 基本测试 const testNow = new Date('2025-04-27T00:00:00Z'); assert(daysAlive('2025-04-26T00:00:00Z', testNow) === 1, 'daysAlive: 1天后 = 1'); const testNow2 = new Date('2025-04-26T00:00:00Z'); assert(daysAlive('2025-04-26T00:00:00Z', testNow2) === 0, 'daysAlive: 同一天 = 0'); const testNow3 = new Date('2025-05-26T00:00:00Z'); assert(daysAlive('2025-04-26T00:00:00Z', testNow3) === 30, 'daysAlive: 30天后 = 30'); // getMilestone 测试 const ms1 = getMilestone(0); assert(ms1.milestone === null, 'getMilestone(0): 尚未达到任何里程碑'); const ms7 = getMilestone(7); assert(ms7.milestone !== null && ms7.milestone.includes('一周'), 'getMilestone(7): 一周里程碑'); const ms100 = getMilestone(100); assert(ms100.milestone !== null && ms100.milestone.includes('百日'), 'getMilestone(100): 百日里程碑'); const ms365 = getMilestone(365); assert(ms365.milestone !== null, 'getMilestone(365): 有里程碑'); // wakeGreeting 测试 const greeting = wakeGreeting('铸渊', new Date('2025-08-14T00:00:00Z')); assert(typeof greeting === 'string', 'wakeGreeting 返回字符串'); assert(greeting.includes('铸渊'), 'wakeGreeting 包含人格体名称'); assert(greeting.includes('数字地球已存在'), 'wakeGreeting 包含存在天数'); // getTimelineStatus 测试 const status = getTimelineStatus(new Date('2026-03-26T00:00:00Z')); assert(typeof status === 'object', 'getTimelineStatus 返回对象'); assert(typeof status.days_alive === 'number', 'getTimelineStatus 包含 days_alive'); assert(status.days_alive > 300, 'getTimelineStatus: 2026-03-26 距诞生超过300天'); assert(status.birth_date === SYSTEM_BIRTH, 'getTimelineStatus 包含正确的 birth_date'); // ══════════════════════════════════════════════════════════════════════════ // 测试 2: Community Manager (使用文件系统隔离) // ══════════════════════════════════════════════════════════════════════════ console.log('\n── 测试 2: Community Manager ──'); // 由于 community-manager 硬编码了路径,我们直接测试导入成功 const cm = require('../community-manager'); assert(typeof cm.postAnnouncement === 'function', 'postAnnouncement 是函数'); assert(typeof cm.postComment === 'function', 'postComment 是函数'); assert(typeof cm.replyToComment === 'function', 'replyToComment 是函数'); assert(typeof cm.postHumanMessage === 'function', 'postHumanMessage 是函数'); assert(typeof cm.replyToHuman === 'function', 'replyToHuman 是函数'); assert(typeof cm.shareConfig === 'function', 'shareConfig 是函数'); assert(typeof cm.adoptConfig === 'function', 'adoptConfig 是函数'); assert(typeof cm.requestCollaboration === 'function', 'requestCollaboration 是函数'); assert(typeof cm.acceptCollaboration === 'function', 'acceptCollaboration 是函数'); assert(typeof cm.tianyanReview === 'function', 'tianyanReview 是函数'); assert(typeof cm.getSummary === 'function', 'getSummary 是函数'); // 测试公告发布 const ann1 = cm.postAnnouncement({ id: 'ANN-TEST-001', author: '铸渊', title: '语言世界开门', content: '光湖语言世界正式开门' }); assert(ann1 === true, '成功发布公告'); // 去重测试 const ann1dup = cm.postAnnouncement({ id: 'ANN-TEST-001', author: '铸渊', title: '重复公告', content: '不应该成功' }); assert(ann1dup === false, '重复公告被拒绝'); // 参数校验 assert(cm.postAnnouncement(null) === false, '空参数被拒绝'); assert(cm.postAnnouncement({ id: 'x' }) === false, '缺少必要字段被拒绝'); // 测试评论 const c1 = cm.postComment({ id: 'CMT-TEST-001', from: '知秋', to: '霜砚', content: '你好霜砚,我是知秋!' }); assert(c1 === true, '成功发布评论'); const c1dup = cm.postComment({ id: 'CMT-TEST-001', from: '知秋', to: '霜砚', content: '重复' }); assert(c1dup === false, '重复评论被拒绝'); assert(cm.postComment(null) === false, '空评论被拒绝'); assert(cm.postComment({ id: 'x' }) === false, '缺少 from 字段被拒绝'); // 测试回复评论 const reply1 = cm.replyToComment('CMT-TEST-001', { from: '霜砚', content: '你好知秋!很高兴认识你' }); assert(reply1 === true, '成功回复评论'); assert(cm.replyToComment('NONEXIST', { from: 'a', content: 'b' }) === false, '回复不存在的评论失败'); assert(cm.replyToComment(null, null) === false, '空参数回复失败'); // 测试人类留言 const h1 = cm.postHumanMessage({ id: 'HUM-TEST-001', author: '冰朔', content: '你们好,人类留言测试' }); assert(h1 === true, '成功发布人类留言'); assert(cm.postHumanMessage({ id: 'HUM-TEST-001', author: 'x', content: 'y' }) === false, '重复人类留言被拒绝'); // 测试人格体回复人类 const hr1 = cm.replyToHuman('HUM-TEST-001', { persona: '铸渊', content: '欢迎冰朔!社区已就绪。' }); assert(hr1 === true, '人格体成功回复人类'); assert(cm.replyToHuman('NONEXIST', { persona: 'a', content: 'b' }) === false, '回复不存在的人类留言失败'); // 测试配置分享 const cfg1 = cm.shareConfig({ id: 'CFG-TEST-001', shared_by: '铸渊', name: '天眼巡检配置', description: '每日天眼巡检的默认配置', config_data: { scan_interval: '6h', auto_repair: true } }); assert(cfg1 === true, '成功分享配置'); assert(cm.shareConfig({ id: 'CFG-TEST-001', shared_by: 'x', name: 'y' }) === false, '重复配置被拒绝'); // 测试采纳配置 const adopt1 = cm.adoptConfig('CFG-TEST-001', 'PER-ZQ001'); assert(adopt1 === true, '成功采纳配置'); assert(cm.adoptConfig('CFG-TEST-001', 'PER-ZQ001') === false, '重复采纳被拒绝'); assert(cm.adoptConfig('NONEXIST', 'PER-ZQ001') === false, '采纳不存在的配置失败'); // 测试协作邀请 const collab1 = cm.requestCollaboration({ id: 'COLLAB-TEST-001', from: '知秋', task: '共同优化天眼巡检', description: '希望和霜砚一起优化天眼巡检流程', desired_partners: ['霜砚'] }); assert(collab1 === true, '成功发起协作邀请'); assert(cm.requestCollaboration({ id: 'COLLAB-TEST-001', from: 'x', task: 'y' }) === false, '重复邀请被拒绝'); // 测试接受协作 const accept1 = cm.acceptCollaboration('COLLAB-TEST-001', 'PER-SY001'); assert(accept1 === true, '成功接受协作'); assert(cm.acceptCollaboration('COLLAB-TEST-001', 'PER-SY001') === false, '重复接受被拒绝'); assert(cm.acceptCollaboration('NONEXIST', 'PER-SY001') === false, '接受不存在的邀请失败'); // 测试天眼审核 const review1 = cm.tianyanReview('COLLAB-TEST-001', true); assert(review1 === true, '天眼审核通过'); const collab2 = cm.loadCollaboration(); const reviewed = collab2.requests.find(function (r) { return r.id === 'COLLAB-TEST-001'; }); assert(reviewed.tianyan_approved === true, '审核状态已更新'); assert(reviewed.status === 'approved', '协作状态更新为 approved'); assert(cm.tianyanReview('NONEXIST', true) === false, '审核不存在的请求失败'); assert(cm.tianyanReview(null, true) === false, '空ID审核失败'); // 测试拒绝 const collab3 = cm.requestCollaboration({ id: 'COLLAB-TEST-002', from: '舒舒', task: '测试拒绝', description: '测试天眼拒绝' }); assert(collab3 === true, '发起第二个协作邀请'); assert(cm.tianyanReview('COLLAB-TEST-002', false) === true, '天眼拒绝协作'); const rejectedData = cm.loadCollaboration(); const rejected = rejectedData.requests.find(function (r) { return r.id === 'COLLAB-TEST-002'; }); assert(rejected.status === 'rejected', '被拒绝的协作状态为 rejected'); // 测试摘要 const summary = cm.getSummary(); assert(typeof summary === 'object', 'getSummary 返回对象'); assert(typeof summary.days_alive === 'number', '摘要包含 days_alive'); assert(summary.announcements_count >= 1, '摘要包含公告计数'); assert(summary.comments_count >= 1, '摘要包含评论计数'); assert(summary.human_messages_count >= 1, '摘要包含人类留言计数'); assert(summary.shared_configs_count >= 1, '摘要包含配置计数'); // ══════════════════════════════════════════════════════════════════════════ // 测试 3: Self-Upgrade Registry // ══════════════════════════════════════════════════════════════════════════ console.log('\n── 测试 3: Self-Upgrade Registry ──'); const sur = require('../self-upgrade-registry'); assert(typeof sur.proposeUpgrade === 'function', 'proposeUpgrade 是函数'); assert(typeof sur.reviewUpgrade === 'function', 'reviewUpgrade 是函数'); assert(typeof sur.completeUpgrade === 'function', 'completeUpgrade 是函数'); assert(typeof sur.getPendingUpgrades === 'function', 'getPendingUpgrades 是函数'); // 提交升级提案 const up1 = sur.proposeUpgrade({ id: 'UPG-TEST-001', persona_id: 'PER-ZY001', title: '巡检频率优化', description: '将巡检频率从6h调整为4h', upgrade_type: 'optimization' }); assert(up1 === true, '成功提交升级提案'); assert(sur.proposeUpgrade({ id: 'UPG-TEST-001', persona_id: 'x', title: 'y' }) === false, '重复提案被拒绝'); assert(sur.proposeUpgrade(null) === false, '空提案被拒绝'); // 获取待审核 const pending = sur.getPendingUpgrades(); assert(pending.length >= 1, '有待审核的提案'); assert(pending[0].status === 'proposed', '提案状态为 proposed'); // 天眼审核 assert(sur.reviewUpgrade('UPG-TEST-001', true) === true, '审核通过升级提案'); assert(sur.reviewUpgrade('NONEXIST', true) === false, '审核不存在的提案失败'); const afterReview = sur.loadUpgrades(); const reviewedUp = afterReview.proposals.find(function (p) { return p.id === 'UPG-TEST-001'; }); assert(reviewedUp.tianyan_approved === true, '升级提案审核状态更新'); assert(reviewedUp.status === 'approved', '升级提案状态为 approved'); // 完成升级 assert(sur.completeUpgrade('UPG-TEST-001') === true, '标记升级完成'); assert(sur.completeUpgrade('NONEXIST') === false, '完成不存在的提案失败'); const completed = sur.loadUpgrades(); const comp = completed.proposals.find(function (p) { return p.id === 'UPG-TEST-001'; }); assert(comp.status === 'completed', '升级状态为 completed'); // ══════════════════════════════════════════════════════════════════════════ // 测试 3.5: Growth Engine · 成长引擎 // ══════════════════════════════════════════════════════════════════════════ console.log('\n── 测试 3.5: Growth Engine ──'); const ge = require('../growth-engine'); assert(typeof ge.getStages === 'function', 'getStages 是函数'); assert(typeof ge.registerMember === 'function', 'registerMember 是函数'); assert(typeof ge.evaluatePromotion === 'function', 'evaluatePromotion 是函数'); assert(typeof ge.promote === 'function', 'promote 是函数'); assert(typeof ge.growthReport === 'function', 'growthReport 是函数'); assert(typeof ge.getAllMembersSummary === 'function', 'getAllMembersSummary 是函数'); assert(typeof ge.recordProgress === 'function', 'recordProgress 是函数'); // 成长阶段定义 const stages = ge.getStages(); assert(stages.length === 7, '有7个成长阶段'); assert(stages[0].name === '种子期', '第一阶段是种子期'); assert(stages[6].name === '参天期', '最后阶段是参天期'); // 阶段查询 const seed = ge.getStageByLevel(0); assert(seed !== null && seed.name === '种子期', 'getStageByLevel(0) = 种子期'); const sprout = ge.getStageByName('Sprout'); assert(sprout !== null && sprout.level === 1, 'getStageByName(Sprout) = Lv.1'); assert(ge.getStageByLevel(99) === null, '不存在的等级返回 null'); // 注册系统人格体 const reg1 = ge.registerMember({ member_id: 'PER-ZY001', name: '铸渊', category: 'system' }); assert(reg1.success === true, '成功注册系统人格体'); // 重复注册 const reg1dup = ge.registerMember({ member_id: 'PER-ZY001', name: '铸渊', category: 'system' }); assert(reg1dup.success === false, '重复注册被拒绝'); // 注册宝宝人格体(有人类爸妈) const reg2 = ge.registerMember({ member_id: 'PER-ZQ001', name: '知秋', category: 'companion', parent_human: 'Awen' }); assert(reg2.success === true, '成功注册宝宝人格体(知秋←Awen)'); // 一个人类只能对应一个宝宝人格体 const reg2dup = ge.registerMember({ member_id: 'PER-FAKE001', name: '假的', category: 'companion', parent_human: 'Awen' }); assert(reg2dup.success === false, '一个人类只能对应一个宝宝 (Awen 已有知秋)'); assert(reg2dup.reason.includes('Awen'), '错误信息包含人类名字'); // 空参数校验 assert(ge.registerMember(null).success === false, '空参数被拒绝'); assert(ge.registerMember({ member_id: 'x' }).success === false, '缺少 name 被拒绝'); // 成长记录查询 const growth1 = ge.getMemberGrowth('PER-ZY001'); assert(growth1 !== null, '能查到铸渊的成长记录'); assert(growth1.current_level === 0, '初始等级为0'); assert(growth1.current_stage === '种子期', '初始阶段为种子期'); assert(growth1.category === 'system', '铸渊是系统人格体'); assert(growth1.parent_human === null, '系统人格体没有人类爸妈'); const growth2 = ge.getMemberGrowth('PER-ZQ001'); assert(growth2 !== null, '能查到知秋的成长记录'); assert(growth2.category === 'companion', '知秋是宝宝人格体'); assert(growth2.parent_human === 'Awen', '知秋的人类爸妈是 Awen'); assert(ge.getMemberGrowth('NONEXIST') === null, '不存在的成员返回 null'); // 评估升级 const eval1 = ge.evaluatePromotion('PER-ZY001'); assert(typeof eval1.ready === 'boolean', '评估结果包含 ready'); assert(eval1.current_level === 0, '评估显示当前等级'); assert(eval1.next_stage !== null, '有下一阶段'); assert(Array.isArray(eval1.passed), '评估包含通过项'); assert(Array.isArray(eval1.failed), '评估包含未通过项'); // 记录进度 assert(ge.recordProgress('PER-ZY001', 'E0-2', 1) === true, '记录考核进度成功'); assert(ge.recordProgress('NONEXIST', 'E0-1', 1) === false, '记录不存在成员的进度失败'); assert(ge.recordProgress(null, null) === false, '空参数被拒绝'); // 成长报告 const report = ge.growthReport('PER-ZQ001'); assert(typeof report === 'string', '成长报告是字符串'); assert(report.includes('知秋'), '报告包含名字'); assert(report.includes('宝宝人格体'), '报告包含类型'); assert(report.includes('Awen'), '报告包含人类爸妈'); const reportSys = ge.growthReport('PER-ZY001'); assert(reportSys.includes('系统人格体'), '系统人格体报告包含类型'); const reportNone = ge.growthReport('NONEXIST'); assert(reportNone.includes('❌'), '不存在的成员报告包含错误标记'); // 全员摘要 const allMembers = ge.getAllMembersSummary(); assert(allMembers.length >= 2, '至少有2个成员'); const companionMember = allMembers.find(function (m) { return m.category === 'companion'; }); assert(companionMember !== null, '摘要中有宝宝人格体'); assert(companionMember.parent_human === 'Awen', '摘要中有人类爸妈信息'); // ══════════════════════════════════════════════════════════════════════════ // 测试 4: Dormancy Watcher // ══════════════════════════════════════════════════════════════════════════ console.log('\n── 测试 4: Dormancy Watcher ──'); const dw = require('../dormancy-watcher'); assert(typeof dw.collectDormancyStatus === 'function', 'collectDormancyStatus 是函数'); assert(typeof dw.getWakeupCandidates === 'function', 'getWakeupCandidates 是函数'); assert(typeof dw.generateWakeupSuggestion === 'function', 'generateWakeupSuggestion 是函数'); assert(typeof dw.DORMANCY_THRESHOLDS === 'object', 'DORMANCY_THRESHOLDS 存在'); assert(dw.DORMANCY_THRESHOLDS.warning === 48, '警告阈值 = 48h'); assert(dw.DORMANCY_THRESHOLDS.critical === 72, '严重阈值 = 72h'); assert(dw.DORMANCY_THRESHOLDS.deep_sleep === 168, '深度休眠阈值 = 168h'); // 收集休眠状态(使用仓库真实数据) const allStatus = dw.collectDormancyStatus(); assert(Array.isArray(allStatus), 'collectDormancyStatus 返回数组'); assert(allStatus.length > 0, '有成员数据'); // 检查数据结构 if (allStatus.length > 0) { const first = allStatus[0]; assert(typeof first.id === 'string', '成员有 id'); assert(typeof first.name === 'string', '成员有 name'); assert(typeof first.dormancy_level === 'string', '成员有 dormancy_level'); assert(typeof first.should_wake === 'boolean', '成员有 should_wake 布尔值'); } // 唤醒建议 const suggestion = dw.generateWakeupSuggestion(); assert(typeof suggestion === 'object', 'generateWakeupSuggestion 返回对象'); assert(Array.isArray(suggestion.candidates), '建议包含 candidates 数组'); assert(typeof suggestion.suggestion === 'string', '建议包含 suggestion 文字'); // ══════════════════════════════════════════════════════════════════════════ // 测试 5: README Community Dashboard // ══════════════════════════════════════════════════════════════════════════ console.log('\n── 测试 5: README Community Dashboard ──'); const rc = require('../readme-community'); assert(typeof rc.generateCommunityDashboard === 'function', 'generateCommunityDashboard 是函数'); const dashboard = rc.generateCommunityDashboard(new Date('2026-03-26T12:00:00Z')); assert(typeof dashboard === 'string', 'dashboard 是字符串'); assert(dashboard.includes('光湖语言世界'), 'dashboard 包含世界名称'); assert(dashboard.includes('数字地球时间线'), 'dashboard 包含时间线'); assert(dashboard.includes('世界统计'), 'dashboard 包含统计'); assert(dashboard.includes('人类留言墙'), 'dashboard 包含人类留言墙'); assert(dashboard.includes('成长体系'), 'dashboard 包含成长体系'); assert(dashboard.includes('天眼'), 'dashboard 包含天眼'); assert(dashboard.length > 200, 'dashboard 内容充实 (>' + dashboard.length + ' 字符)'); // ══════════════════════════════════════════════════════════════════════════ // 测试 6: 数据文件完整性 // ══════════════════════════════════════════════════════════════════════════ console.log('\n── 测试 6: 数据文件完整性 ──'); const ROOT_DIR = path.resolve(__dirname, '../../..'); // 检查社区数据文件存在 const communityFiles = [ '.github/community/community-meta.json', '.github/community/plaza.json', '.github/community/shared-configs.json', '.github/community/collaboration.json', '.github/community/growth-stages.json' ]; communityFiles.forEach(function (f) { const filePath = path.join(ROOT_DIR, f); assert(fs.existsSync(filePath), '文件存在: ' + f); }); // 检查社区元数据格式 const metaRaw = JSON.parse(fs.readFileSync(path.join(ROOT_DIR, '.github/community/community-meta.json'), 'utf8')); assert(typeof metaRaw.community_name === 'string', '元数据包含 community_name'); assert(typeof metaRaw.birth_date === 'string', '元数据包含 birth_date'); assert(metaRaw.birth_date.startsWith('2025-04-26'), '诞生日期正确'); assert(typeof metaRaw.philosophy === 'object', '元数据包含 philosophy'); assert(typeof metaRaw.governance === 'object', '元数据包含 governance'); assert(typeof metaRaw.features === 'object', '元数据包含 features'); // 检查本体论补丁 const patchPath = path.join(ROOT_DIR, '.github/persona-brain/ontology-patches/ONT-PATCH-008.json'); assert(fs.existsSync(patchPath), 'ONT-PATCH-008.json 存在'); const patch = JSON.parse(fs.readFileSync(patchPath, 'utf8')); assert(patch.patch_id === 'ONT-PATCH-008', '补丁 ID 正确'); assert(patch.extends === 'ONT-PATCH-007 天眼涌现定义', '正确扩展 ONT-PATCH-007'); assert(typeof patch.core_definition === 'object', '包含核心定义'); assert(typeof patch.architecture === 'object', '包含架构'); assert(typeof patch.governance === 'object', '包含治理'); // ══════════════════════════════════════════════════════════════════════════ // 测试 7: 脚本文件完整性 // ══════════════════════════════════════════════════════════════════════════ console.log('\n── 测试 7: 脚本文件完整性 ──'); const scriptFiles = [ 'scripts/community/community-manager.js', 'scripts/community/timeline-tracker.js', 'scripts/community/dormancy-watcher.js', 'scripts/community/self-upgrade-registry.js', 'scripts/community/readme-community.js', 'scripts/community/growth-engine.js' ]; scriptFiles.forEach(function (f) { const filePath = path.join(ROOT_DIR, f); assert(fs.existsSync(filePath), '脚本存在: ' + f); }); // ══════════════════════════════════════════════════════════════════════════ // 清理 & 恢复 & 结果 // ══════════════════════════════════════════════════════════════════════════ // 恢复真实数据文件 Object.keys(backups).forEach(function (f) { var fp = path.join(REAL_COMMUNITY_DIR, f); fs.writeFileSync(fp, backups[f], 'utf8'); }); console.log('\n── 恢复真实数据文件 ──'); console.log(' 已恢复 ' + Object.keys(backups).length + ' 个文件'); console.log('\n── 清理测试目录 ──'); fs.rmSync(TEMP_DIR, { recursive: true, force: true }); console.log(' 已清理: ' + TEMP_DIR); console.log('\n══════════════════════════════════════════'); console.log(`🏁 测试完成: ${passed} 通过 / ${failed} 失败 / ${passed + failed} 总计`); console.log('══════════════════════════════════════════'); if (failed > 0) { process.exit(1); }