From 2ccb27bcdaed49826cfca474871f15d414032ee1 Mon Sep 17 00:00:00 2001 From: Nicolas FRADIN Date: Wed, 20 May 2026 19:18:51 +0200 Subject: [PATCH] test: add failing unit tests for new MCP tools (TDD) --- tests/unit/router.test.ts | 291 +++++++++++++++++++++++++++++++++++--- 1 file changed, 275 insertions(+), 16 deletions(-) diff --git a/tests/unit/router.test.ts b/tests/unit/router.test.ts index 3673a9d..1d1753e 100644 --- a/tests/unit/router.test.ts +++ b/tests/unit/router.test.ts @@ -55,14 +55,14 @@ vi.mock('../../src/clients/pull-request-client.js', () => { title: 'Test PR', description: 'Test description' }), - createPullRequest: async () => ({ id: 1 }), - updatePullRequest: async () => ({ id: 1 }), - mergePullRequest: async () => ({ id: 1 }), - declinePullRequest: async () => ({ id: 1 }), - approvePullRequest: async () => ({ id: 1 }), - unapprovePullRequest: async () => ({ id: 1 }), - requestChangesPullRequest: async () => ({ id: 1 }), - removeRequestChangesPullRequest: async () => ({ id: 1 }) + createPullRequest: async () => ({ id: 10, title: 'New PR', state: 'OPEN' }), + updatePullRequest: async () => ({ id: 1, title: 'Updated PR', state: 'OPEN' }), + mergePullRequest: async () => ({ id: 1, state: 'MERGED' }), + declinePullRequest: async () => ({ id: 1, state: 'DECLINED' }), + approvePullRequest: async () => ({ approved: true }), + unapprovePullRequest: async () => ({ success: true }), + requestChangesPullRequest: async () => ({ state: 'changes_requested' }), + removeRequestChangesPullRequest: async () => ({ success: true }) }; return { @@ -76,10 +76,10 @@ vi.mock('../../src/clients/pull-request-client.js', () => { vi.mock('../../src/clients/repository-client.js', () => { const mockRepoClient = { - listWorkspaces: async () => ({ values: [] }), - listRepositories: async () => ({ values: [] }), - getRepository: async () => ({ slug: 'test' }), - listBranches: async () => ({ values: [] }) + listWorkspaces: async () => ({ values: [{ slug: 'my-workspace' }] }), + listRepositories: async () => ({ values: [{ slug: 'my-repo', name: 'My Repo' }] }), + getRepository: async () => ({ slug: 'my-repo', name: 'My Repo', full_name: 'ws/my-repo' }), + listBranches: async () => ({ values: [{ name: 'main' }, { name: 'develop' }] }) }; return { @@ -100,11 +100,11 @@ vi.mock('../../src/clients/comment-client.js', () => { id: 1, content: { raw: 'Test comment' } }), - addPullRequestComment: async () => ({ id: 1 }), - updatePullRequestComment: async () => ({ id: 1 }), + addPullRequestComment: async () => ({ id: 100, content: { raw: 'New comment' } }), + updatePullRequestComment: async () => ({ id: 100, content: { raw: 'Updated' } }), deletePullRequestComment: async () => ({ success: true }), - createPullRequestTask: async () => ({ id: 1 }), - updatePullRequestTask: async () => ({ id: 1 }), + createPullRequestTask: async () => ({ id: 200, content: { raw: 'Task' }, state: 'UNRESOLVED' }), + updatePullRequestTask: async () => ({ id: 200, state: 'RESOLVED' }), deletePullRequestTask: async () => ({ success: true }) }; @@ -465,6 +465,265 @@ describe('BitbucketRouter', () => { }); }); + describe('list_workspaces', () => { + it('should return workspaces', async () => { + const result = await router.executeTool('list_workspaces', {}) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('values'); + }); + }); + + describe('list_repositories', () => { + it('should return repositories for valid workspace', async () => { + const result = await router.executeTool('list_repositories', { + workspace: 'test-workspace', + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('values'); + }); + + it('should fail without workspace', async () => { + const result = await router.executeTool('list_repositories', {}) as ToolResult; + expect(result.success).toBe(false); + expect(result.error).toContain('Missing required parameters'); + }); + }); + + describe('get_repository', () => { + it('should return repository metadata', async () => { + const result = await router.executeTool('get_repository', { + workspace: 'test-workspace', + repository: 'test-repo', + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('slug'); + }); + }); + + describe('list_branches', () => { + it('should return branches', async () => { + const result = await router.executeTool('list_branches', { + workspace: 'test-workspace', + repository: 'test-repo', + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('values'); + }); + }); + + describe('create_pull_request', () => { + it('should create a pull request', async () => { + const result = await router.executeTool('create_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + title: 'My PR', + source_branch: 'feature/x', + destination_branch: 'main', + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('id'); + }); + + it('should fail without title', async () => { + const result = await router.executeTool('create_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + source_branch: 'feature/x', + destination_branch: 'main', + }) as ToolResult; + expect(result.success).toBe(false); + expect(result.error).toContain('Missing required parameters'); + }); + + it('should fail without source_branch', async () => { + const result = await router.executeTool('create_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + title: 'My PR', + destination_branch: 'main', + }) as ToolResult; + expect(result.success).toBe(false); + expect(result.error).toContain('Missing required parameters'); + }); + }); + + describe('update_pull_request', () => { + it('should update a pull request', async () => { + const result = await router.executeTool('update_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + title: 'Updated', + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('id'); + }); + }); + + describe('merge_pull_request', () => { + it('should merge a pull request', async () => { + const result = await router.executeTool('merge_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('state', 'MERGED'); + }); + }); + + describe('decline_pull_request', () => { + it('should decline a pull request', async () => { + const result = await router.executeTool('decline_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('state', 'DECLINED'); + }); + }); + + describe('approve_pull_request', () => { + it('should approve a pull request', async () => { + const result = await router.executeTool('approve_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + }) as ToolResult; + expect(result.success).toBe(true); + }); + }); + + describe('unapprove_pull_request', () => { + it('should remove approval', async () => { + const result = await router.executeTool('unapprove_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + }) as ToolResult; + expect(result.success).toBe(true); + }); + }); + + describe('request_changes_pull_request', () => { + it('should request changes', async () => { + const result = await router.executeTool('request_changes_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + }) as ToolResult; + expect(result.success).toBe(true); + }); + }); + + describe('remove_request_changes_pull_request', () => { + it('should remove request-changes', async () => { + const result = await router.executeTool('remove_request_changes_pull_request', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + }) as ToolResult; + expect(result.success).toBe(true); + }); + }); + + describe('add_pull_request_comment', () => { + it('should add a comment', async () => { + const result = await router.executeTool('add_pull_request_comment', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + content: 'Great work!', + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('id'); + }); + + it('should fail without content', async () => { + const result = await router.executeTool('add_pull_request_comment', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + }) as ToolResult; + expect(result.success).toBe(false); + expect(result.error).toContain('Missing required parameters'); + }); + }); + + describe('update_pull_request_comment', () => { + it('should update a comment', async () => { + const result = await router.executeTool('update_pull_request_comment', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + commentId: 100, + content: 'Edited', + }) as ToolResult; + expect(result.success).toBe(true); + }); + }); + + describe('delete_pull_request_comment', () => { + it('should delete a comment', async () => { + const result = await router.executeTool('delete_pull_request_comment', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + commentId: 100, + }) as ToolResult; + expect(result.success).toBe(true); + }); + }); + + describe('create_pull_request_task', () => { + it('should create a task', async () => { + const result = await router.executeTool('create_pull_request_task', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + content: 'Review section 3', + }) as ToolResult; + expect(result.success).toBe(true); + expect(result.data).toHaveProperty('id'); + }); + + it('should fail without content', async () => { + const result = await router.executeTool('create_pull_request_task', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + }) as ToolResult; + expect(result.success).toBe(false); + expect(result.error).toContain('Missing required parameters'); + }); + }); + + describe('update_pull_request_task', () => { + it('should update a task', async () => { + const result = await router.executeTool('update_pull_request_task', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + taskId: 200, + state: 'RESOLVED', + }) as ToolResult; + expect(result.success).toBe(true); + }); + }); + + describe('delete_pull_request_task', () => { + it('should delete a task', async () => { + const result = await router.executeTool('delete_pull_request_task', { + workspace: 'test-workspace', + repository: 'test-repo', + pullRequestId: 1, + taskId: 200, + }) as ToolResult; + expect(result.success).toBe(true); + }); + }); + describe('unknown tool', () => { it('should return error for unknown tool', async () => { const result = await router.executeTool('unknown_tool', {}) as ToolResult;