mirror of
https://github.com/pacnpal/Roo-Code.git
synced 2025-12-20 04:11:10 -05:00
Add snapshot tests for system prompts to ensure modifications are intentional
This commit is contained in:
2054
src/core/prompts/__tests__/__snapshots__/system.test.ts.snap
Normal file
2054
src/core/prompts/__tests__/__snapshots__/system.test.ts.snap
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,112 +1,203 @@
|
||||
import { SYSTEM_PROMPT, addCustomInstructions } from '../system'
|
||||
import { McpHub } from '../../../services/mcp/McpHub'
|
||||
import { McpServer } from '../../../shared/mcp'
|
||||
import { ClineProvider } from '../../../core/webview/ClineProvider'
|
||||
import { SearchReplaceDiffStrategy } from '../../../core/diff/strategies/search-replace'
|
||||
import fs from 'fs/promises'
|
||||
import path from 'path'
|
||||
import os from 'os'
|
||||
import { addCustomInstructions } from '../system'
|
||||
// Import path utils to get access to toPosix string extension
|
||||
import '../../../utils/path'
|
||||
|
||||
// Mock external dependencies
|
||||
jest.mock('os-name', () => () => 'macOS')
|
||||
jest.mock('default-shell', () => '/bin/zsh')
|
||||
// Mock environment-specific values for consistent tests
|
||||
jest.mock('os', () => ({
|
||||
homedir: () => '/Users/test',
|
||||
...jest.requireActual('os')
|
||||
...jest.requireActual('os'),
|
||||
homedir: () => '/home/user'
|
||||
}))
|
||||
|
||||
describe('system.ts', () => {
|
||||
let tempDir: string
|
||||
jest.mock('default-shell', () => '/bin/bash')
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create a temporary directory for test files
|
||||
tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'cline-test-'))
|
||||
jest.mock('os-name', () => () => 'Linux')
|
||||
|
||||
// Mock fs.readFile to return empty mcpServers config and mock .clinerules
|
||||
jest.mock('fs/promises', () => ({
|
||||
...jest.requireActual('fs/promises'),
|
||||
readFile: jest.fn().mockImplementation(async (path: string) => {
|
||||
if (path.endsWith('mcpSettings.json')) {
|
||||
return '{"mcpServers": {}}'
|
||||
}
|
||||
if (path.endsWith('.clinerules')) {
|
||||
return '# Test Rules\n1. First rule\n2. Second rule'
|
||||
}
|
||||
return ''
|
||||
}),
|
||||
writeFile: jest.fn().mockResolvedValue(undefined)
|
||||
}))
|
||||
|
||||
// Create a minimal mock of ClineProvider
|
||||
const mockProvider = {
|
||||
ensureMcpServersDirectoryExists: async () => '/mock/mcp/path',
|
||||
ensureSettingsDirectoryExists: async () => '/mock/settings/path',
|
||||
postMessageToWebview: async () => {},
|
||||
context: {
|
||||
extension: {
|
||||
packageJSON: {
|
||||
version: '1.0.0'
|
||||
}
|
||||
}
|
||||
}
|
||||
} as unknown as ClineProvider
|
||||
|
||||
// Instead of extending McpHub, create a mock that implements just what we need
|
||||
const createMockMcpHub = (): McpHub => ({
|
||||
getServers: () => [],
|
||||
getMcpServersPath: async () => '/mock/mcp/path',
|
||||
getMcpSettingsFilePath: async () => '/mock/settings/path',
|
||||
dispose: async () => {},
|
||||
// Add other required public methods with no-op implementations
|
||||
restartConnection: async () => {},
|
||||
readResource: async () => ({ contents: [] }),
|
||||
callTool: async () => ({ content: [] }),
|
||||
toggleServerDisabled: async () => {},
|
||||
toggleToolAlwaysAllow: async () => {},
|
||||
isConnecting: false,
|
||||
connections: []
|
||||
} as unknown as McpHub)
|
||||
|
||||
describe('SYSTEM_PROMPT', () => {
|
||||
let mockMcpHub: McpHub
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
afterEach(async () => {
|
||||
// Clean up temporary directory after each test
|
||||
await fs.rm(tempDir, { recursive: true, force: true })
|
||||
// Clean up any McpHub instances
|
||||
if (mockMcpHub) {
|
||||
await mockMcpHub.dispose()
|
||||
}
|
||||
})
|
||||
|
||||
describe('addCustomInstructions', () => {
|
||||
it('should include content from .clinerules and .cursorrules if present', async () => {
|
||||
// Create test rule files
|
||||
await fs.writeFile(path.join(tempDir, '.clinerules'), 'Always write tests\nUse TypeScript')
|
||||
await fs.writeFile(path.join(tempDir, '.cursorrules'), 'Format code before committing')
|
||||
it('should maintain consistent system prompt', async () => {
|
||||
const prompt = await SYSTEM_PROMPT(
|
||||
'/test/path',
|
||||
false, // supportsComputerUse
|
||||
undefined, // mcpHub
|
||||
undefined, // diffStrategy
|
||||
undefined // browserViewportSize
|
||||
)
|
||||
|
||||
expect(prompt).toMatchSnapshot()
|
||||
})
|
||||
|
||||
const customInstructions = 'Base instructions'
|
||||
const result = await addCustomInstructions(customInstructions, tempDir)
|
||||
it('should include browser actions when supportsComputerUse is true', async () => {
|
||||
const prompt = await SYSTEM_PROMPT(
|
||||
'/test/path',
|
||||
true,
|
||||
undefined,
|
||||
undefined,
|
||||
'1280x800'
|
||||
)
|
||||
|
||||
expect(prompt).toMatchSnapshot()
|
||||
})
|
||||
|
||||
// Verify all instructions are included
|
||||
expect(result).toContain('Base instructions')
|
||||
expect(result).toContain('Always write tests')
|
||||
expect(result).toContain('Use TypeScript')
|
||||
expect(result).toContain('Format code before committing')
|
||||
expect(result).toContain('Rules from .clinerules:')
|
||||
expect(result).toContain('Rules from .cursorrules:')
|
||||
})
|
||||
it('should include MCP server info when mcpHub is provided', async () => {
|
||||
mockMcpHub = createMockMcpHub()
|
||||
|
||||
it('should handle missing rule files gracefully', async () => {
|
||||
const customInstructions = 'Base instructions'
|
||||
const result = await addCustomInstructions(customInstructions, tempDir)
|
||||
const prompt = await SYSTEM_PROMPT(
|
||||
'/test/path',
|
||||
false,
|
||||
mockMcpHub
|
||||
)
|
||||
|
||||
expect(prompt).toMatchSnapshot()
|
||||
})
|
||||
|
||||
// Should only contain base instructions
|
||||
expect(result).toContain('Base instructions')
|
||||
expect(result).not.toContain('Rules from')
|
||||
})
|
||||
it('should explicitly handle undefined mcpHub', async () => {
|
||||
const prompt = await SYSTEM_PROMPT(
|
||||
'/test/path',
|
||||
false,
|
||||
undefined, // explicitly undefined mcpHub
|
||||
undefined,
|
||||
undefined
|
||||
)
|
||||
|
||||
expect(prompt).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should handle empty rule files', async () => {
|
||||
// Create empty rule files
|
||||
await fs.writeFile(path.join(tempDir, '.clinerules'), '')
|
||||
await fs.writeFile(path.join(tempDir, '.cursorrules'), '')
|
||||
it('should handle different browser viewport sizes', async () => {
|
||||
const prompt = await SYSTEM_PROMPT(
|
||||
'/test/path',
|
||||
true,
|
||||
undefined,
|
||||
undefined,
|
||||
'900x600' // different viewport size
|
||||
)
|
||||
|
||||
expect(prompt).toMatchSnapshot()
|
||||
})
|
||||
|
||||
const customInstructions = 'Base instructions'
|
||||
const result = await addCustomInstructions(customInstructions, tempDir)
|
||||
it('should include diff strategy tool description', async () => {
|
||||
const prompt = await SYSTEM_PROMPT(
|
||||
'/test/path',
|
||||
false,
|
||||
undefined,
|
||||
new SearchReplaceDiffStrategy(), // Use actual diff strategy from the codebase
|
||||
undefined
|
||||
)
|
||||
|
||||
expect(prompt).toMatchSnapshot()
|
||||
})
|
||||
|
||||
// Should only contain base instructions
|
||||
expect(result).toContain('Base instructions')
|
||||
expect(result).not.toContain('Rules from')
|
||||
})
|
||||
|
||||
it('should handle whitespace-only rule files', async () => {
|
||||
// Create rule files with only whitespace
|
||||
await fs.writeFile(path.join(tempDir, '.clinerules'), ' \n \t ')
|
||||
await fs.writeFile(path.join(tempDir, '.cursorrules'), ' \n ')
|
||||
|
||||
const customInstructions = 'Base instructions'
|
||||
const result = await addCustomInstructions(customInstructions, tempDir)
|
||||
|
||||
// Should only contain base instructions
|
||||
expect(result).toContain('Base instructions')
|
||||
expect(result).not.toContain('Rules from')
|
||||
})
|
||||
|
||||
it('should handle one rule file present and one missing', async () => {
|
||||
// Create only .clinerules
|
||||
await fs.writeFile(path.join(tempDir, '.clinerules'), 'Always write tests')
|
||||
|
||||
const customInstructions = 'Base instructions'
|
||||
const result = await addCustomInstructions(customInstructions, tempDir)
|
||||
|
||||
// Should contain base instructions and .clinerules content
|
||||
expect(result).toContain('Base instructions')
|
||||
expect(result).toContain('Always write tests')
|
||||
expect(result).toContain('Rules from .clinerules:')
|
||||
expect(result).not.toContain('Rules from .cursorrules:')
|
||||
})
|
||||
|
||||
it('should handle empty custom instructions with rule files', async () => {
|
||||
await fs.writeFile(path.join(tempDir, '.clinerules'), 'Always write tests')
|
||||
await fs.writeFile(path.join(tempDir, '.cursorrules'), 'Format code before committing')
|
||||
|
||||
const result = await addCustomInstructions('', tempDir)
|
||||
|
||||
// Should contain rule file content even with empty custom instructions
|
||||
expect(result).toContain('Always write tests')
|
||||
expect(result).toContain('Format code before committing')
|
||||
expect(result).toContain('Rules from .clinerules:')
|
||||
expect(result).toContain('Rules from .cursorrules:')
|
||||
})
|
||||
|
||||
it('should return empty string when no instructions or rules exist', async () => {
|
||||
const result = await addCustomInstructions('', tempDir)
|
||||
expect(result).toBe('')
|
||||
})
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
})
|
||||
|
||||
describe('addCustomInstructions', () => {
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks()
|
||||
})
|
||||
|
||||
it('should include preferred language when provided', async () => {
|
||||
const result = await addCustomInstructions(
|
||||
'',
|
||||
'/test/path',
|
||||
'Spanish'
|
||||
)
|
||||
|
||||
expect(result).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should include custom instructions when provided', async () => {
|
||||
const result = await addCustomInstructions(
|
||||
'Custom test instructions',
|
||||
'/test/path'
|
||||
)
|
||||
|
||||
expect(result).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should include rules from .clinerules', async () => {
|
||||
const result = await addCustomInstructions(
|
||||
'',
|
||||
'/test/path'
|
||||
)
|
||||
|
||||
expect(result).toMatchSnapshot()
|
||||
})
|
||||
|
||||
it('should combine all custom instructions', async () => {
|
||||
const result = await addCustomInstructions(
|
||||
'Custom test instructions',
|
||||
'/test/path',
|
||||
'French'
|
||||
)
|
||||
|
||||
expect(result).toMatchSnapshot()
|
||||
})
|
||||
|
||||
afterAll(() => {
|
||||
jest.restoreAllMocks()
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user