cli / mcp for bitbucket
at main 120 lines 3.6 kB view raw
1import { Client } from '@modelcontextprotocol/sdk/client/index.js'; 2import { InMemoryTransport } from '@modelcontextprotocol/sdk/inMemory.js'; 3import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'; 4import { afterAll, beforeAll, beforeEach, describe, expect, it, vi } from 'vitest'; 5import { registerAllTools } from '../../tools'; 6 7vi.mock('@bitbucket-tool/core', () => ({ 8 listCommits: vi.fn(), 9 getCommit: vi.fn(), 10})); 11 12import { getCommit, listCommits } from '@bitbucket-tool/core'; 13 14const ws = 'test-workspace'; 15const repo = 'test-repo'; 16 17const callTool = async (client: Client, name: string, args: Record<string, unknown>) => 18 client.callTool({ name, arguments: args }); 19 20const textOf = (result: Awaited<ReturnType<Client['callTool']>>) => 21 (result.content as Array<{ type: string; text: string }>)[0].text; 22 23describe('Commit tools', () => { 24 let client: Client; 25 let server: McpServer; 26 27 beforeAll(async () => { 28 const [clientTransport, serverTransport] = InMemoryTransport.createLinkedPair(); 29 server = new McpServer({ name: 'test', version: '0.0.1' }); 30 client = new Client({ name: 'test-client', version: '0.0.1' }); 31 32 registerAllTools(server); 33 34 await server.connect(serverTransport); 35 await client.connect(clientTransport); 36 }); 37 38 afterAll(async () => { 39 await client.close(); 40 await server.close(); 41 }); 42 43 beforeEach(() => { 44 vi.clearAllMocks(); 45 }); 46 47 describe('list_commits', () => { 48 it('passes params to core and returns JSON response', async () => { 49 const commits = [{ hash: 'abc123', message: 'initial commit' }]; 50 vi.mocked(listCommits).mockResolvedValue({ ok: true, data: commits as never }); 51 52 const result = await callTool(client, 'list_commits', { 53 workspace: ws, 54 repo_slug: repo, 55 page: 1, 56 pagelen: 30, 57 }); 58 59 expect(listCommits).toHaveBeenCalledWith({ 60 workspace: ws, 61 repoSlug: repo, 62 page: 1, 63 pagelen: 30, 64 }); 65 expect(result.isError).toBeFalsy(); 66 expect(JSON.parse(textOf(result))).toEqual(commits); 67 }); 68 69 it('returns error response on failure', async () => { 70 vi.mocked(listCommits).mockResolvedValue({ 71 ok: false, 72 error: { status: 404, message: 'Repository not found' }, 73 }); 74 75 const result = await callTool(client, 'list_commits', { 76 workspace: ws, 77 repo_slug: repo, 78 }); 79 80 expect(result.isError).toBe(true); 81 expect(textOf(result)).toBe('Repository not found (404)'); 82 }); 83 }); 84 85 describe('get_commit', () => { 86 it('passes params to core and returns JSON response', async () => { 87 const commit = { hash: 'abc123', message: 'feat: add feature', author: { raw: 'Test' } }; 88 vi.mocked(getCommit).mockResolvedValue({ ok: true, data: commit as never }); 89 90 const result = await callTool(client, 'get_commit', { 91 workspace: ws, 92 repo_slug: repo, 93 commit: 'abc123', 94 }); 95 96 expect(getCommit).toHaveBeenCalledWith({ 97 workspace: ws, 98 repoSlug: repo, 99 commit: 'abc123', 100 }); 101 expect(JSON.parse(textOf(result))).toEqual(commit); 102 }); 103 104 it('returns error on failure', async () => { 105 vi.mocked(getCommit).mockResolvedValue({ 106 ok: false, 107 error: { status: 404, message: 'Commit not found' }, 108 }); 109 110 const result = await callTool(client, 'get_commit', { 111 workspace: ws, 112 repo_slug: repo, 113 commit: 'nonexistent', 114 }); 115 116 expect(result.isError).toBe(true); 117 expect(textOf(result)).toBe('Commit not found (404)'); 118 }); 119 }); 120});