mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-22 21:31:08 -05:00
Prettier backfill
This commit is contained in:
@@ -1,97 +1,97 @@
|
||||
import { calculateApiCost } from '../cost';
|
||||
import { ModelInfo } from '../../shared/api';
|
||||
import { calculateApiCost } from "../cost"
|
||||
import { ModelInfo } from "../../shared/api"
|
||||
|
||||
describe('Cost Utility', () => {
|
||||
describe('calculateApiCost', () => {
|
||||
const mockModelInfo: ModelInfo = {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0, // $3 per million tokens
|
||||
outputPrice: 15.0, // $15 per million tokens
|
||||
cacheWritesPrice: 3.75, // $3.75 per million tokens
|
||||
cacheReadsPrice: 0.3, // $0.30 per million tokens
|
||||
};
|
||||
describe("Cost Utility", () => {
|
||||
describe("calculateApiCost", () => {
|
||||
const mockModelInfo: ModelInfo = {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsPromptCache: true,
|
||||
inputPrice: 3.0, // $3 per million tokens
|
||||
outputPrice: 15.0, // $15 per million tokens
|
||||
cacheWritesPrice: 3.75, // $3.75 per million tokens
|
||||
cacheReadsPrice: 0.3, // $0.30 per million tokens
|
||||
}
|
||||
|
||||
it('should calculate basic input/output costs correctly', () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500);
|
||||
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Total: 0.003 + 0.0075 = 0.0105
|
||||
expect(cost).toBe(0.0105);
|
||||
});
|
||||
it("should calculate basic input/output costs correctly", () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500)
|
||||
|
||||
it('should handle cache writes cost', () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500, 2000);
|
||||
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Cache writes: (3.75 / 1_000_000) * 2000 = 0.0075
|
||||
// Total: 0.003 + 0.0075 + 0.0075 = 0.018
|
||||
expect(cost).toBeCloseTo(0.018, 6);
|
||||
});
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Total: 0.003 + 0.0075 = 0.0105
|
||||
expect(cost).toBe(0.0105)
|
||||
})
|
||||
|
||||
it('should handle cache reads cost', () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500, undefined, 3000);
|
||||
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Cache reads: (0.3 / 1_000_000) * 3000 = 0.0009
|
||||
// Total: 0.003 + 0.0075 + 0.0009 = 0.0114
|
||||
expect(cost).toBe(0.0114);
|
||||
});
|
||||
it("should handle cache writes cost", () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500, 2000)
|
||||
|
||||
it('should handle all cost components together', () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500, 2000, 3000);
|
||||
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Cache writes: (3.75 / 1_000_000) * 2000 = 0.0075
|
||||
// Cache reads: (0.3 / 1_000_000) * 3000 = 0.0009
|
||||
// Total: 0.003 + 0.0075 + 0.0075 + 0.0009 = 0.0189
|
||||
expect(cost).toBe(0.0189);
|
||||
});
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Cache writes: (3.75 / 1_000_000) * 2000 = 0.0075
|
||||
// Total: 0.003 + 0.0075 + 0.0075 = 0.018
|
||||
expect(cost).toBeCloseTo(0.018, 6)
|
||||
})
|
||||
|
||||
it('should handle missing prices gracefully', () => {
|
||||
const modelWithoutPrices: ModelInfo = {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsPromptCache: true
|
||||
};
|
||||
it("should handle cache reads cost", () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500, undefined, 3000)
|
||||
|
||||
const cost = calculateApiCost(modelWithoutPrices, 1000, 500, 2000, 3000);
|
||||
expect(cost).toBe(0);
|
||||
});
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Cache reads: (0.3 / 1_000_000) * 3000 = 0.0009
|
||||
// Total: 0.003 + 0.0075 + 0.0009 = 0.0114
|
||||
expect(cost).toBe(0.0114)
|
||||
})
|
||||
|
||||
it('should handle zero tokens', () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 0, 0, 0, 0);
|
||||
expect(cost).toBe(0);
|
||||
});
|
||||
it("should handle all cost components together", () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500, 2000, 3000)
|
||||
|
||||
it('should handle undefined cache values', () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500);
|
||||
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Total: 0.003 + 0.0075 = 0.0105
|
||||
expect(cost).toBe(0.0105);
|
||||
});
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Cache writes: (3.75 / 1_000_000) * 2000 = 0.0075
|
||||
// Cache reads: (0.3 / 1_000_000) * 3000 = 0.0009
|
||||
// Total: 0.003 + 0.0075 + 0.0075 + 0.0009 = 0.0189
|
||||
expect(cost).toBe(0.0189)
|
||||
})
|
||||
|
||||
it('should handle missing cache prices', () => {
|
||||
const modelWithoutCachePrices: ModelInfo = {
|
||||
...mockModelInfo,
|
||||
cacheWritesPrice: undefined,
|
||||
cacheReadsPrice: undefined
|
||||
};
|
||||
it("should handle missing prices gracefully", () => {
|
||||
const modelWithoutPrices: ModelInfo = {
|
||||
maxTokens: 8192,
|
||||
contextWindow: 200_000,
|
||||
supportsPromptCache: true,
|
||||
}
|
||||
|
||||
const cost = calculateApiCost(modelWithoutCachePrices, 1000, 500, 2000, 3000);
|
||||
|
||||
// Should only include input and output costs
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Total: 0.003 + 0.0075 = 0.0105
|
||||
expect(cost).toBe(0.0105);
|
||||
});
|
||||
});
|
||||
});
|
||||
const cost = calculateApiCost(modelWithoutPrices, 1000, 500, 2000, 3000)
|
||||
expect(cost).toBe(0)
|
||||
})
|
||||
|
||||
it("should handle zero tokens", () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 0, 0, 0, 0)
|
||||
expect(cost).toBe(0)
|
||||
})
|
||||
|
||||
it("should handle undefined cache values", () => {
|
||||
const cost = calculateApiCost(mockModelInfo, 1000, 500)
|
||||
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Total: 0.003 + 0.0075 = 0.0105
|
||||
expect(cost).toBe(0.0105)
|
||||
})
|
||||
|
||||
it("should handle missing cache prices", () => {
|
||||
const modelWithoutCachePrices: ModelInfo = {
|
||||
...mockModelInfo,
|
||||
cacheWritesPrice: undefined,
|
||||
cacheReadsPrice: undefined,
|
||||
}
|
||||
|
||||
const cost = calculateApiCost(modelWithoutCachePrices, 1000, 500, 2000, 3000)
|
||||
|
||||
// Should only include input and output costs
|
||||
// Input cost: (3.0 / 1_000_000) * 1000 = 0.003
|
||||
// Output cost: (15.0 / 1_000_000) * 500 = 0.0075
|
||||
// Total: 0.003 + 0.0075 = 0.0105
|
||||
expect(cost).toBe(0.0105)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,126 +1,126 @@
|
||||
import { enhancePrompt } from '../enhance-prompt'
|
||||
import { ApiConfiguration } from '../../shared/api'
|
||||
import { buildApiHandler, SingleCompletionHandler } from '../../api'
|
||||
import { defaultPrompts } from '../../shared/modes'
|
||||
import { enhancePrompt } from "../enhance-prompt"
|
||||
import { ApiConfiguration } from "../../shared/api"
|
||||
import { buildApiHandler, SingleCompletionHandler } from "../../api"
|
||||
import { defaultPrompts } from "../../shared/modes"
|
||||
|
||||
// Mock the API handler
|
||||
jest.mock('../../api', () => ({
|
||||
buildApiHandler: jest.fn()
|
||||
jest.mock("../../api", () => ({
|
||||
buildApiHandler: jest.fn(),
|
||||
}))
|
||||
|
||||
describe('enhancePrompt', () => {
|
||||
const mockApiConfig: ApiConfiguration = {
|
||||
apiProvider: 'openai',
|
||||
openAiApiKey: 'test-key',
|
||||
openAiBaseUrl: 'https://api.openai.com/v1'
|
||||
}
|
||||
describe("enhancePrompt", () => {
|
||||
const mockApiConfig: ApiConfiguration = {
|
||||
apiProvider: "openai",
|
||||
openAiApiKey: "test-key",
|
||||
openAiBaseUrl: "https://api.openai.com/v1",
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
// Mock the API handler with a completePrompt method
|
||||
;(buildApiHandler as jest.Mock).mockReturnValue({
|
||||
completePrompt: jest.fn().mockResolvedValue('Enhanced prompt'),
|
||||
createMessage: jest.fn(),
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: 'test-model',
|
||||
info: {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false
|
||||
}
|
||||
})
|
||||
} as unknown as SingleCompletionHandler)
|
||||
})
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
|
||||
it('enhances prompt using default enhancement prompt when no custom prompt provided', async () => {
|
||||
const result = await enhancePrompt(mockApiConfig, 'Test prompt')
|
||||
|
||||
expect(result).toBe('Enhanced prompt')
|
||||
const handler = buildApiHandler(mockApiConfig)
|
||||
expect((handler as any).completePrompt).toHaveBeenCalledWith(
|
||||
`${defaultPrompts.enhance}\n\nTest prompt`
|
||||
)
|
||||
})
|
||||
// Mock the API handler with a completePrompt method
|
||||
;(buildApiHandler as jest.Mock).mockReturnValue({
|
||||
completePrompt: jest.fn().mockResolvedValue("Enhanced prompt"),
|
||||
createMessage: jest.fn(),
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: "test-model",
|
||||
info: {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
}),
|
||||
} as unknown as SingleCompletionHandler)
|
||||
})
|
||||
|
||||
it('enhances prompt using custom enhancement prompt when provided', async () => {
|
||||
const customEnhancePrompt = 'You are a custom prompt enhancer'
|
||||
|
||||
const result = await enhancePrompt(mockApiConfig, 'Test prompt', customEnhancePrompt)
|
||||
|
||||
expect(result).toBe('Enhanced prompt')
|
||||
const handler = buildApiHandler(mockApiConfig)
|
||||
expect((handler as any).completePrompt).toHaveBeenCalledWith(
|
||||
`${customEnhancePrompt}\n\nTest prompt`
|
||||
)
|
||||
})
|
||||
it("enhances prompt using default enhancement prompt when no custom prompt provided", async () => {
|
||||
const result = await enhancePrompt(mockApiConfig, "Test prompt")
|
||||
|
||||
it('throws error for empty prompt input', async () => {
|
||||
await expect(enhancePrompt(mockApiConfig, '')).rejects.toThrow('No prompt text provided')
|
||||
})
|
||||
expect(result).toBe("Enhanced prompt")
|
||||
const handler = buildApiHandler(mockApiConfig)
|
||||
expect((handler as any).completePrompt).toHaveBeenCalledWith(`${defaultPrompts.enhance}\n\nTest prompt`)
|
||||
})
|
||||
|
||||
it('throws error for missing API configuration', async () => {
|
||||
await expect(enhancePrompt({} as ApiConfiguration, 'Test prompt')).rejects.toThrow('No valid API configuration provided')
|
||||
})
|
||||
it("enhances prompt using custom enhancement prompt when provided", async () => {
|
||||
const customEnhancePrompt = "You are a custom prompt enhancer"
|
||||
|
||||
it('throws error for API provider that does not support prompt enhancement', async () => {
|
||||
(buildApiHandler as jest.Mock).mockReturnValue({
|
||||
// No completePrompt method
|
||||
createMessage: jest.fn(),
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: 'test-model',
|
||||
info: {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false
|
||||
}
|
||||
})
|
||||
})
|
||||
const result = await enhancePrompt(mockApiConfig, "Test prompt", customEnhancePrompt)
|
||||
|
||||
await expect(enhancePrompt(mockApiConfig, 'Test prompt')).rejects.toThrow('The selected API provider does not support prompt enhancement')
|
||||
})
|
||||
expect(result).toBe("Enhanced prompt")
|
||||
const handler = buildApiHandler(mockApiConfig)
|
||||
expect((handler as any).completePrompt).toHaveBeenCalledWith(`${customEnhancePrompt}\n\nTest prompt`)
|
||||
})
|
||||
|
||||
it('uses appropriate model based on provider', async () => {
|
||||
const openRouterConfig: ApiConfiguration = {
|
||||
apiProvider: 'openrouter',
|
||||
openRouterApiKey: 'test-key',
|
||||
openRouterModelId: 'test-model'
|
||||
}
|
||||
it("throws error for empty prompt input", async () => {
|
||||
await expect(enhancePrompt(mockApiConfig, "")).rejects.toThrow("No prompt text provided")
|
||||
})
|
||||
|
||||
// Mock successful enhancement
|
||||
;(buildApiHandler as jest.Mock).mockReturnValue({
|
||||
completePrompt: jest.fn().mockResolvedValue('Enhanced prompt'),
|
||||
createMessage: jest.fn(),
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: 'test-model',
|
||||
info: {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false
|
||||
}
|
||||
})
|
||||
} as unknown as SingleCompletionHandler)
|
||||
it("throws error for missing API configuration", async () => {
|
||||
await expect(enhancePrompt({} as ApiConfiguration, "Test prompt")).rejects.toThrow(
|
||||
"No valid API configuration provided",
|
||||
)
|
||||
})
|
||||
|
||||
const result = await enhancePrompt(openRouterConfig, 'Test prompt')
|
||||
|
||||
expect(buildApiHandler).toHaveBeenCalledWith(openRouterConfig)
|
||||
expect(result).toBe('Enhanced prompt')
|
||||
})
|
||||
it("throws error for API provider that does not support prompt enhancement", async () => {
|
||||
;(buildApiHandler as jest.Mock).mockReturnValue({
|
||||
// No completePrompt method
|
||||
createMessage: jest.fn(),
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: "test-model",
|
||||
info: {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
}),
|
||||
})
|
||||
|
||||
it('propagates API errors', async () => {
|
||||
(buildApiHandler as jest.Mock).mockReturnValue({
|
||||
completePrompt: jest.fn().mockRejectedValue(new Error('API Error')),
|
||||
createMessage: jest.fn(),
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: 'test-model',
|
||||
info: {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false
|
||||
}
|
||||
})
|
||||
} as unknown as SingleCompletionHandler)
|
||||
await expect(enhancePrompt(mockApiConfig, "Test prompt")).rejects.toThrow(
|
||||
"The selected API provider does not support prompt enhancement",
|
||||
)
|
||||
})
|
||||
|
||||
await expect(enhancePrompt(mockApiConfig, 'Test prompt')).rejects.toThrow('API Error')
|
||||
})
|
||||
})
|
||||
it("uses appropriate model based on provider", async () => {
|
||||
const openRouterConfig: ApiConfiguration = {
|
||||
apiProvider: "openrouter",
|
||||
openRouterApiKey: "test-key",
|
||||
openRouterModelId: "test-model",
|
||||
}
|
||||
|
||||
// Mock successful enhancement
|
||||
;(buildApiHandler as jest.Mock).mockReturnValue({
|
||||
completePrompt: jest.fn().mockResolvedValue("Enhanced prompt"),
|
||||
createMessage: jest.fn(),
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: "test-model",
|
||||
info: {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
}),
|
||||
} as unknown as SingleCompletionHandler)
|
||||
|
||||
const result = await enhancePrompt(openRouterConfig, "Test prompt")
|
||||
|
||||
expect(buildApiHandler).toHaveBeenCalledWith(openRouterConfig)
|
||||
expect(result).toBe("Enhanced prompt")
|
||||
})
|
||||
|
||||
it("propagates API errors", async () => {
|
||||
;(buildApiHandler as jest.Mock).mockReturnValue({
|
||||
completePrompt: jest.fn().mockRejectedValue(new Error("API Error")),
|
||||
createMessage: jest.fn(),
|
||||
getModel: jest.fn().mockReturnValue({
|
||||
id: "test-model",
|
||||
info: {
|
||||
maxTokens: 4096,
|
||||
contextWindow: 8192,
|
||||
supportsPromptCache: false,
|
||||
},
|
||||
}),
|
||||
} as unknown as SingleCompletionHandler)
|
||||
|
||||
await expect(enhancePrompt(mockApiConfig, "Test prompt")).rejects.toThrow("API Error")
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,336 +1,344 @@
|
||||
import { jest } from '@jest/globals'
|
||||
import { searchCommits, getCommitInfo, getWorkingState, GitCommit } from '../git'
|
||||
import { ExecException } from 'child_process'
|
||||
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
|
||||
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()
|
||||
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!)
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
})
|
||||
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)
|
||||
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'
|
||||
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()
|
||||
})
|
||||
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')
|
||||
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: '' }]
|
||||
])
|
||||
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}`))
|
||||
})
|
||||
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)
|
||||
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'
|
||||
})
|
||||
// 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)
|
||||
// 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'))
|
||||
})
|
||||
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))
|
||||
})
|
||||
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
|
||||
])
|
||||
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'))
|
||||
}
|
||||
})
|
||||
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))
|
||||
})
|
||||
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: '' }]
|
||||
])
|
||||
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'))
|
||||
})
|
||||
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'
|
||||
})
|
||||
})
|
||||
})
|
||||
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'
|
||||
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: '' }]
|
||||
])
|
||||
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'))
|
||||
})
|
||||
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:')
|
||||
})
|
||||
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'))
|
||||
})
|
||||
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')
|
||||
})
|
||||
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
|
||||
])
|
||||
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'))
|
||||
}
|
||||
})
|
||||
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')
|
||||
})
|
||||
})
|
||||
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'
|
||||
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: '' }]
|
||||
])
|
||||
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'))
|
||||
})
|
||||
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')
|
||||
})
|
||||
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: '' }]
|
||||
])
|
||||
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'))
|
||||
})
|
||||
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')
|
||||
})
|
||||
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'))
|
||||
})
|
||||
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')
|
||||
})
|
||||
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
|
||||
])
|
||||
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'))
|
||||
}
|
||||
})
|
||||
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')
|
||||
})
|
||||
})
|
||||
})
|
||||
const result = await getWorkingState(cwd)
|
||||
expect(result).toBe("Not a git repository")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,135 +1,135 @@
|
||||
import { arePathsEqual, getReadablePath } from '../path';
|
||||
import * as path from 'path';
|
||||
import os from 'os';
|
||||
import { arePathsEqual, getReadablePath } from "../path"
|
||||
import * as path from "path"
|
||||
import os from "os"
|
||||
|
||||
describe('Path Utilities', () => {
|
||||
const originalPlatform = process.platform;
|
||||
describe("Path Utilities", () => {
|
||||
const originalPlatform = process.platform
|
||||
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: originalPlatform
|
||||
});
|
||||
});
|
||||
afterEach(() => {
|
||||
Object.defineProperty(process, "platform", {
|
||||
value: originalPlatform,
|
||||
})
|
||||
})
|
||||
|
||||
describe('String.prototype.toPosix', () => {
|
||||
it('should convert backslashes to forward slashes', () => {
|
||||
const windowsPath = 'C:\\Users\\test\\file.txt';
|
||||
expect(windowsPath.toPosix()).toBe('C:/Users/test/file.txt');
|
||||
});
|
||||
describe("String.prototype.toPosix", () => {
|
||||
it("should convert backslashes to forward slashes", () => {
|
||||
const windowsPath = "C:\\Users\\test\\file.txt"
|
||||
expect(windowsPath.toPosix()).toBe("C:/Users/test/file.txt")
|
||||
})
|
||||
|
||||
it('should not modify paths with forward slashes', () => {
|
||||
const unixPath = '/home/user/file.txt';
|
||||
expect(unixPath.toPosix()).toBe('/home/user/file.txt');
|
||||
});
|
||||
it("should not modify paths with forward slashes", () => {
|
||||
const unixPath = "/home/user/file.txt"
|
||||
expect(unixPath.toPosix()).toBe("/home/user/file.txt")
|
||||
})
|
||||
|
||||
it('should preserve extended-length Windows paths', () => {
|
||||
const extendedPath = '\\\\?\\C:\\Very\\Long\\Path';
|
||||
expect(extendedPath.toPosix()).toBe('\\\\?\\C:\\Very\\Long\\Path');
|
||||
});
|
||||
});
|
||||
it("should preserve extended-length Windows paths", () => {
|
||||
const extendedPath = "\\\\?\\C:\\Very\\Long\\Path"
|
||||
expect(extendedPath.toPosix()).toBe("\\\\?\\C:\\Very\\Long\\Path")
|
||||
})
|
||||
})
|
||||
|
||||
describe('arePathsEqual', () => {
|
||||
describe('on Windows', () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'win32'
|
||||
});
|
||||
});
|
||||
describe("arePathsEqual", () => {
|
||||
describe("on Windows", () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process, "platform", {
|
||||
value: "win32",
|
||||
})
|
||||
})
|
||||
|
||||
it('should compare paths case-insensitively', () => {
|
||||
expect(arePathsEqual('C:\\Users\\Test', 'c:\\users\\test')).toBe(true);
|
||||
});
|
||||
it("should compare paths case-insensitively", () => {
|
||||
expect(arePathsEqual("C:\\Users\\Test", "c:\\users\\test")).toBe(true)
|
||||
})
|
||||
|
||||
it('should handle different path separators', () => {
|
||||
// Convert both paths to use forward slashes after normalization
|
||||
const path1 = path.normalize('C:\\Users\\Test').replace(/\\/g, '/');
|
||||
const path2 = path.normalize('C:/Users/Test').replace(/\\/g, '/');
|
||||
expect(arePathsEqual(path1, path2)).toBe(true);
|
||||
});
|
||||
it("should handle different path separators", () => {
|
||||
// Convert both paths to use forward slashes after normalization
|
||||
const path1 = path.normalize("C:\\Users\\Test").replace(/\\/g, "/")
|
||||
const path2 = path.normalize("C:/Users/Test").replace(/\\/g, "/")
|
||||
expect(arePathsEqual(path1, path2)).toBe(true)
|
||||
})
|
||||
|
||||
it('should normalize paths with ../', () => {
|
||||
// Convert both paths to use forward slashes after normalization
|
||||
const path1 = path.normalize('C:\\Users\\Test\\..\\Test').replace(/\\/g, '/');
|
||||
const path2 = path.normalize('C:\\Users\\Test').replace(/\\/g, '/');
|
||||
expect(arePathsEqual(path1, path2)).toBe(true);
|
||||
});
|
||||
});
|
||||
it("should normalize paths with ../", () => {
|
||||
// Convert both paths to use forward slashes after normalization
|
||||
const path1 = path.normalize("C:\\Users\\Test\\..\\Test").replace(/\\/g, "/")
|
||||
const path2 = path.normalize("C:\\Users\\Test").replace(/\\/g, "/")
|
||||
expect(arePathsEqual(path1, path2)).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('on POSIX', () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process, 'platform', {
|
||||
value: 'darwin'
|
||||
});
|
||||
});
|
||||
describe("on POSIX", () => {
|
||||
beforeEach(() => {
|
||||
Object.defineProperty(process, "platform", {
|
||||
value: "darwin",
|
||||
})
|
||||
})
|
||||
|
||||
it('should compare paths case-sensitively', () => {
|
||||
expect(arePathsEqual('/Users/Test', '/Users/test')).toBe(false);
|
||||
});
|
||||
it("should compare paths case-sensitively", () => {
|
||||
expect(arePathsEqual("/Users/Test", "/Users/test")).toBe(false)
|
||||
})
|
||||
|
||||
it('should normalize paths', () => {
|
||||
expect(arePathsEqual('/Users/./Test', '/Users/Test')).toBe(true);
|
||||
});
|
||||
it("should normalize paths", () => {
|
||||
expect(arePathsEqual("/Users/./Test", "/Users/Test")).toBe(true)
|
||||
})
|
||||
|
||||
it('should handle trailing slashes', () => {
|
||||
expect(arePathsEqual('/Users/Test/', '/Users/Test')).toBe(true);
|
||||
});
|
||||
});
|
||||
it("should handle trailing slashes", () => {
|
||||
expect(arePathsEqual("/Users/Test/", "/Users/Test")).toBe(true)
|
||||
})
|
||||
})
|
||||
|
||||
describe('edge cases', () => {
|
||||
it('should handle undefined paths', () => {
|
||||
expect(arePathsEqual(undefined, undefined)).toBe(true);
|
||||
expect(arePathsEqual('/test', undefined)).toBe(false);
|
||||
expect(arePathsEqual(undefined, '/test')).toBe(false);
|
||||
});
|
||||
describe("edge cases", () => {
|
||||
it("should handle undefined paths", () => {
|
||||
expect(arePathsEqual(undefined, undefined)).toBe(true)
|
||||
expect(arePathsEqual("/test", undefined)).toBe(false)
|
||||
expect(arePathsEqual(undefined, "/test")).toBe(false)
|
||||
})
|
||||
|
||||
it('should handle root paths with trailing slashes', () => {
|
||||
expect(arePathsEqual('/', '/')).toBe(true);
|
||||
expect(arePathsEqual('C:\\', 'C:\\')).toBe(true);
|
||||
});
|
||||
});
|
||||
});
|
||||
it("should handle root paths with trailing slashes", () => {
|
||||
expect(arePathsEqual("/", "/")).toBe(true)
|
||||
expect(arePathsEqual("C:\\", "C:\\")).toBe(true)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('getReadablePath', () => {
|
||||
const homeDir = os.homedir();
|
||||
const desktop = path.join(homeDir, 'Desktop');
|
||||
describe("getReadablePath", () => {
|
||||
const homeDir = os.homedir()
|
||||
const desktop = path.join(homeDir, "Desktop")
|
||||
|
||||
it('should return basename when path equals cwd', () => {
|
||||
const cwd = '/Users/test/project';
|
||||
expect(getReadablePath(cwd, cwd)).toBe('project');
|
||||
});
|
||||
it("should return basename when path equals cwd", () => {
|
||||
const cwd = "/Users/test/project"
|
||||
expect(getReadablePath(cwd, cwd)).toBe("project")
|
||||
})
|
||||
|
||||
it('should return relative path when inside cwd', () => {
|
||||
const cwd = '/Users/test/project';
|
||||
const filePath = '/Users/test/project/src/file.txt';
|
||||
expect(getReadablePath(cwd, filePath)).toBe('src/file.txt');
|
||||
});
|
||||
it("should return relative path when inside cwd", () => {
|
||||
const cwd = "/Users/test/project"
|
||||
const filePath = "/Users/test/project/src/file.txt"
|
||||
expect(getReadablePath(cwd, filePath)).toBe("src/file.txt")
|
||||
})
|
||||
|
||||
it('should return absolute path when outside cwd', () => {
|
||||
const cwd = '/Users/test/project';
|
||||
const filePath = '/Users/test/other/file.txt';
|
||||
expect(getReadablePath(cwd, filePath)).toBe('/Users/test/other/file.txt');
|
||||
});
|
||||
it("should return absolute path when outside cwd", () => {
|
||||
const cwd = "/Users/test/project"
|
||||
const filePath = "/Users/test/other/file.txt"
|
||||
expect(getReadablePath(cwd, filePath)).toBe("/Users/test/other/file.txt")
|
||||
})
|
||||
|
||||
it('should handle Desktop as cwd', () => {
|
||||
const filePath = path.join(desktop, 'file.txt');
|
||||
expect(getReadablePath(desktop, filePath)).toBe(filePath.toPosix());
|
||||
});
|
||||
it("should handle Desktop as cwd", () => {
|
||||
const filePath = path.join(desktop, "file.txt")
|
||||
expect(getReadablePath(desktop, filePath)).toBe(filePath.toPosix())
|
||||
})
|
||||
|
||||
it('should handle undefined relative path', () => {
|
||||
const cwd = '/Users/test/project';
|
||||
expect(getReadablePath(cwd)).toBe('project');
|
||||
});
|
||||
it("should handle undefined relative path", () => {
|
||||
const cwd = "/Users/test/project"
|
||||
expect(getReadablePath(cwd)).toBe("project")
|
||||
})
|
||||
|
||||
it('should handle parent directory traversal', () => {
|
||||
const cwd = '/Users/test/project';
|
||||
const filePath = '../../other/file.txt';
|
||||
expect(getReadablePath(cwd, filePath)).toBe('/Users/other/file.txt');
|
||||
});
|
||||
it("should handle parent directory traversal", () => {
|
||||
const cwd = "/Users/test/project"
|
||||
const filePath = "../../other/file.txt"
|
||||
expect(getReadablePath(cwd, filePath)).toBe("/Users/other/file.txt")
|
||||
})
|
||||
|
||||
it('should normalize paths with redundant segments', () => {
|
||||
const cwd = '/Users/test/project';
|
||||
const filePath = '/Users/test/project/./src/../src/file.txt';
|
||||
expect(getReadablePath(cwd, filePath)).toBe('src/file.txt');
|
||||
});
|
||||
});
|
||||
});
|
||||
it("should normalize paths with redundant segments", () => {
|
||||
const cwd = "/Users/test/project"
|
||||
const filePath = "/Users/test/project/./src/../src/file.txt"
|
||||
expect(getReadablePath(cwd, filePath)).toBe("src/file.txt")
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -6,22 +6,26 @@ import { defaultPrompts } from "../shared/modes"
|
||||
* Enhances a prompt using the configured API without creating a full Cline instance or task history.
|
||||
* This is a lightweight alternative that only uses the API's completion functionality.
|
||||
*/
|
||||
export async function enhancePrompt(apiConfiguration: ApiConfiguration, promptText: string, enhancePrompt?: string): Promise<string> {
|
||||
if (!promptText) {
|
||||
throw new Error("No prompt text provided")
|
||||
}
|
||||
if (!apiConfiguration || !apiConfiguration.apiProvider) {
|
||||
throw new Error("No valid API configuration provided")
|
||||
}
|
||||
|
||||
const handler = buildApiHandler(apiConfiguration)
|
||||
|
||||
// Check if handler supports single completions
|
||||
if (!('completePrompt' in handler)) {
|
||||
throw new Error("The selected API provider does not support prompt enhancement")
|
||||
}
|
||||
|
||||
const enhancePromptText = enhancePrompt ?? defaultPrompts.enhance
|
||||
const prompt = `${enhancePromptText}\n\n${promptText}`
|
||||
return (handler as SingleCompletionHandler).completePrompt(prompt)
|
||||
}
|
||||
export async function enhancePrompt(
|
||||
apiConfiguration: ApiConfiguration,
|
||||
promptText: string,
|
||||
enhancePrompt?: string,
|
||||
): Promise<string> {
|
||||
if (!promptText) {
|
||||
throw new Error("No prompt text provided")
|
||||
}
|
||||
if (!apiConfiguration || !apiConfiguration.apiProvider) {
|
||||
throw new Error("No valid API configuration provided")
|
||||
}
|
||||
|
||||
const handler = buildApiHandler(apiConfiguration)
|
||||
|
||||
// Check if handler supports single completions
|
||||
if (!("completePrompt" in handler)) {
|
||||
throw new Error("The selected API provider does not support prompt enhancement")
|
||||
}
|
||||
|
||||
const enhancePromptText = enhancePrompt ?? defaultPrompts.enhance
|
||||
const prompt = `${enhancePromptText}\n\n${promptText}`
|
||||
return (handler as SingleCompletionHandler).completePrompt(prompt)
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ export interface GitCommit {
|
||||
|
||||
async function checkGitRepo(cwd: string): Promise<boolean> {
|
||||
try {
|
||||
await execAsync('git rev-parse --git-dir', { cwd })
|
||||
await execAsync("git rev-parse --git-dir", { cwd })
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
@@ -24,7 +24,7 @@ async function checkGitRepo(cwd: string): Promise<boolean> {
|
||||
|
||||
async function checkGitInstalled(): Promise<boolean> {
|
||||
try {
|
||||
await execAsync('git --version')
|
||||
await execAsync("git --version")
|
||||
return true
|
||||
} catch (error) {
|
||||
return false
|
||||
@@ -47,18 +47,16 @@ export async function searchCommits(query: string, cwd: string): Promise<GitComm
|
||||
|
||||
// Search commits by hash or message, limiting to 10 results
|
||||
const { stdout } = await execAsync(
|
||||
`git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short ` +
|
||||
`--grep="${query}" --regexp-ignore-case`,
|
||||
{ cwd }
|
||||
`git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short ` + `--grep="${query}" --regexp-ignore-case`,
|
||||
{ cwd },
|
||||
)
|
||||
|
||||
let output = stdout
|
||||
if (!output.trim() && /^[a-f0-9]+$/i.test(query)) {
|
||||
// If no results from grep search and query looks like a hash, try searching by hash
|
||||
const { stdout: hashStdout } = await execAsync(
|
||||
`git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short ` +
|
||||
`--author-date-order ${query}`,
|
||||
{ cwd }
|
||||
`git log -n 10 --format="%H%n%h%n%s%n%an%n%ad" --date=short ` + `--author-date-order ${query}`,
|
||||
{ cwd },
|
||||
).catch(() => ({ stdout: "" }))
|
||||
|
||||
if (!hashStdout.trim()) {
|
||||
@@ -69,7 +67,10 @@ export async function searchCommits(query: string, cwd: string): Promise<GitComm
|
||||
}
|
||||
|
||||
const commits: GitCommit[] = []
|
||||
const lines = output.trim().split("\n").filter(line => line !== "--")
|
||||
const lines = output
|
||||
.trim()
|
||||
.split("\n")
|
||||
.filter((line) => line !== "--")
|
||||
|
||||
for (let i = 0; i < lines.length; i += 5) {
|
||||
commits.push({
|
||||
@@ -77,7 +78,7 @@ export async function searchCommits(query: string, cwd: string): Promise<GitComm
|
||||
shortHash: lines[i + 1],
|
||||
subject: lines[i + 2],
|
||||
author: lines[i + 3],
|
||||
date: lines[i + 4]
|
||||
date: lines[i + 4],
|
||||
})
|
||||
}
|
||||
|
||||
@@ -101,34 +102,27 @@ export async function getCommitInfo(hash: string, cwd: string): Promise<string>
|
||||
}
|
||||
|
||||
// Get commit info, stats, and diff separately
|
||||
const { stdout: info } = await execAsync(
|
||||
`git show --format="%H%n%h%n%s%n%an%n%ad%n%b" --no-patch ${hash}`,
|
||||
{ cwd }
|
||||
)
|
||||
const [fullHash, shortHash, subject, author, date, body] = info.trim().split('\n')
|
||||
|
||||
const { stdout: stats } = await execAsync(
|
||||
`git show --stat --format="" ${hash}`,
|
||||
{ cwd }
|
||||
)
|
||||
const { stdout: info } = await execAsync(`git show --format="%H%n%h%n%s%n%an%n%ad%n%b" --no-patch ${hash}`, {
|
||||
cwd,
|
||||
})
|
||||
const [fullHash, shortHash, subject, author, date, body] = info.trim().split("\n")
|
||||
|
||||
const { stdout: diff } = await execAsync(
|
||||
`git show --format="" ${hash}`,
|
||||
{ cwd }
|
||||
)
|
||||
const { stdout: stats } = await execAsync(`git show --stat --format="" ${hash}`, { cwd })
|
||||
|
||||
const { stdout: diff } = await execAsync(`git show --format="" ${hash}`, { cwd })
|
||||
|
||||
const summary = [
|
||||
`Commit: ${shortHash} (${fullHash})`,
|
||||
`Author: ${author}`,
|
||||
`Date: ${date}`,
|
||||
`\nMessage: ${subject}`,
|
||||
body ? `\nDescription:\n${body}` : '',
|
||||
'\nFiles Changed:',
|
||||
body ? `\nDescription:\n${body}` : "",
|
||||
"\nFiles Changed:",
|
||||
stats.trim(),
|
||||
'\nFull Changes:'
|
||||
].join('\n')
|
||||
"\nFull Changes:",
|
||||
].join("\n")
|
||||
|
||||
const output = summary + '\n\n' + diff.trim()
|
||||
const output = summary + "\n\n" + diff.trim()
|
||||
return truncateOutput(output, GIT_OUTPUT_LINE_LIMIT)
|
||||
} catch (error) {
|
||||
console.error("Error getting commit info:", error)
|
||||
@@ -149,13 +143,13 @@ export async function getWorkingState(cwd: string): Promise<string> {
|
||||
}
|
||||
|
||||
// Get status of working directory
|
||||
const { stdout: status } = await execAsync('git status --short', { cwd })
|
||||
const { stdout: status } = await execAsync("git status --short", { cwd })
|
||||
if (!status.trim()) {
|
||||
return "No changes in working directory"
|
||||
}
|
||||
|
||||
// Get all changes (both staged and unstaged) compared to HEAD
|
||||
const { stdout: diff } = await execAsync('git diff HEAD', { cwd })
|
||||
const { stdout: diff } = await execAsync("git diff HEAD", { cwd })
|
||||
const lineLimit = GIT_OUTPUT_LINE_LIMIT
|
||||
const output = `Working directory changes:\n\n${status}\n\n${diff}`.trim()
|
||||
return truncateOutput(output, lineLimit)
|
||||
@@ -163,4 +157,4 @@ export async function getWorkingState(cwd: string): Promise<string> {
|
||||
console.error("Error getting working state:", error)
|
||||
return `Failed to get working state: ${error instanceof Error ? error.message : String(error)}`
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ export const isWAV = (filepath: string): boolean => {
|
||||
}
|
||||
|
||||
let isSoundEnabled = false
|
||||
let volume = .5
|
||||
let volume = 0.5
|
||||
|
||||
/**
|
||||
* Set sound configuration
|
||||
|
||||
Reference in New Issue
Block a user