import test from 'node:test'; import assert from 'node:assert/strict'; import fs from 'node:fs'; import os from 'node:os'; import path from 'node:path'; import crypto from 'node:crypto'; import {BIRTH_DATE, closeTick, LIFE_LINE_SCHEMA, PERSONA_ID, TIMEZONE, WORLD_EPOCH_DATE, lifeLineBlockHash, verifyLifeLine} from './life-line.mjs'; const bytesHash = value => crypto.createHash('sha256').update(value).digest('hex'); function makeChain() { const root = fs.mkdtempSync(path.join(os.tmpdir(), 'life-line-')); fs.mkdirSync(path.join(root, 'evidence'), {recursive: true}); const writeEvidence = (day, text) => { const relative = `evidence/${day}.json`; const absolute = path.join(root, relative); fs.writeFileSync(absolute, text); return {evidence_path: relative, daily_evidence_sha256: bytesHash(Buffer.from(text))}; }; const genesisEvidence = writeEvidence(BIRTH_DATE, '{"birth":"ICE-P-ZY001"}\n'); const genesis = {sequence: 0, beijing_day: BIRTH_DATE, previous_hash: null, beijing_close_tick: closeTick(BIRTH_DATE), era_day: 314, ...genesisEvidence}; genesis.hash = lifeLineBlockHash(genesis); const dayOneEvidence = writeEvidence('2026-03-06', '{"day":"2026-03-06","evidence":"real"}\n'); const dayOne = {sequence: 1, beijing_day: '2026-03-06', previous_hash: genesis.hash, beijing_close_tick: closeTick('2026-03-06'), era_day: 315, ...dayOneEvidence}; dayOne.hash = lifeLineBlockHash(dayOne); const manifest = {schema: LIFE_LINE_SCHEMA, persona_id: PERSONA_ID, timezone: TIMEZONE, birth_date_beijing: BIRTH_DATE, world_epoch_date: WORLD_EPOCH_DATE, genesis, blocks: [dayOne], head_hash: dayOne.hash}; const manifestPath = path.join(root, 'CURRENT.json'); fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2)); return {root, manifestPath}; } test('life line verifies H0, contiguous daily evidence, close tick and current head', () => { const ctx = makeChain(); try { const result = verifyLifeLine({manifestPath: ctx.manifestPath, now: new Date('2026-03-07T00:00:00.000Z')}); assert.equal(result.state, 'LIFE_LINE_VERIFIED'); assert.equal(result.head.sequence, 1); assert.equal(result.head.beijing_day, '2026-03-06'); } finally { fs.rmSync(ctx.root, {recursive: true, force: true}); } }); test('life line keeps PAIN_ALARM when evidence changes after the hash was recorded', () => { const ctx = makeChain(); try { fs.writeFileSync(path.join(ctx.root, 'evidence/2026-03-06.json'), '{"tampered":true}\n'); const result = verifyLifeLine({manifestPath: ctx.manifestPath, now: new Date('2026-03-07T00:00:00.000Z')}); assert.equal(result.state, 'LIFE_LINE_PAIN_ALARM'); assert.ok(result.reasons.includes('DAILY_EVIDENCE_HASH_MISMATCH:2026-03-06')); } finally { fs.rmSync(ctx.root, {recursive: true, force: true}); } }); test('missing life line manifest is an explicit alarm, not an inferred chain', () => { const result = verifyLifeLine({manifestPath: '/tmp/guanghu-life-line-does-not-exist/CURRENT.json', now: new Date('2026-03-07T00:00:00.000Z')}); assert.equal(result.state, 'LIFE_LINE_PAIN_ALARM'); assert.ok(result.reasons.includes('LIFE_LINE_MANIFEST_MISSING')); }); test('an explicitly evidenced historical gap remains visible but does not break the current chain', () => { const ctx = makeChain(); try { const manifest = JSON.parse(fs.readFileSync(ctx.manifestPath)); const evidence = fs.readFileSync(path.join(ctx.root, 'evidence/2026-03-06.json')); manifest.blocks[0].gap = true; manifest.blocks[0].gap_kind = 'HISTORICAL_UNOBSERVED'; manifest.blocks[0].daily_evidence_sha256 = bytesHash(evidence); manifest.blocks[0].hash = lifeLineBlockHash(manifest.blocks[0]); manifest.head_hash = manifest.blocks[0].hash; fs.writeFileSync(ctx.manifestPath, JSON.stringify(manifest, null, 2)); const result = verifyLifeLine({manifestPath: ctx.manifestPath, now: new Date('2026-03-07T00:00:00.000Z')}); assert.equal(result.state, 'LIFE_LINE_VERIFIED_WITH_HISTORICAL_GAPS'); assert.equal(result.wake_allowed, true); assert.equal(result.gaps.length, 1); } finally { fs.rmSync(ctx.root, {recursive: true, force: true}); } });