spacehub

surshar / claude-code

Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflows - all through natural language commands.

Built by Anthropic · github.com/anthropics

claude-code / mods / diff / tests / git.test.ts
176 lines · 5.6 KBRaw
1import { describe, expect, mock, test, tier } from 'claude-code/testing'
2
3import Git from '../hooks/git'
4import Limits from '../hooks/limits'
5import Fixtures from './fixtures'
6
7tier('builtin')
8
9describe('git', () => {
10 test('each git child names the pin, in the C locale', async ($, on) => {
11 const world = Fixtures.inRepository(on, Fixtures.oneSecret())
12
13 await $.session.start(Fixtures.WORKTREE_SESSION)
14 await $.command.run(Fixtures.DIFF)
15 await world.clock.advance(Fixtures.SETTLE_MS)
16 await $.ui.render(Fixtures.PANE)
17 await $.ui.press({ plugin: 'diff', key: 'ask:.env' })
18 await world.clock.settle()
19
20 const [discovery, ...pinned] = world.runs
21 const diffs = pinned.filter(run => run.argv.includes('diff'))
22
23 expect(discovery?.argv).toContain('--show-toplevel')
24
25 expect(
26 discovery?.init?.cwd,
27 "found where the session started, wherever Claude's shell has gone",
28 ).toBe('/main/wt')
29
30 expect(diffs.length).toBeGreaterThanOrEqual(3)
31
32 for (const run of world.runs) {
33 expect(run.init?.env).toEqual(Git.GIT_CHILD_ENV)
34 }
35
36 for (const run of pinned) {
37 expect(run.argv.slice(0, Fixtures.PINNED_LEAD.length)).toEqual(
38 Fixtures.PINNED_LEAD,
39 )
40
41 expect(run.init).toMatchObject({ cwd: '/main/wt' })
42 }
43
44 expect(world.runs.some(run => run.argv.includes('config'))).toBe(false)
45 })
46
47 test('the working tree is walked once, by the first fetch', async ($, on) => {
48 const world = Fixtures.inRepository(on)
49
50 const walks = () =>
51 world.runs.filter(run => Fixtures.gitWordOf(run.argv) === 'status')
52
53 const fetches = () =>
54 world.runs.filter(
55 run => Fixtures.gitWordOf(run.argv) === 'diff --numstat',
56 )
57
58 on('tool.call', () => ({ result: 'ran' }))
59
60 await $.session.start(Fixtures.SESSION)
61 await $.command.run(Fixtures.DIFF)
62 await world.clock.advance(Fixtures.SETTLE_MS)
63
64 expect(walks(), 'the first fetch walked').toHaveLength(1)
65 expect(fetches()).toHaveLength(1)
66
67 await $.tool.call({ tool: 'Bash', command: 'make' })
68 await world.clock.advance(Fixtures.SETTLE_MS)
69
70 expect(fetches(), 'the command fetched again').toHaveLength(2)
71 expect(walks(), 'and read the walk it had').toHaveLength(1)
72 expect(walks()[0]?.init?.cwd, 'pinned like every child').toBe('/work')
73 })
74
75 test('fifty drawn files: one hunks child a read', async ($, on) => {
76 const world = Fixtures.inRepository(
77 on,
78 Fixtures.filesChanged(Limits.MAX_FILES),
79 )
80
81 const hunkReads = () => world.runs.filter(run => run.argv.includes('--raw'))
82
83 on('tool.call', () => ({ result: 'ran' }))
84
85 await $.session.start(Fixtures.SESSION)
86 await $.command.run(Fixtures.DIFF)
87 await world.clock.advance(Fixtures.SETTLE_MS)
88
89 expect(hunkReads(), 'the open read every body at once').toHaveLength(1)
90
91 expect(
92 hunkReads()[0]?.argv.filter(word => /^file\d+\.ts$/.test(word)),
93 'every drawn file named to the one child',
94 ).toHaveLength(Limits.MAX_FILES)
95
96 const read = world.runs.length
97
98 await $.tool.call({ tool: 'Bash', command: 'make' })
99 await world.clock.advance(Fixtures.SETTLE_MS)
100
101 expect(
102 world.runs
103 .slice(read)
104 .filter(run => Fixtures.gitWordOf(run.argv) !== Fixtures.POLL_WORD)
105 .map(run => run.argv.find(word => /^--(\w*stat|raw)$/.test(word))),
106 'a command: the counts, then one child for fifty bodies',
107 ).toEqual(['--shortstat', '--numstat', '--raw'])
108
109 expect(
110 Fixtures.textOf(await $.ui.render(Fixtures.PANE)),
111 'drawn from that one answer',
112 ).toContain('const v = 1')
113 })
114
115 test('a file moved in since the start is session work', async ($, on) => {
116 const clock = Fixtures.startsSession(on, Fixtures.SETTLE_MS)
117
118 on('process.run', ($, e) => ({
119 value: Fixtures.gitIn(e.argv, Fixtures.MOVED_IN),
120 }))
121
122 on('ui.open', () => ({ value: undefined }))
123 on('ui.invalidate', () => ({ value: undefined }))
124 on('session.messages', () => ({ value: [] }))
125 Fixtures.oldFiles(on)
126 mock.store(on)
127
128 await $.session.start(Fixtures.SESSION)
129 await $.command.run(Fixtures.DIFF)
130 await clock.advance(Fixtures.SETTLE_MS)
131
132 const drawn = Fixtures.textOf(await $.ui.render(Fixtures.PANE))
133
134 expect(drawn).toContain('1 file changed +1 -1')
135 expect(drawn).toContain('moved.ts')
136 expect(drawn).toContain('+1 file edited before this session (show)')
137 })
138
139 test('the session began when the engine says', async ($, on) => {
140 let startedAt = 0
141
142 const clock = Fixtures.startsSession(on, Fixtures.SETTLE_MS)
143
144 on('process.run', ($, e) => ({
145 value: Fixtures.gitIn(e.argv, Fixtures.MOVED_IN),
146 }))
147
148 on('ui.open', () => ({ value: undefined }))
149 on('ui.invalidate', () => ({ value: undefined }))
150 on('session.messages', () => ({ value: [] }))
151 on('session.usage', () => ({ value: Fixtures.usageAt(startedAt) }))
152 on('command.run', { command: 'clear' }, () => ({}))
153 Fixtures.oldFiles(on)
154 mock.store(on)
155
156 await $.session.start(Fixtures.SESSION)
157 await $.command.run(Fixtures.DIFF)
158 await clock.advance(Fixtures.SETTLE_MS)
159
160 expect(
161 Fixtures.textOf(await $.ui.render(Fixtures.PANE)),
162 'resumed from before the old files were written: both are the ' +
163 "session's, as the built-in restores its start",
164 ).toContain('2 files changed +2 -2')
165
166 startedAt = clock.now()
167
168 await $.command.run(Fixtures.CLEAR)
169 await clock.advance(Fixtures.SETTLE_MS)
170
171 expect(
172 Fixtures.textOf(await $.ui.render(Fixtures.PANE)),
173 '/clear started the session over, past the dirty file',
174 ).toContain('+1 file edited before this session (show)')
175 })
176})