-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathtest.js
More file actions
83 lines (74 loc) · 3.64 KB
/
Copy pathtest.js
File metadata and controls
83 lines (74 loc) · 3.64 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
// 역할: 레인 배정·히스토리 파싱·URL 정규화·스쿼시 링크의 최소 불변식 셀프체크.
// 책임 경계: 프레임워크 없는 assert 스모크. 실행: npm test.
'use strict'
const assert = require('node:assert')
const fs = require('node:fs')
const os = require('node:os')
const path = require('node:path')
const { execFileSync } = require('node:child_process')
const { assignLanes } = require('./public/layout.js')
const { normalizeRepo, parseHistory, squashLinks } = require('./server.js')
// 픽스처 레포에서 git 명령 실행함.
const g = (dir, ...args) => execFileSync('git', ['-c', 'user.name=t', '-c', 'user.email=t@t', ...args], { cwd: dir })
// 브랜치·머지가 있는 픽스처 레포를 임시 디렉터리에 만들어 경로를 돌려줌.
function makeFixture() {
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rhg-'))
g(dir, 'init', '-q', '-b', 'main')
g(dir, 'commit', '-q', '--allow-empty', '-m', 'root')
g(dir, 'commit', '-q', '--allow-empty', '-m', 'B')
g(dir, 'checkout', '-q', '-b', 'feat')
g(dir, 'commit', '-q', '--allow-empty', '-m', 'C')
g(dir, 'checkout', '-q', 'main')
g(dir, 'commit', '-q', '--allow-empty', '-m', 'D')
g(dir, 'merge', '-q', '--no-ff', '-m', 'M', 'feat')
g(dir, 'tag', 'v1')
return dir
}
async function main() {
// normalizeRepo: URL·suffix 정리와 형식 검증
assert.equal(normalizeRepo('https://github.com/a/b.git'), 'a/b')
assert.equal(normalizeRepo('a/b/'), 'a/b')
assert.equal(normalizeRepo('nope'), null)
assert.equal(normalizeRepo('--upload-pack=x/y'), null)
// parseHistory: 픽스처 5커밋, topo 순서(자식 먼저), 머지 부모 2개, refs 파싱
const dir = makeFixture()
const nodes = await parseHistory(dir)
assert.equal(nodes.length, 5)
const merge = nodes[0]
assert.equal(merge.msg, 'M')
assert.equal(merge.parents.length, 2)
assert.ok(merge.refs.includes('main') && merge.refs.includes('tag: v1'))
const seen = new Set()
for (const n of nodes) { // topo: 부모가 자식보다 먼저 나오면 안 됨
for (const p of n.parents) assert.ok(!seen.has(p), 'topo order violated')
seen.add(n.id)
}
assert.equal(nodes.filter(n => n.parents.length === 0).length, 1) // 루트 1개
// assignLanes: 전 노드 배정, 메인 체인과 사이드 브랜치는 다른 레인
const laneOf = assignLanes(nodes)
for (const n of nodes) assert.ok(Number.isInteger(laneOf[n.id]))
const byMsg = Object.fromEntries(nodes.map(n => [n.msg, n]))
assert.equal(laneOf[byMsg.M.id], laneOf[byMsg.D.id], 'merge는 첫 부모 레인 유지')
assert.notEqual(laneOf[byMsg.C.id], laneOf[byMsg.D.id], '사이드 브랜치는 별도 레인')
assert.equal(laneOf[byMsg.root.id], laneOf[byMsg.B.id], '직선 구간은 같은 레인')
// 직선 체인만 있으면 전부 레인 0
const linear = [
{ id: 'c', parents: ['b'] }, { id: 'b', parents: ['a'] }, { id: 'a', parents: [] },
]
assert.deepEqual(assignLanes(linear), { a: 0, b: 0, c: 0 })
// squashLinks: 스쿼시(부모 1개 머지 결과)만 합성 링크, 진짜 머지는 제외
const sNodes = [
{ id: 'sq', parents: ['x'] }, { id: 'head', parents: ['x'] }, { id: 'x', parents: [] },
{ id: 'mg', parents: ['x', 'head'] },
]
const prs = {
1: { state: 'merged', headSha: 'head', mergeSha: 'sq' },
2: { state: 'merged', headSha: 'head', mergeSha: 'mg' },
3: { state: 'closed', headSha: 'head', mergeSha: 'sq' },
}
const links = squashLinks(sNodes, prs)
assert.deepEqual(links, [{ source: 'head', target: 'sq', pr: 1 }])
fs.rmSync(dir, { recursive: true, force: true })
console.log('ALL TESTS PASS')
}
main().catch(e => { console.error(e); process.exit(1) })