okr-service.test.js
3.77 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
/**
* Unit Tests — okr-service.js + migrate-goals-to-okr.js
*
* 覆盖 27-NAMING §六 正名后的两件事:
* 1. OkrService 用新命名工作(okr/ 目录、O- 前缀、objective.json、Objective/KeyResult 层级)
* 2. 迁移脚本的纯函数把旧 ID 正确改写(含 nodes key、children 数组)
*/
import { describe, it, expect, beforeEach, afterEach } from 'vitest';
import fs from 'fs';
import path from 'path';
import os from 'os';
const { OkrService } = require('../../services/okr-service');
const { reId, reObjective } = require('../../../scripts/migrate-goals-to-okr');
describe('OkrService(27-NAMING §六 正名后)', () => {
let tmpCompany;
beforeEach(() => {
// OkrService 按 companyId 落在 data/companies/{id}/okr/,用随机 id 隔离
tmpCompany = `__test-okr-${Math.random().toString(36).slice(2, 8)}`;
});
afterEach(() => {
const dir = path.resolve(__dirname, '../../../data/companies', tmpCompany);
fs.rmSync(dir, { recursive: true, force: true });
});
it('目录用 okr/ 而不是 goals/', () => {
const svc = new OkrService(tmpCompany);
expect(svc.okrDir.endsWith(path.join(tmpCompany, 'okr'))).toBe(true);
expect(fs.existsSync(svc.okrDir)).toBe(true);
});
it('新建 Objective 用 O- 前缀,落盘为 objective.json', () => {
const svc = new OkrService(tmpCompany);
const obj = svc.create({ title: '测试目标', owner: 'ceo' });
expect(obj.id).toBe('O-001');
expect(fs.existsSync(path.join(svc.okrDir, 'O-001', 'objective.json'))).toBe(true);
// 旧文件名不应再出现
expect(fs.existsSync(path.join(svc.okrDir, 'O-001', 'goal.json'))).toBe(false);
});
it('层级名是 Objective / KeyResult / SubTask(不再有 Goal 这一层)', () => {
const svc = new OkrService(tmpCompany);
expect(svc.parseNodeId('O-001').levelName).toBe('Objective');
expect(svc.parseNodeId('O-001.1').levelName).toBe('KeyResult');
expect(svc.parseNodeId('O-001.1.1').levelName).toBe('SubTask');
// 返回的是 objectiveId,不是 goalId
expect(svc.parseNodeId('O-001.1.1').objectiveId).toBe('O-001');
});
it('addNode / updateNode / get 走通,ID 全程 O- 前缀', () => {
const svc = new OkrService(tmpCompany);
const obj = svc.create({ title: '目标', owner: 'ceo' });
const { nodeId } = svc.addNode(obj.id, null, { title: 'KR1', owner: 'dev' });
expect(nodeId).toBe('O-001.1');
svc.updateNode(obj.id, nodeId, { status: 'done' });
expect(svc.get(obj.id).nodes[nodeId].status).toBe('done');
expect(svc.getProgress(obj.id).done).toBe(1);
});
it('updateObjective 存在(原 updateGoal 已改名)', () => {
const svc = new OkrService(tmpCompany);
const obj = svc.create({ title: '旧标题', owner: 'ceo' });
expect(typeof svc.updateObjective).toBe('function');
expect(svc.updateObjective(obj.id, { title: '新标题' }).title).toBe('新标题');
});
});
describe('migrate-goals-to-okr 纯函数', () => {
it('reId 只改 G- 前缀,其余原样', () => {
expect(reId('G-001')).toBe('O-001');
expect(reId('G-001.1.1')).toBe('O-001.1.1');
expect(reId('O-001')).toBe('O-001'); // 幂等
expect(reId('T-abc')).toBe('T-abc'); // 不误伤 taskId
expect(reId(null)).toBe(null);
});
it('reObjective 改写 id、nodes 的 key、以及 children 数组', () => {
const out = reObjective({
id: 'G-001',
title: 'x',
nodes: {
'G-001.1': { title: 'O1', children: ['G-001.1.1'] },
'G-001.1.1': { title: 'KR1', children: [] },
},
});
expect(out.id).toBe('O-001');
expect(Object.keys(out.nodes).sort()).toEqual(['O-001.1', 'O-001.1.1']);
expect(out.nodes['O-001.1'].children).toEqual(['O-001.1.1']);
expect(out.title).toBe('x'); // 非 ID 字段不动
});
});