|
|
@@ -0,0 +1,336 @@
|
|
|
+import { jest } from '@jest/globals'
|
|
|
+import { searchCommits, getCommitInfo, getWorkingState, GitCommit } from '../git'
|
|
|
+import { ExecException } from 'child_process'
|
|
|
+
|
|
|
+type ExecFunction = (
|
|
|
+ command: string,
|
|
|
+ options: { cwd?: string },
|
|
|
+ callback: (error: ExecException | null, result?: { stdout: string; stderr: string }) => void
|
|
|
+) => void
|
|
|
+
|
|
|
+type PromisifiedExec = (command: string, options?: { cwd?: string }) => Promise<{ stdout: string; stderr: string }>
|
|
|
+
|
|
|
+// Mock child_process.exec
|
|
|
+jest.mock('child_process', () => ({
|
|
|
+ exec: jest.fn()
|
|
|
+}))
|
|
|
+
|
|
|
+// Mock util.promisify to return our own mock function
|
|
|
+jest.mock('util', () => ({
|
|
|
+ promisify: jest.fn((fn: ExecFunction): PromisifiedExec => {
|
|
|
+ return async (command: string, options?: { cwd?: string }) => {
|
|
|
+ // Call the original mock to maintain the mock implementation
|
|
|
+ return new Promise((resolve, reject) => {
|
|
|
+ fn(command, options || {}, (error: ExecException | null, result?: { stdout: string; stderr: string }) => {
|
|
|
+ if (error) {
|
|
|
+ reject(error)
|
|
|
+ } else {
|
|
|
+ resolve(result!)
|
|
|
+ }
|
|
|
+ })
|
|
|
+ })
|
|
|
+ }
|
|
|
+ })
|
|
|
+}))
|
|
|
+
|
|
|
+// Mock extract-text
|
|
|
+jest.mock('../../integrations/misc/extract-text', () => ({
|
|
|
+ truncateOutput: jest.fn(text => text)
|
|
|
+}))
|
|
|
+
|
|
|
+describe('git utils', () => {
|
|
|
+ // Get the mock with proper typing
|
|
|
+ const { exec } = jest.requireMock('child_process') as { exec: jest.MockedFunction<ExecFunction> }
|
|
|
+ const cwd = '/test/path'
|
|
|
+
|
|
|
+ beforeEach(() => {
|
|
|
+ jest.clearAllMocks()
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('searchCommits', () => {
|
|
|
+ const mockCommitData = [
|
|
|
+ 'abc123def456',
|
|
|
+ 'abc123',
|
|
|
+ 'fix: test commit',
|
|
|
+ 'John Doe',
|
|
|
+ '2024-01-06',
|
|
|
+ 'def456abc789',
|
|
|
+ 'def456',
|
|
|
+ 'feat: new feature',
|
|
|
+ 'Jane Smith',
|
|
|
+ '2024-01-05'
|
|
|
+ ].join('\n')
|
|
|
+
|
|
|
+ it('should return commits when git is installed and repo exists', async () => {
|
|
|
+ // Set up mock responses
|
|
|
+ const responses = new Map([
|
|
|
+ ['git --version', { stdout: 'git version 2.39.2', stderr: '' }],
|
|
|
+ ['git rev-parse --git-dir', { stdout: '.git', stderr: '' }],
|
|
|
+ ['git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short --grep="test" --regexp-ignore-case', { stdout: mockCommitData, stderr: '' }]
|
|
|
+ ])
|
|
|
+
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ // Find matching response
|
|
|
+ for (const [cmd, response] of responses) {
|
|
|
+ if (command === cmd) {
|
|
|
+ callback(null, response)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback(new Error(`Unexpected command: ${command}`))
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await searchCommits('test', cwd)
|
|
|
+
|
|
|
+ // First verify the result is correct
|
|
|
+ expect(result).toHaveLength(2)
|
|
|
+ expect(result[0]).toEqual({
|
|
|
+ hash: 'abc123def456',
|
|
|
+ shortHash: 'abc123',
|
|
|
+ subject: 'fix: test commit',
|
|
|
+ author: 'John Doe',
|
|
|
+ date: '2024-01-06'
|
|
|
+ })
|
|
|
+
|
|
|
+ // Then verify all commands were called correctly
|
|
|
+ expect(exec).toHaveBeenCalledWith(
|
|
|
+ 'git --version',
|
|
|
+ {},
|
|
|
+ expect.any(Function)
|
|
|
+ )
|
|
|
+ expect(exec).toHaveBeenCalledWith(
|
|
|
+ 'git rev-parse --git-dir',
|
|
|
+ { cwd },
|
|
|
+ expect.any(Function)
|
|
|
+ )
|
|
|
+ expect(exec).toHaveBeenCalledWith(
|
|
|
+ 'git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short --grep="test" --regexp-ignore-case',
|
|
|
+ { cwd },
|
|
|
+ expect.any(Function)
|
|
|
+ )
|
|
|
+ }, 20000)
|
|
|
+
|
|
|
+ it('should return empty array when git is not installed', async () => {
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ if (command === 'git --version') {
|
|
|
+ callback(new Error('git not found'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await searchCommits('test', cwd)
|
|
|
+ expect(result).toEqual([])
|
|
|
+ expect(exec).toHaveBeenCalledWith('git --version', {}, expect.any(Function))
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should return empty array when not in a git repository', async () => {
|
|
|
+ const responses = new Map([
|
|
|
+ ['git --version', { stdout: 'git version 2.39.2', stderr: '' }],
|
|
|
+ ['git rev-parse --git-dir', null] // null indicates error should be called
|
|
|
+ ])
|
|
|
+
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ const response = responses.get(command)
|
|
|
+ if (response === null) {
|
|
|
+ callback(new Error('not a git repository'))
|
|
|
+ } else if (response) {
|
|
|
+ callback(null, response)
|
|
|
+ } else {
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await searchCommits('test', cwd)
|
|
|
+ expect(result).toEqual([])
|
|
|
+ expect(exec).toHaveBeenCalledWith('git --version', {}, expect.any(Function))
|
|
|
+ expect(exec).toHaveBeenCalledWith('git rev-parse --git-dir', { cwd }, expect.any(Function))
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should handle hash search when grep search returns no results', async () => {
|
|
|
+ const responses = new Map([
|
|
|
+ ['git --version', { stdout: 'git version 2.39.2', stderr: '' }],
|
|
|
+ ['git rev-parse --git-dir', { stdout: '.git', stderr: '' }],
|
|
|
+ ['git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short --grep="abc123" --regexp-ignore-case', { stdout: '', stderr: '' }],
|
|
|
+ ['git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short --author-date-order abc123', { stdout: mockCommitData, stderr: '' }]
|
|
|
+ ])
|
|
|
+
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ for (const [cmd, response] of responses) {
|
|
|
+ if (command === cmd) {
|
|
|
+ callback(null, response)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await searchCommits('abc123', cwd)
|
|
|
+ expect(result).toHaveLength(2)
|
|
|
+ expect(result[0]).toEqual({
|
|
|
+ hash: 'abc123def456',
|
|
|
+ shortHash: 'abc123',
|
|
|
+ subject: 'fix: test commit',
|
|
|
+ author: 'John Doe',
|
|
|
+ date: '2024-01-06'
|
|
|
+ })
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('getCommitInfo', () => {
|
|
|
+ const mockCommitInfo = [
|
|
|
+ 'abc123def456',
|
|
|
+ 'abc123',
|
|
|
+ 'fix: test commit',
|
|
|
+ 'John Doe',
|
|
|
+ '2024-01-06',
|
|
|
+ 'Detailed description'
|
|
|
+ ].join('\n')
|
|
|
+ const mockStats = '1 file changed, 2 insertions(+), 1 deletion(-)'
|
|
|
+ const mockDiff = '@@ -1,1 +1,2 @@\n-old line\n+new line'
|
|
|
+
|
|
|
+ it('should return formatted commit info', async () => {
|
|
|
+ const responses = new Map([
|
|
|
+ ['git --version', { stdout: 'git version 2.39.2', stderr: '' }],
|
|
|
+ ['git rev-parse --git-dir', { stdout: '.git', stderr: '' }],
|
|
|
+ ['git show --format="%H%n%h%n%s%n%an%n%ad%n%b" --no-patch abc123', { stdout: mockCommitInfo, stderr: '' }],
|
|
|
+ ['git show --stat --format="" abc123', { stdout: mockStats, stderr: '' }],
|
|
|
+ ['git show --format="" abc123', { stdout: mockDiff, stderr: '' }]
|
|
|
+ ])
|
|
|
+
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ for (const [cmd, response] of responses) {
|
|
|
+ if (command.startsWith(cmd)) {
|
|
|
+ callback(null, response)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await getCommitInfo('abc123', cwd)
|
|
|
+ expect(result).toContain('Commit: abc123')
|
|
|
+ expect(result).toContain('Author: John Doe')
|
|
|
+ expect(result).toContain('Files Changed:')
|
|
|
+ expect(result).toContain('Full Changes:')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should return error message when git is not installed', async () => {
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ if (command === 'git --version') {
|
|
|
+ callback(new Error('git not found'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await getCommitInfo('abc123', cwd)
|
|
|
+ expect(result).toBe('Git is not installed')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should return error message when not in a git repository', async () => {
|
|
|
+ const responses = new Map([
|
|
|
+ ['git --version', { stdout: 'git version 2.39.2', stderr: '' }],
|
|
|
+ ['git rev-parse --git-dir', null] // null indicates error should be called
|
|
|
+ ])
|
|
|
+
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ const response = responses.get(command)
|
|
|
+ if (response === null) {
|
|
|
+ callback(new Error('not a git repository'))
|
|
|
+ } else if (response) {
|
|
|
+ callback(null, response)
|
|
|
+ } else {
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await getCommitInfo('abc123', cwd)
|
|
|
+ expect(result).toBe('Not a git repository')
|
|
|
+ })
|
|
|
+ })
|
|
|
+
|
|
|
+ describe('getWorkingState', () => {
|
|
|
+ const mockStatus = ' M src/file1.ts\n?? src/file2.ts'
|
|
|
+ const mockDiff = '@@ -1,1 +1,2 @@\n-old line\n+new line'
|
|
|
+
|
|
|
+ it('should return working directory changes', async () => {
|
|
|
+ const responses = new Map([
|
|
|
+ ['git --version', { stdout: 'git version 2.39.2', stderr: '' }],
|
|
|
+ ['git rev-parse --git-dir', { stdout: '.git', stderr: '' }],
|
|
|
+ ['git status --short', { stdout: mockStatus, stderr: '' }],
|
|
|
+ ['git diff HEAD', { stdout: mockDiff, stderr: '' }]
|
|
|
+ ])
|
|
|
+
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ for (const [cmd, response] of responses) {
|
|
|
+ if (command === cmd) {
|
|
|
+ callback(null, response)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await getWorkingState(cwd)
|
|
|
+ expect(result).toContain('Working directory changes:')
|
|
|
+ expect(result).toContain('src/file1.ts')
|
|
|
+ expect(result).toContain('src/file2.ts')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should return message when working directory is clean', async () => {
|
|
|
+ const responses = new Map([
|
|
|
+ ['git --version', { stdout: 'git version 2.39.2', stderr: '' }],
|
|
|
+ ['git rev-parse --git-dir', { stdout: '.git', stderr: '' }],
|
|
|
+ ['git status --short', { stdout: '', stderr: '' }]
|
|
|
+ ])
|
|
|
+
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ for (const [cmd, response] of responses) {
|
|
|
+ if (command === cmd) {
|
|
|
+ callback(null, response)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ }
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await getWorkingState(cwd)
|
|
|
+ expect(result).toBe('No changes in working directory')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should return error message when git is not installed', async () => {
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ if (command === 'git --version') {
|
|
|
+ callback(new Error('git not found'))
|
|
|
+ return
|
|
|
+ }
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await getWorkingState(cwd)
|
|
|
+ expect(result).toBe('Git is not installed')
|
|
|
+ })
|
|
|
+
|
|
|
+ it('should return error message when not in a git repository', async () => {
|
|
|
+ const responses = new Map([
|
|
|
+ ['git --version', { stdout: 'git version 2.39.2', stderr: '' }],
|
|
|
+ ['git rev-parse --git-dir', null] // null indicates error should be called
|
|
|
+ ])
|
|
|
+
|
|
|
+ exec.mockImplementation((command: string, options: { cwd?: string }, callback: Function) => {
|
|
|
+ const response = responses.get(command)
|
|
|
+ if (response === null) {
|
|
|
+ callback(new Error('not a git repository'))
|
|
|
+ } else if (response) {
|
|
|
+ callback(null, response)
|
|
|
+ } else {
|
|
|
+ callback(new Error('Unexpected command'))
|
|
|
+ }
|
|
|
+ })
|
|
|
+
|
|
|
+ const result = await getWorkingState(cwd)
|
|
|
+ expect(result).toBe('Not a git repository')
|
|
|
+ })
|
|
|
+ })
|
|
|
+})
|