timeline-logger.test.js
3.62 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
/**
* timelineLogger + changelogWriter 单测
*/
const fs = require('fs');
const pathResolver = require('../../services/devloop/path-resolver');
const timelineLogger = require('../../services/devloop/timeline-logger');
const changelogWriter = require('../../services/devloop/changelog-writer');
describe('timelineLogger', () => {
let pid, root;
beforeEach(() => {
pathResolver._resetForTesting();
const r = pathResolver.allocateProjectRoot({ company_id: 'c-test', mode: 'sandbox' });
pid = r.pid;
root = r.root_path;
});
afterEach(() => {
fs.rmSync(root, { recursive: true, force: true });
});
it('append writes NDJSON line', () => {
timelineLogger.append(pid, {
source: 'system', kind: 'project_created', ref: pid,
summary: 'test project',
});
const events = timelineLogger.read(pid);
expect(events).toHaveLength(1);
expect(events[0].kind).toBe('project_created');
});
it('append rejects missing fields', () => {
expect(() => timelineLogger.append(pid, {})).toThrow();
expect(() => timelineLogger.append(pid, { source: 'x' })).toThrow();
});
it('summary truncated to 280 chars', () => {
const longSummary = 'x'.repeat(500);
timelineLogger.append(pid, {
source: 'system', kind: 'test', summary: longSummary,
});
const events = timelineLogger.read(pid);
expect(events[0].summary.length).toBeLessThanOrEqual(280);
expect(events[0].summary).toMatch(/…$/);
});
it('oversized payload reduced to ref marker', () => {
const big = { huge: 'x'.repeat(5000) };
timelineLogger.append(pid, {
source: 'system', kind: 'test', summary: 's', payload: big,
});
const events = timelineLogger.read(pid);
expect(events[0].payload.oversize).toBe(true);
});
it('tail returns last N', () => {
for (let i = 0; i < 15; i++) {
timelineLogger.append(pid, { source: 'system', kind: 'k', summary: `${i}` });
}
const last5 = timelineLogger.tail(pid, 5);
expect(last5).toHaveLength(5);
// 时间正序,最后一条是最新
expect(last5[4].summary).toBe('14');
});
it('filter by kind', () => {
timelineLogger.append(pid, { source: 'pm', kind: 'prd_drafted', summary: 'a' });
timelineLogger.append(pid, { source: 'arch', kind: 'sdd_generated', summary: 'b' });
timelineLogger.append(pid, { source: 'pm', kind: 'prd_drafted', summary: 'c' });
const r = timelineLogger.read(pid, { kind: 'prd_drafted' });
expect(r).toHaveLength(2);
});
});
describe('changelogWriter', () => {
let pid, root;
beforeEach(() => {
pathResolver._resetForTesting();
const r = pathResolver.allocateProjectRoot({ company_id: 'c-test', mode: 'sandbox' });
pid = r.pid;
root = r.root_path;
});
afterEach(() => {
fs.rmSync(root, { recursive: true, force: true });
});
it('append writes entry', () => {
changelogWriter.append(pid, {
kind: 'prd_rev', rev: 1, by: 'pm-agent',
trigger: 'init', summary: 'first PRD',
});
const all = changelogWriter.read(pid);
expect(all).toHaveLength(1);
expect(all[0].kind).toBe('prd_rev');
expect(all[0].rev).toBe(1);
});
it('rejects invalid kind', () => {
expect(() => changelogWriter.append(pid, {
kind: 'wrong-kind', rev: 1, summary: 's',
})).toThrow();
});
it('latest returns most recent of kind', () => {
changelogWriter.append(pid, { kind: 'prd_rev', rev: 1, summary: 's1' });
changelogWriter.append(pid, { kind: 'sdd_rev', rev: 1, summary: 's2' });
changelogWriter.append(pid, { kind: 'prd_rev', rev: 2, summary: 's3' });
const r = changelogWriter.latest(pid, 'prd_rev');
expect(r.rev).toBe(2);
});
});