chore: use github's shared prettier-config
This commit is contained in:
@@ -1,41 +1,37 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {
|
||||
buildMessages,
|
||||
buildResponseFormat,
|
||||
buildInferenceRequest
|
||||
} from '../src/helpers'
|
||||
import { PromptConfig } from '../src/prompt'
|
||||
import {describe, it, expect} from 'vitest'
|
||||
import {buildMessages, buildResponseFormat, buildInferenceRequest} from '../src/helpers'
|
||||
import {PromptConfig} from '../src/prompt'
|
||||
|
||||
describe('helpers.ts - inference request building', () => {
|
||||
describe('buildMessages', () => {
|
||||
it('should build messages from prompt config', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: [
|
||||
{ role: 'system', content: 'System message' },
|
||||
{ role: 'user', content: 'User message' }
|
||||
]
|
||||
{role: 'system', content: 'System message'},
|
||||
{role: 'user', content: 'User message'},
|
||||
],
|
||||
}
|
||||
|
||||
const result = buildMessages(promptConfig)
|
||||
expect(result).toEqual([
|
||||
{ role: 'system', content: 'System message' },
|
||||
{ role: 'user', content: 'User message' }
|
||||
{role: 'system', content: 'System message'},
|
||||
{role: 'user', content: 'User message'},
|
||||
])
|
||||
})
|
||||
|
||||
it('should build messages from legacy format', () => {
|
||||
const result = buildMessages(undefined, 'System prompt', 'User prompt')
|
||||
expect(result).toEqual([
|
||||
{ role: 'system', content: 'System prompt' },
|
||||
{ role: 'user', content: 'User prompt' }
|
||||
{role: 'system', content: 'System prompt'},
|
||||
{role: 'user', content: 'User prompt'},
|
||||
])
|
||||
})
|
||||
|
||||
it('should use default system prompt when none provided', () => {
|
||||
const result = buildMessages(undefined, undefined, 'User prompt')
|
||||
expect(result).toEqual([
|
||||
{ role: 'system', content: 'You are a helpful assistant' },
|
||||
{ role: 'user', content: 'User prompt' }
|
||||
{role: 'system', content: 'You are a helpful assistant'},
|
||||
{role: 'user', content: 'User prompt'},
|
||||
])
|
||||
})
|
||||
})
|
||||
@@ -47,8 +43,8 @@ describe('helpers.ts - inference request building', () => {
|
||||
responseFormat: 'json_schema',
|
||||
jsonSchema: JSON.stringify({
|
||||
name: 'test_schema',
|
||||
schema: { type: 'object' }
|
||||
})
|
||||
schema: {type: 'object'},
|
||||
}),
|
||||
}
|
||||
|
||||
const result = buildResponseFormat(promptConfig)
|
||||
@@ -56,15 +52,15 @@ describe('helpers.ts - inference request building', () => {
|
||||
type: 'json_schema',
|
||||
json_schema: {
|
||||
name: 'test_schema',
|
||||
schema: { type: 'object' }
|
||||
}
|
||||
schema: {type: 'object'},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('should return undefined for text format', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: [],
|
||||
responseFormat: 'text'
|
||||
responseFormat: 'text',
|
||||
}
|
||||
|
||||
const result = buildResponseFormat(promptConfig)
|
||||
@@ -73,7 +69,7 @@ describe('helpers.ts - inference request building', () => {
|
||||
|
||||
it('should return undefined when no response format specified', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: []
|
||||
messages: [],
|
||||
}
|
||||
|
||||
const result = buildResponseFormat(promptConfig)
|
||||
@@ -84,12 +80,10 @@ describe('helpers.ts - inference request building', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: [],
|
||||
responseFormat: 'json_schema',
|
||||
jsonSchema: 'invalid json'
|
||||
jsonSchema: 'invalid json',
|
||||
}
|
||||
|
||||
expect(() => buildResponseFormat(promptConfig)).toThrow(
|
||||
'Invalid JSON schema'
|
||||
)
|
||||
expect(() => buildResponseFormat(promptConfig)).toThrow('Invalid JSON schema')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -97,14 +91,14 @@ describe('helpers.ts - inference request building', () => {
|
||||
it('should build complete inference request from prompt config', () => {
|
||||
const promptConfig: PromptConfig = {
|
||||
messages: [
|
||||
{ role: 'system', content: 'System message' },
|
||||
{ role: 'user', content: 'User message' }
|
||||
{role: 'system', content: 'System message'},
|
||||
{role: 'user', content: 'User message'},
|
||||
],
|
||||
responseFormat: 'json_schema',
|
||||
jsonSchema: JSON.stringify({
|
||||
name: 'test_schema',
|
||||
schema: { type: 'object' }
|
||||
})
|
||||
schema: {type: 'object'},
|
||||
}),
|
||||
}
|
||||
|
||||
const result = buildInferenceRequest(
|
||||
@@ -114,13 +108,13 @@ describe('helpers.ts - inference request building', () => {
|
||||
'gpt-4',
|
||||
100,
|
||||
'https://api.test.com',
|
||||
'test-token'
|
||||
'test-token',
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
messages: [
|
||||
{ role: 'system', content: 'System message' },
|
||||
{ role: 'user', content: 'User message' }
|
||||
{role: 'system', content: 'System message'},
|
||||
{role: 'user', content: 'User message'},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
@@ -130,9 +124,9 @@ describe('helpers.ts - inference request building', () => {
|
||||
type: 'json_schema',
|
||||
json_schema: {
|
||||
name: 'test_schema',
|
||||
schema: { type: 'object' }
|
||||
}
|
||||
}
|
||||
schema: {type: 'object'},
|
||||
},
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -144,19 +138,19 @@ describe('helpers.ts - inference request building', () => {
|
||||
'gpt-4',
|
||||
100,
|
||||
'https://api.test.com',
|
||||
'test-token'
|
||||
'test-token',
|
||||
)
|
||||
|
||||
expect(result).toEqual({
|
||||
messages: [
|
||||
{ role: 'system', content: 'System prompt' },
|
||||
{ role: 'user', content: 'User prompt' }
|
||||
{role: 'system', content: 'System prompt'},
|
||||
{role: 'user', content: 'User prompt'},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
endpoint: 'https://api.test.com',
|
||||
token: 'test-token',
|
||||
responseFormat: undefined
|
||||
responseFormat: undefined,
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { vi, it, expect, beforeEach, describe } from 'vitest'
|
||||
import {vi, it, expect, beforeEach, describe} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
const mockExistsSync = vi.fn()
|
||||
@@ -6,12 +6,12 @@ const mockReadFileSync = vi.fn()
|
||||
|
||||
vi.mock('fs', () => ({
|
||||
existsSync: mockExistsSync,
|
||||
readFileSync: mockReadFileSync
|
||||
readFileSync: mockReadFileSync,
|
||||
}))
|
||||
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
const { loadContentFromFileOrInput } = await import('../src/helpers.js')
|
||||
const {loadContentFromFileOrInput} = await import('../src/helpers.js')
|
||||
|
||||
describe('helpers.ts', () => {
|
||||
beforeEach(() => {
|
||||
@@ -103,11 +103,7 @@ describe('helpers.ts', () => {
|
||||
|
||||
core.getInput.mockImplementation(() => '')
|
||||
|
||||
const result = loadContentFromFileOrInput(
|
||||
'file-input',
|
||||
'content-input',
|
||||
defaultValue
|
||||
)
|
||||
const result = loadContentFromFileOrInput('file-input', 'content-input', defaultValue)
|
||||
|
||||
expect(result).toBe(defaultValue)
|
||||
expect(mockExistsSync).not.toHaveBeenCalled()
|
||||
@@ -131,11 +127,7 @@ describe('helpers.ts', () => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
core.getInput.mockImplementation(() => undefined as any)
|
||||
|
||||
const result = loadContentFromFileOrInput(
|
||||
'file-input',
|
||||
'content-input',
|
||||
defaultValue
|
||||
)
|
||||
const result = loadContentFromFileOrInput('file-input', 'content-input', defaultValue)
|
||||
|
||||
expect(result).toBe(defaultValue)
|
||||
})
|
||||
|
||||
@@ -1,48 +1,41 @@
|
||||
import {
|
||||
vi,
|
||||
type MockedFunction,
|
||||
beforeEach,
|
||||
expect,
|
||||
describe,
|
||||
it
|
||||
} from 'vitest'
|
||||
import {vi, type MockedFunction, beforeEach, expect, describe, it} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockPost = vi.fn() as MockedFunction<any>
|
||||
const mockPath = vi.fn(() => ({ post: mockPost }))
|
||||
const mockClient = vi.fn(() => ({ path: mockPath }))
|
||||
const mockPath = vi.fn(() => ({post: mockPost}))
|
||||
const mockClient = vi.fn(() => ({path: mockPath}))
|
||||
|
||||
vi.mock('@azure-rest/ai-inference', () => ({
|
||||
default: mockClient,
|
||||
isUnexpected: vi.fn(() => false)
|
||||
isUnexpected: vi.fn(() => false),
|
||||
}))
|
||||
|
||||
vi.mock('@azure/core-auth', () => ({
|
||||
AzureKeyCredential: vi.fn()
|
||||
AzureKeyCredential: vi.fn(),
|
||||
}))
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
const mockExecuteToolCalls = vi.fn() as MockedFunction<any>
|
||||
vi.mock('../src/mcp.js', () => ({
|
||||
executeToolCalls: mockExecuteToolCalls
|
||||
executeToolCalls: mockExecuteToolCalls,
|
||||
}))
|
||||
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// Import the module being tested
|
||||
const { simpleInference, mcpInference } = await import('../src/inference.js')
|
||||
const {simpleInference, mcpInference} = await import('../src/inference.js')
|
||||
|
||||
describe('inference.ts', () => {
|
||||
const mockRequest = {
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a test assistant' },
|
||||
{ role: 'user', content: 'Hello, AI!' }
|
||||
{role: 'system', content: 'You are a test assistant'},
|
||||
{role: 'user', content: 'Hello, AI!'},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
endpoint: 'https://api.test.com',
|
||||
token: 'test-token'
|
||||
token: 'test-token',
|
||||
}
|
||||
|
||||
beforeEach(() => {
|
||||
@@ -56,11 +49,11 @@ describe('inference.ts', () => {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: 'Hello, user!'
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
content: 'Hello, user!',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(mockResponse)
|
||||
@@ -68,9 +61,7 @@ describe('inference.ts', () => {
|
||||
const result = await simpleInference(mockRequest)
|
||||
|
||||
expect(result).toBe('Hello, user!')
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Running simple inference without tools'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Running simple inference without tools')
|
||||
expect(core.info).toHaveBeenCalledWith('Model response: Hello, user!')
|
||||
|
||||
// Verify the request structure
|
||||
@@ -79,16 +70,16 @@ describe('inference.ts', () => {
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'You are a test assistant'
|
||||
content: 'You are a test assistant',
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, AI!'
|
||||
}
|
||||
content: 'Hello, AI!',
|
||||
},
|
||||
],
|
||||
max_tokens: 100,
|
||||
model: 'gpt-4'
|
||||
}
|
||||
model: 'gpt-4',
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
@@ -98,11 +89,11 @@ describe('inference.ts', () => {
|
||||
choices: [
|
||||
{
|
||||
message: {
|
||||
content: null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
content: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(mockResponse)
|
||||
@@ -110,9 +101,7 @@ describe('inference.ts', () => {
|
||||
const result = await simpleInference(mockRequest)
|
||||
|
||||
expect(result).toBeNull()
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Model response: No response content'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Model response: No response content')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -126,10 +115,10 @@ describe('inference.ts', () => {
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
description: 'A test tool',
|
||||
parameters: { type: 'object' }
|
||||
}
|
||||
}
|
||||
]
|
||||
parameters: {type: 'object'},
|
||||
},
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
it('performs inference without tool calls', async () => {
|
||||
@@ -139,11 +128,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Hello, user!',
|
||||
tool_calls: null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(mockResponse)
|
||||
@@ -151,13 +140,9 @@ describe('inference.ts', () => {
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
expect(result).toBe('Hello, user!')
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Running GitHub MCP inference with tools'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Running GitHub MCP inference with tools')
|
||||
expect(core.info).toHaveBeenCalledWith('MCP inference iteration 1')
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'No tool calls requested, ending GitHub MCP inference loop'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('No tool calls requested, ending GitHub MCP inference loop')
|
||||
|
||||
// The MCP inference loop will always add the assistant message, even when there are no tool calls
|
||||
// So we don't check the exact messages, just that tools were included
|
||||
@@ -175,9 +160,9 @@ describe('inference.ts', () => {
|
||||
id: 'call-123',
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
arguments: '{"param": "value"}'
|
||||
}
|
||||
}
|
||||
arguments: '{"param": "value"}',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const toolResults = [
|
||||
@@ -185,8 +170,8 @@ describe('inference.ts', () => {
|
||||
tool_call_id: 'call-123',
|
||||
role: 'tool',
|
||||
name: 'test-tool',
|
||||
content: 'Tool result'
|
||||
}
|
||||
content: 'Tool result',
|
||||
},
|
||||
]
|
||||
|
||||
// First response with tool calls
|
||||
@@ -196,11 +181,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'I need to use a tool.',
|
||||
tool_calls: toolCalls
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: toolCalls,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
// Second response after tool execution
|
||||
@@ -210,26 +195,21 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Here is the final answer.',
|
||||
tool_calls: null
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: null,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost
|
||||
.mockResolvedValueOnce(firstResponse)
|
||||
.mockResolvedValueOnce(secondResponse)
|
||||
mockPost.mockResolvedValueOnce(firstResponse).mockResolvedValueOnce(secondResponse)
|
||||
|
||||
mockExecuteToolCalls.mockResolvedValue(toolResults)
|
||||
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
expect(result).toBe('Here is the final answer.')
|
||||
expect(mockExecuteToolCalls).toHaveBeenCalledWith(
|
||||
mockMcpClient.client,
|
||||
toolCalls
|
||||
)
|
||||
expect(mockExecuteToolCalls).toHaveBeenCalledWith(mockMcpClient.client, toolCalls)
|
||||
expect(mockPost).toHaveBeenCalledTimes(2)
|
||||
|
||||
// Verify the second call includes the conversation history
|
||||
@@ -247,9 +227,9 @@ describe('inference.ts', () => {
|
||||
id: 'call-123',
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
arguments: '{}'
|
||||
}
|
||||
}
|
||||
arguments: '{}',
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
const toolResults = [
|
||||
@@ -257,8 +237,8 @@ describe('inference.ts', () => {
|
||||
tool_call_id: 'call-123',
|
||||
role: 'tool',
|
||||
name: 'test-tool',
|
||||
content: 'Tool result'
|
||||
}
|
||||
content: 'Tool result',
|
||||
},
|
||||
]
|
||||
|
||||
// Always respond with tool calls to trigger infinite loop
|
||||
@@ -268,11 +248,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Using tool again.',
|
||||
tool_calls: toolCalls
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: toolCalls,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(responseWithToolCalls)
|
||||
@@ -281,9 +261,7 @@ describe('inference.ts', () => {
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
expect(mockPost).toHaveBeenCalledTimes(5) // Max iterations reached
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'GitHub MCP inference loop exceeded maximum iterations (5)'
|
||||
)
|
||||
expect(core.warning).toHaveBeenCalledWith('GitHub MCP inference loop exceeded maximum iterations (5)')
|
||||
expect(result).toBe('Using tool again.') // Last assistant message
|
||||
})
|
||||
|
||||
@@ -294,11 +272,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Hello, user!',
|
||||
tool_calls: []
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: [],
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost.mockResolvedValue(mockResponse)
|
||||
@@ -306,9 +284,7 @@ describe('inference.ts', () => {
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
expect(result).toBe('Hello, user!')
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'No tool calls requested, ending GitHub MCP inference loop'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('No tool calls requested, ending GitHub MCP inference loop')
|
||||
expect(mockExecuteToolCalls).not.toHaveBeenCalled()
|
||||
})
|
||||
|
||||
@@ -316,8 +292,8 @@ describe('inference.ts', () => {
|
||||
const toolCalls = [
|
||||
{
|
||||
id: 'call-123',
|
||||
function: { name: 'test-tool', arguments: '{}' }
|
||||
}
|
||||
function: {name: 'test-tool', arguments: '{}'},
|
||||
},
|
||||
]
|
||||
|
||||
const firstResponse = {
|
||||
@@ -326,11 +302,11 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'First message',
|
||||
tool_calls: toolCalls
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: toolCalls,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
const secondResponse = {
|
||||
@@ -339,24 +315,22 @@ describe('inference.ts', () => {
|
||||
{
|
||||
message: {
|
||||
content: 'Second message',
|
||||
tool_calls: toolCalls
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
tool_calls: toolCalls,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
mockPost
|
||||
.mockResolvedValueOnce(firstResponse)
|
||||
.mockResolvedValue(secondResponse)
|
||||
mockPost.mockResolvedValueOnce(firstResponse).mockResolvedValue(secondResponse)
|
||||
|
||||
mockExecuteToolCalls.mockResolvedValue([
|
||||
{
|
||||
tool_call_id: 'call-123',
|
||||
role: 'tool',
|
||||
name: 'test-tool',
|
||||
content: 'result'
|
||||
}
|
||||
content: 'result',
|
||||
},
|
||||
])
|
||||
|
||||
const result = await mcpInference(mockRequest, mockMcpClient)
|
||||
|
||||
@@ -1,12 +1,4 @@
|
||||
import {
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach,
|
||||
vi,
|
||||
type MockedFunction,
|
||||
type Mock
|
||||
} from 'vitest'
|
||||
import {describe, it, expect, beforeEach, vi, type MockedFunction, type Mock} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// Create fs mocks
|
||||
@@ -26,25 +18,25 @@ const mockConnectToGitHubMCP = vi.fn()
|
||||
vi.mock('fs', () => ({
|
||||
existsSync: mockExistsSync,
|
||||
readFileSync: mockReadFileSync,
|
||||
writeFileSync: mockWriteFileSync
|
||||
writeFileSync: mockWriteFileSync,
|
||||
}))
|
||||
|
||||
// Mock the inference functions
|
||||
vi.mock('../src/inference.js', () => ({
|
||||
simpleInference: mockSimpleInference,
|
||||
mcpInference: mockMcpInference
|
||||
mcpInference: mockMcpInference,
|
||||
}))
|
||||
|
||||
// Mock the MCP connection
|
||||
vi.mock('../src/mcp.js', () => ({
|
||||
connectToGitHubMCP: mockConnectToGitHubMCP
|
||||
connectToGitHubMCP: mockConnectToGitHubMCP,
|
||||
}))
|
||||
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// The module being tested should be imported dynamically. This ensures that the
|
||||
// mocks are used in place of any actual dependencies.
|
||||
const { run } = await import('../src/main.js')
|
||||
const {run} = await import('../src/main.js')
|
||||
|
||||
describe('main.ts - prompt.yml integration', () => {
|
||||
beforeEach(() => {
|
||||
@@ -119,29 +111,23 @@ model: openai/gpt-4o
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'Be as concise as possible'
|
||||
content: 'Be as concise as possible',
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Compare cats and dogs, please'
|
||||
}
|
||||
content: 'Compare cats and dogs, please',
|
||||
},
|
||||
],
|
||||
modelName: 'openai/gpt-4o',
|
||||
maxTokens: 200,
|
||||
endpoint: 'https://models.github.ai/inference',
|
||||
token: 'test-token'
|
||||
})
|
||||
token: 'test-token',
|
||||
}),
|
||||
)
|
||||
|
||||
// Verify outputs were set
|
||||
expect(core.setOutput).toHaveBeenCalledWith(
|
||||
'response',
|
||||
'Mocked AI response'
|
||||
)
|
||||
expect(core.setOutput).toHaveBeenCalledWith(
|
||||
'response-file',
|
||||
expect.any(String)
|
||||
)
|
||||
expect(core.setOutput).toHaveBeenCalledWith('response', 'Mocked AI response')
|
||||
expect(core.setOutput).toHaveBeenCalledWith('response-file', expect.any(String))
|
||||
})
|
||||
|
||||
it('should fall back to legacy format when not using prompt YAML', async () => {
|
||||
@@ -173,18 +159,18 @@ model: openai/gpt-4o
|
||||
messages: [
|
||||
{
|
||||
role: 'system',
|
||||
content: 'You are helpful'
|
||||
content: 'You are helpful',
|
||||
},
|
||||
{
|
||||
role: 'user',
|
||||
content: 'Hello, world!'
|
||||
}
|
||||
content: 'Hello, world!',
|
||||
},
|
||||
],
|
||||
modelName: 'openai/gpt-4o',
|
||||
maxTokens: 200,
|
||||
endpoint: 'https://models.github.ai/inference',
|
||||
token: 'test-token'
|
||||
})
|
||||
token: 'test-token',
|
||||
}),
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
import {
|
||||
vi,
|
||||
describe,
|
||||
expect,
|
||||
it,
|
||||
beforeEach,
|
||||
type MockedFunction
|
||||
} from 'vitest'
|
||||
import {vi, describe, expect, it, beforeEach, type MockedFunction} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// Default to throwing errors to catch unexpected calls
|
||||
const mockExistsSync = vi.fn().mockImplementation(() => {
|
||||
throw new Error(
|
||||
'Unexpected call to existsSync - test should override this implementation'
|
||||
)
|
||||
throw new Error('Unexpected call to existsSync - test should override this implementation')
|
||||
})
|
||||
const mockReadFileSync = vi.fn().mockImplementation(() => {
|
||||
throw new Error(
|
||||
'Unexpected call to readFileSync - test should override this implementation'
|
||||
)
|
||||
throw new Error('Unexpected call to readFileSync - test should override this implementation')
|
||||
})
|
||||
const mockWriteFileSync = vi.fn()
|
||||
|
||||
@@ -26,10 +15,7 @@ const mockWriteFileSync = vi.fn()
|
||||
* @param fileContents - Object mapping file paths to their contents
|
||||
* @param nonExistentFiles - Array of file paths that should be treated as non-existent
|
||||
*/
|
||||
function mockFileContent(
|
||||
fileContents: Record<string, string> = {},
|
||||
nonExistentFiles: string[] = []
|
||||
): void {
|
||||
function mockFileContent(fileContents: Record<string, string> = {}, nonExistentFiles: string[] = []): void {
|
||||
// Mock existsSync to return true for files that exist, false for those that don't
|
||||
mockExistsSync.mockImplementation((...args: unknown[]): boolean => {
|
||||
const [path] = args as [string]
|
||||
@@ -59,11 +45,11 @@ function mockInputs(inputs: Record<string, string> = {}): void {
|
||||
token: 'fake-token',
|
||||
model: 'gpt-4',
|
||||
'max-tokens': '100',
|
||||
endpoint: 'https://api.test.com'
|
||||
endpoint: 'https://api.test.com',
|
||||
}
|
||||
|
||||
// Combine defaults with user-provided inputs
|
||||
const allInputs: Record<string, string> = { ...defaultInputs, ...inputs }
|
||||
const allInputs: Record<string, string> = {...defaultInputs, ...inputs}
|
||||
|
||||
core.getInput.mockImplementation((name: string) => {
|
||||
return allInputs[name] || ''
|
||||
@@ -80,17 +66,13 @@ function mockInputs(inputs: Record<string, string> = {}): void {
|
||||
*/
|
||||
function verifyStandardResponse(): void {
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(1, 'response', 'Hello, user!')
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'response-file',
|
||||
expect.stringContaining('modelResponse.txt')
|
||||
)
|
||||
expect(core.setOutput).toHaveBeenNthCalledWith(2, 'response-file', expect.stringContaining('modelResponse.txt'))
|
||||
}
|
||||
|
||||
vi.mock('fs', () => ({
|
||||
existsSync: mockExistsSync,
|
||||
readFileSync: mockReadFileSync,
|
||||
writeFileSync: mockWriteFileSync
|
||||
writeFileSync: mockWriteFileSync,
|
||||
}))
|
||||
|
||||
// Mock MCP and inference modules
|
||||
@@ -102,19 +84,19 @@ const mockSimpleInference = vi.fn() as MockedFunction<any>
|
||||
const mockMcpInference = vi.fn() as MockedFunction<any>
|
||||
|
||||
vi.mock('../src/mcp.js', () => ({
|
||||
connectToGitHubMCP: mockConnectToGitHubMCP
|
||||
connectToGitHubMCP: mockConnectToGitHubMCP,
|
||||
}))
|
||||
|
||||
vi.mock('../src/inference.js', () => ({
|
||||
simpleInference: mockSimpleInference,
|
||||
mcpInference: mockMcpInference
|
||||
mcpInference: mockMcpInference,
|
||||
}))
|
||||
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// The module being tested should be imported dynamically. This ensures that the
|
||||
// mocks are used in place of any actual dependencies.
|
||||
const { run } = await import('../src/main.js')
|
||||
const {run} = await import('../src/main.js')
|
||||
|
||||
describe('main.ts', () => {
|
||||
// Reset all mocks before each test
|
||||
@@ -132,7 +114,7 @@ describe('main.ts', () => {
|
||||
it('Sets the response output', async () => {
|
||||
mockInputs({
|
||||
prompt: 'Hello, AI!',
|
||||
'system-prompt': 'You are a test assistant.'
|
||||
'system-prompt': 'You are a test assistant.',
|
||||
})
|
||||
|
||||
await run()
|
||||
@@ -144,36 +126,33 @@ describe('main.ts', () => {
|
||||
it('Sets a failed status when no prompt is set', async () => {
|
||||
mockInputs({
|
||||
prompt: '',
|
||||
'prompt-file': ''
|
||||
'prompt-file': '',
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.setFailed).toHaveBeenNthCalledWith(
|
||||
1,
|
||||
'Neither prompt-file nor prompt was set'
|
||||
)
|
||||
expect(core.setFailed).toHaveBeenNthCalledWith(1, 'Neither prompt-file nor prompt was set')
|
||||
})
|
||||
|
||||
it('uses simple inference when MCP is disabled', async () => {
|
||||
mockInputs({
|
||||
prompt: 'Hello, AI!',
|
||||
'system-prompt': 'You are a test assistant.',
|
||||
'enable-github-mcp': 'false'
|
||||
'enable-github-mcp': 'false',
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(mockSimpleInference).toHaveBeenCalledWith({
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a test assistant.' },
|
||||
{ role: 'user', content: 'Hello, AI!' }
|
||||
{role: 'system', content: 'You are a test assistant.'},
|
||||
{role: 'user', content: 'Hello, AI!'},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
endpoint: 'https://api.test.com',
|
||||
token: 'fake-token',
|
||||
responseFormat: undefined
|
||||
responseFormat: undefined,
|
||||
})
|
||||
expect(mockConnectToGitHubMCP).not.toHaveBeenCalled()
|
||||
expect(mockMcpInference).not.toHaveBeenCalled()
|
||||
@@ -184,13 +163,13 @@ describe('main.ts', () => {
|
||||
const mockMcpClient = {
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
client: {} as any,
|
||||
tools: [{ type: 'function', function: { name: 'test-tool' } }]
|
||||
tools: [{type: 'function', function: {name: 'test-tool'}}],
|
||||
}
|
||||
|
||||
mockInputs({
|
||||
prompt: 'Hello, AI!',
|
||||
'system-prompt': 'You are a test assistant.',
|
||||
'enable-github-mcp': 'true'
|
||||
'enable-github-mcp': 'true',
|
||||
})
|
||||
|
||||
mockConnectToGitHubMCP.mockResolvedValue(mockMcpClient)
|
||||
@@ -201,12 +180,12 @@ describe('main.ts', () => {
|
||||
expect(mockMcpInference).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
messages: [
|
||||
{ role: 'system', content: 'You are a test assistant.' },
|
||||
{ role: 'user', content: 'Hello, AI!' }
|
||||
{role: 'system', content: 'You are a test assistant.'},
|
||||
{role: 'user', content: 'Hello, AI!'},
|
||||
],
|
||||
token: 'fake-token'
|
||||
token: 'fake-token',
|
||||
}),
|
||||
mockMcpClient
|
||||
mockMcpClient,
|
||||
)
|
||||
expect(mockSimpleInference).not.toHaveBeenCalled()
|
||||
verifyStandardResponse()
|
||||
@@ -216,7 +195,7 @@ describe('main.ts', () => {
|
||||
mockInputs({
|
||||
prompt: 'Hello, AI!',
|
||||
'system-prompt': 'You are a test assistant.',
|
||||
'enable-github-mcp': 'true'
|
||||
'enable-github-mcp': 'true',
|
||||
})
|
||||
|
||||
mockConnectToGitHubMCP.mockResolvedValue(null)
|
||||
@@ -226,9 +205,7 @@ describe('main.ts', () => {
|
||||
expect(mockConnectToGitHubMCP).toHaveBeenCalledWith('fake-token')
|
||||
expect(mockSimpleInference).toHaveBeenCalled()
|
||||
expect(mockMcpInference).not.toHaveBeenCalled()
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'MCP connection failed, falling back to simple inference'
|
||||
)
|
||||
expect(core.warning).toHaveBeenCalledWith('MCP connection failed, falling back to simple inference')
|
||||
verifyStandardResponse()
|
||||
})
|
||||
|
||||
@@ -240,27 +217,27 @@ describe('main.ts', () => {
|
||||
|
||||
mockFileContent({
|
||||
[promptFile]: promptContent,
|
||||
[systemPromptFile]: systemPromptContent
|
||||
[systemPromptFile]: systemPromptContent,
|
||||
})
|
||||
|
||||
mockInputs({
|
||||
'prompt-file': promptFile,
|
||||
'system-prompt-file': systemPromptFile,
|
||||
'enable-github-mcp': 'false'
|
||||
'enable-github-mcp': 'false',
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(mockSimpleInference).toHaveBeenCalledWith({
|
||||
messages: [
|
||||
{ role: 'system', content: systemPromptContent },
|
||||
{ role: 'user', content: promptContent }
|
||||
{role: 'system', content: systemPromptContent},
|
||||
{role: 'user', content: promptContent},
|
||||
],
|
||||
modelName: 'gpt-4',
|
||||
maxTokens: 100,
|
||||
endpoint: 'https://api.test.com',
|
||||
token: 'fake-token',
|
||||
responseFormat: undefined
|
||||
responseFormat: undefined,
|
||||
})
|
||||
verifyStandardResponse()
|
||||
})
|
||||
@@ -271,13 +248,11 @@ describe('main.ts', () => {
|
||||
mockFileContent({}, [promptFile])
|
||||
|
||||
mockInputs({
|
||||
'prompt-file': promptFile
|
||||
'prompt-file': promptFile,
|
||||
})
|
||||
|
||||
await run()
|
||||
|
||||
expect(core.setFailed).toHaveBeenCalledWith(
|
||||
`File for prompt-file was not found: ${promptFile}`
|
||||
)
|
||||
expect(core.setFailed).toHaveBeenCalledWith(`File for prompt-file was not found: ${promptFile}`)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,11 +1,4 @@
|
||||
import {
|
||||
vi,
|
||||
type MockedFunction,
|
||||
describe,
|
||||
it,
|
||||
expect,
|
||||
beforeEach
|
||||
} from 'vitest'
|
||||
import {vi, type MockedFunction, describe, it, expect, beforeEach} from 'vitest'
|
||||
import * as core from '../__fixtures__/core.js'
|
||||
|
||||
// Mock MCP SDK
|
||||
@@ -19,24 +12,22 @@ const mockCallTool = vi.fn() as MockedFunction<any>
|
||||
const mockClient = {
|
||||
connect: mockConnect,
|
||||
listTools: mockListTools,
|
||||
callTool: mockCallTool
|
||||
callTool: mockCallTool,
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
} as any
|
||||
|
||||
vi.mock('@modelcontextprotocol/sdk/client/index.js', () => ({
|
||||
Client: vi.fn(() => mockClient)
|
||||
Client: vi.fn(() => mockClient),
|
||||
}))
|
||||
|
||||
vi.mock('@modelcontextprotocol/sdk/client/streamableHttp.js', () => ({
|
||||
StreamableHTTPClientTransport: vi.fn()
|
||||
StreamableHTTPClientTransport: vi.fn(),
|
||||
}))
|
||||
|
||||
vi.mock('@actions/core', () => core)
|
||||
|
||||
// Import the module being tested
|
||||
const { connectToGitHubMCP, executeToolCall, executeToolCalls } = await import(
|
||||
'../src/mcp.js'
|
||||
)
|
||||
const {connectToGitHubMCP, executeToolCall, executeToolCalls} = await import('../src/mcp.js')
|
||||
|
||||
describe('mcp.ts', () => {
|
||||
beforeEach(() => {
|
||||
@@ -50,20 +41,20 @@ describe('mcp.ts', () => {
|
||||
{
|
||||
name: 'test-tool-1',
|
||||
description: 'Test tool 1',
|
||||
inputSchema: { type: 'object', properties: {} }
|
||||
inputSchema: {type: 'object', properties: {}},
|
||||
},
|
||||
{
|
||||
name: 'test-tool-2',
|
||||
description: 'Test tool 2',
|
||||
inputSchema: {
|
||||
type: 'object',
|
||||
properties: { param: { type: 'string' } }
|
||||
}
|
||||
}
|
||||
properties: {param: {type: 'string'}},
|
||||
},
|
||||
},
|
||||
]
|
||||
|
||||
mockConnect.mockResolvedValue(undefined)
|
||||
mockListTools.mockResolvedValue({ tools: mockTools })
|
||||
mockListTools.mockResolvedValue({tools: mockTools})
|
||||
|
||||
const result = await connectToGitHubMCP(token)
|
||||
|
||||
@@ -75,21 +66,13 @@ describe('mcp.ts', () => {
|
||||
function: {
|
||||
name: 'test-tool-1',
|
||||
description: 'Test tool 1',
|
||||
parameters: { type: 'object', properties: {} }
|
||||
}
|
||||
parameters: {type: 'object', properties: {}},
|
||||
},
|
||||
})
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Connecting to GitHub MCP server...'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Successfully connected to GitHub MCP server'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Retrieved 2 tools from GitHub MCP server'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Mapped 2 GitHub MCP tools for Azure AI Inference'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Connecting to GitHub MCP server...')
|
||||
expect(core.info).toHaveBeenCalledWith('Successfully connected to GitHub MCP server')
|
||||
expect(core.info).toHaveBeenCalledWith('Retrieved 2 tools from GitHub MCP server')
|
||||
expect(core.info).toHaveBeenCalledWith('Mapped 2 GitHub MCP tools for Azure AI Inference')
|
||||
})
|
||||
|
||||
it('returns null when connection fails', async () => {
|
||||
@@ -101,27 +84,21 @@ describe('mcp.ts', () => {
|
||||
const result = await connectToGitHubMCP(token)
|
||||
|
||||
expect(result).toBeNull()
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'Failed to connect to GitHub MCP server: Error: Connection failed'
|
||||
)
|
||||
expect(core.warning).toHaveBeenCalledWith('Failed to connect to GitHub MCP server: Error: Connection failed')
|
||||
})
|
||||
|
||||
it('handles empty tools list', async () => {
|
||||
const token = 'test-token'
|
||||
|
||||
mockConnect.mockResolvedValue(undefined)
|
||||
mockListTools.mockResolvedValue({ tools: [] })
|
||||
mockListTools.mockResolvedValue({tools: []})
|
||||
|
||||
const result = await connectToGitHubMCP(token)
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.tools).toHaveLength(0)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Retrieved 0 tools from GitHub MCP server'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Mapped 0 GitHub MCP tools for Azure AI Inference'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Retrieved 0 tools from GitHub MCP server')
|
||||
expect(core.info).toHaveBeenCalledWith('Mapped 0 GitHub MCP tools for Azure AI Inference')
|
||||
})
|
||||
|
||||
it('handles undefined tools list', async () => {
|
||||
@@ -134,9 +111,7 @@ describe('mcp.ts', () => {
|
||||
|
||||
expect(result).not.toBeNull()
|
||||
expect(result?.tools).toHaveLength(0)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Retrieved 0 tools from GitHub MCP server'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Retrieved 0 tools from GitHub MCP server')
|
||||
})
|
||||
})
|
||||
|
||||
@@ -147,11 +122,11 @@ describe('mcp.ts', () => {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
arguments: '{"param": "value"}'
|
||||
}
|
||||
arguments: '{"param": "value"}',
|
||||
},
|
||||
}
|
||||
const toolResult = {
|
||||
content: [{ type: 'text', text: 'Tool execution result' }]
|
||||
content: [{type: 'text', text: 'Tool execution result'}],
|
||||
}
|
||||
|
||||
mockCallTool.mockResolvedValue(toolResult)
|
||||
@@ -160,20 +135,16 @@ describe('mcp.ts', () => {
|
||||
|
||||
expect(mockCallTool).toHaveBeenCalledWith({
|
||||
name: 'test-tool',
|
||||
arguments: { param: 'value' }
|
||||
arguments: {param: 'value'},
|
||||
})
|
||||
expect(result).toEqual({
|
||||
tool_call_id: 'call-123',
|
||||
role: 'tool',
|
||||
name: 'test-tool',
|
||||
content: JSON.stringify(toolResult.content)
|
||||
content: JSON.stringify(toolResult.content),
|
||||
})
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'Executing GitHub MCP tool: test-tool with args: {"param": "value"}'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith(
|
||||
'GitHub MCP tool test-tool executed successfully'
|
||||
)
|
||||
expect(core.info).toHaveBeenCalledWith('Executing GitHub MCP tool: test-tool with args: {"param": "value"}')
|
||||
expect(core.info).toHaveBeenCalledWith('GitHub MCP tool test-tool executed successfully')
|
||||
})
|
||||
|
||||
it('handles tool execution errors gracefully', async () => {
|
||||
@@ -182,8 +153,8 @@ describe('mcp.ts', () => {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'failing-tool',
|
||||
arguments: '{"param": "value"}'
|
||||
}
|
||||
arguments: '{"param": "value"}',
|
||||
},
|
||||
}
|
||||
const toolError = new Error('Tool execution failed')
|
||||
|
||||
@@ -195,10 +166,10 @@ describe('mcp.ts', () => {
|
||||
tool_call_id: 'call-456',
|
||||
role: 'tool',
|
||||
name: 'failing-tool',
|
||||
content: 'Error: Error: Tool execution failed'
|
||||
content: 'Error: Error: Tool execution failed',
|
||||
})
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
'Failed to execute GitHub MCP tool failing-tool: Error: Tool execution failed'
|
||||
'Failed to execute GitHub MCP tool failing-tool: Error: Tool execution failed',
|
||||
)
|
||||
})
|
||||
|
||||
@@ -208,8 +179,8 @@ describe('mcp.ts', () => {
|
||||
type: 'function',
|
||||
function: {
|
||||
name: 'test-tool',
|
||||
arguments: 'invalid-json'
|
||||
}
|
||||
arguments: 'invalid-json',
|
||||
},
|
||||
}
|
||||
|
||||
const result = await executeToolCall(mockClient, toolCall)
|
||||
@@ -218,9 +189,7 @@ describe('mcp.ts', () => {
|
||||
expect(result.role).toBe('tool')
|
||||
expect(result.name).toBe('test-tool')
|
||||
expect(result.content).toContain('Error:')
|
||||
expect(core.warning).toHaveBeenCalledWith(
|
||||
expect.stringContaining('Failed to execute GitHub MCP tool test-tool:')
|
||||
)
|
||||
expect(core.warning).toHaveBeenCalledWith(expect.stringContaining('Failed to execute GitHub MCP tool test-tool:'))
|
||||
})
|
||||
})
|
||||
|
||||
@@ -230,21 +199,21 @@ describe('mcp.ts', () => {
|
||||
{
|
||||
id: 'call-1',
|
||||
type: 'function',
|
||||
function: { name: 'tool-1', arguments: '{}' }
|
||||
function: {name: 'tool-1', arguments: '{}'},
|
||||
},
|
||||
{
|
||||
id: 'call-2',
|
||||
type: 'function',
|
||||
function: { name: 'tool-2', arguments: '{"param": "value"}' }
|
||||
}
|
||||
function: {name: 'tool-2', arguments: '{"param": "value"}'},
|
||||
},
|
||||
]
|
||||
|
||||
mockCallTool
|
||||
.mockResolvedValueOnce({
|
||||
content: [{ type: 'text', text: 'Result 1' }]
|
||||
content: [{type: 'text', text: 'Result 1'}],
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
content: [{ type: 'text', text: 'Result 2' }]
|
||||
content: [{type: 'text', text: 'Result 2'}],
|
||||
})
|
||||
|
||||
const results = await executeToolCalls(mockClient, toolCalls)
|
||||
@@ -267,18 +236,18 @@ describe('mcp.ts', () => {
|
||||
{
|
||||
id: 'call-1',
|
||||
type: 'function',
|
||||
function: { name: 'tool-1', arguments: '{}' }
|
||||
function: {name: 'tool-1', arguments: '{}'},
|
||||
},
|
||||
{
|
||||
id: 'call-2',
|
||||
type: 'function',
|
||||
function: { name: 'tool-2', arguments: '{}' }
|
||||
}
|
||||
function: {name: 'tool-2', arguments: '{}'},
|
||||
},
|
||||
]
|
||||
|
||||
mockCallTool
|
||||
.mockResolvedValueOnce({
|
||||
content: [{ type: 'text', text: 'Result 1' }]
|
||||
content: [{type: 'text', text: 'Result 1'}],
|
||||
})
|
||||
.mockRejectedValueOnce(new Error('Tool 2 failed'))
|
||||
|
||||
|
||||
@@ -1,12 +1,7 @@
|
||||
import { describe, it, expect } from 'vitest'
|
||||
import {describe, it, expect} from 'vitest'
|
||||
import * as path from 'path'
|
||||
import { fileURLToPath } from 'url'
|
||||
import {
|
||||
parseTemplateVariables,
|
||||
replaceTemplateVariables,
|
||||
loadPromptFile,
|
||||
isPromptYamlFile
|
||||
} from '../src/prompt'
|
||||
import {fileURLToPath} from 'url'
|
||||
import {parseTemplateVariables, replaceTemplateVariables, loadPromptFile, isPromptYamlFile} from '../src/prompt'
|
||||
|
||||
const __filename = fileURLToPath(import.meta.url)
|
||||
const __dirname = path.dirname(__filename)
|
||||
@@ -19,7 +14,7 @@ a: hello
|
||||
b: world
|
||||
`
|
||||
const result = parseTemplateVariables(input)
|
||||
expect(result).toEqual({ a: 'hello', b: 'world' })
|
||||
expect(result).toEqual({a: 'hello', b: 'world'})
|
||||
})
|
||||
|
||||
it('should parse multiline variables', () => {
|
||||
@@ -49,14 +44,14 @@ var2: |
|
||||
describe('replaceTemplateVariables', () => {
|
||||
it('should replace simple variables', () => {
|
||||
const text = 'Hello {{name}}, welcome to {{place}}!'
|
||||
const variables = { name: 'John', place: 'GitHub' }
|
||||
const variables = {name: 'John', place: 'GitHub'}
|
||||
const result = replaceTemplateVariables(text, variables)
|
||||
expect(result).toBe('Hello John, welcome to GitHub!')
|
||||
})
|
||||
|
||||
it('should leave unreplaced variables as is', () => {
|
||||
const text = 'Hello {{name}}, welcome to {{unknown}}!'
|
||||
const variables = { name: 'John' }
|
||||
const variables = {name: 'John'}
|
||||
const result = replaceTemplateVariables(text, variables)
|
||||
expect(result).toBe('Hello John, welcome to {{unknown}}!')
|
||||
})
|
||||
@@ -90,31 +85,25 @@ var2: |
|
||||
|
||||
describe('loadPromptFile', () => {
|
||||
it('should load simple prompt file', () => {
|
||||
const filePath = path.join(
|
||||
__dirname,
|
||||
'../__fixtures__/prompts/simple.prompt.yml'
|
||||
)
|
||||
const variables = { a: 'cats', b: 'dogs' }
|
||||
const filePath = path.join(__dirname, '../__fixtures__/prompts/simple.prompt.yml')
|
||||
const variables = {a: 'cats', b: 'dogs'}
|
||||
const result = loadPromptFile(filePath, variables)
|
||||
|
||||
expect(result.messages).toHaveLength(2)
|
||||
expect(result.messages[0]).toEqual({
|
||||
role: 'system',
|
||||
content: 'Be as concise as possible'
|
||||
content: 'Be as concise as possible',
|
||||
})
|
||||
expect(result.messages[1]).toEqual({
|
||||
role: 'user',
|
||||
content: 'Compare cats and dogs, please'
|
||||
content: 'Compare cats and dogs, please',
|
||||
})
|
||||
expect(result.model).toBe('openai/gpt-4o')
|
||||
})
|
||||
|
||||
it('should load JSON schema prompt file', () => {
|
||||
const filePath = path.join(
|
||||
__dirname,
|
||||
'../__fixtures__/prompts/json-schema.prompt.yml'
|
||||
)
|
||||
const variables = { animal: 'dog' }
|
||||
const filePath = path.join(__dirname, '../__fixtures__/prompts/json-schema.prompt.yml')
|
||||
const variables = {animal: 'dog'}
|
||||
const result = loadPromptFile(filePath, variables)
|
||||
|
||||
expect(result.messages).toHaveLength(2)
|
||||
@@ -125,9 +114,7 @@ var2: |
|
||||
})
|
||||
|
||||
it('should throw error for non-existent file', () => {
|
||||
expect(() => loadPromptFile('non-existent.prompt.yml')).toThrow(
|
||||
'Prompt file not found'
|
||||
)
|
||||
expect(() => loadPromptFile('non-existent.prompt.yml')).toThrow('Prompt file not found')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user