// 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);
}
