Add code
This commit is contained in:
294
tests/integration/bitbucket-api.test.ts
Normal file
294
tests/integration/bitbucket-api.test.ts
Normal file
@@ -0,0 +1,294 @@
|
||||
/**
|
||||
* Integration tests for Bitbucket MCP Server
|
||||
* Tests against real Bitbucket API with iland-software-engineering/1111-frontend repo
|
||||
*
|
||||
* Requires BITBUCKET_MCP_EMAIL and BITBUCKET_MCP_TOKEN in .env or environment variable.
|
||||
*/
|
||||
|
||||
import { describe, it, expect, beforeAll } from 'vitest';
|
||||
import { BitbucketClient } from '../../src/bitbucket-client.js';
|
||||
import { BitbucketRouter, ToolResult } from '../../src/router.js';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as dotenv from 'dotenv';
|
||||
|
||||
const WORKSPACE = 'iland-software-engineering';
|
||||
const REPO_SLUG = '1111-front';
|
||||
|
||||
dotenv.config({ path: path.resolve(process.cwd(), '.env') });
|
||||
|
||||
const EMAIL = process.env.BITBUCKET_MCP_EMAIL || '';
|
||||
const TOKEN = process.env.BITBUCKET_MCP_TOKEN || '';
|
||||
const HAS_TOKEN = !!EMAIL && !!TOKEN && TOKEN.length >= 20;
|
||||
|
||||
describe('Bitbucket API Integration Tests', () => {
|
||||
let client: BitbucketClient;
|
||||
let router: BitbucketRouter;
|
||||
|
||||
beforeAll(() => {
|
||||
client = new BitbucketClient();
|
||||
router = new BitbucketRouter();
|
||||
}, 35000);
|
||||
|
||||
describe('Pull Request Listing', () => {
|
||||
it('should list pull requests', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
expect(Array.isArray(prs)).toBe(true);
|
||||
if (prs.length > 0) {
|
||||
expect(prs[0]).toHaveProperty('id');
|
||||
expect(prs[0]).toHaveProperty('title');
|
||||
expect(prs[0]).toHaveProperty('state');
|
||||
}
|
||||
});
|
||||
|
||||
it('should list open pull requests', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG, { state: 'open' });
|
||||
expect(Array.isArray(prs)).toBe(true);
|
||||
});
|
||||
|
||||
it('should list closed pull requests', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG, { state: 'closed' });
|
||||
expect(Array.isArray(prs)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pull Request Details', () => {
|
||||
it('should get a specific pull request', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG, { state: 'open' });
|
||||
|
||||
if (prs.length === 0) {
|
||||
const closedPrs = await client.listPullRequests(WORKSPACE, REPO_SLUG, { state: 'closed' });
|
||||
if (closedPrs.length === 0) {
|
||||
console.log('⚠️ No PRs found in repository, skipping specific PR test');
|
||||
return;
|
||||
}
|
||||
const pr = await client.getPullRequest(WORKSPACE, REPO_SLUG, closedPrs[0].id);
|
||||
expect(pr).toHaveProperty('id');
|
||||
expect(pr).toHaveProperty('title');
|
||||
return;
|
||||
}
|
||||
|
||||
const pr = await client.getPullRequest(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(pr).toHaveProperty('id');
|
||||
expect(pr).toHaveProperty('title');
|
||||
expect(pr.id).toBe(prs[0].id);
|
||||
});
|
||||
|
||||
it('should get pull request status', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping status test');
|
||||
return;
|
||||
}
|
||||
|
||||
const status = await client.getPullRequestStatus(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(status).toHaveProperty('id');
|
||||
expect(status).toHaveProperty('state');
|
||||
expect(status).toHaveProperty('title');
|
||||
});
|
||||
|
||||
it('should get pull request reviewers', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping reviewers test');
|
||||
return;
|
||||
}
|
||||
|
||||
const reviewers = await client.getPullRequestReviewers(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(Array.isArray(reviewers)).toBe(true);
|
||||
});
|
||||
|
||||
it('should get pull request participants', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping participants test');
|
||||
return;
|
||||
}
|
||||
|
||||
const participants = await client.getPullRequestParticipants(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(participants).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pull Request Activities & Comments', () => {
|
||||
it('should get pull request activities', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping activities test');
|
||||
return;
|
||||
}
|
||||
|
||||
const activities = await client.getPullRequestActivities(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(activities).toBeDefined();
|
||||
expect(activities).toHaveProperty('values');
|
||||
});
|
||||
|
||||
it('should get pull request comments', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping comments test');
|
||||
return;
|
||||
}
|
||||
|
||||
const comments = await client.getPullRequestComments(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(comments).toBeDefined();
|
||||
expect(comments).toHaveProperty('values');
|
||||
});
|
||||
|
||||
it('should get pull request commits', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping commits test');
|
||||
return;
|
||||
}
|
||||
|
||||
const commits = await client.getPullRequestCommits(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(commits).toBeDefined();
|
||||
expect(commits).toHaveProperty('values');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pull Request Changes & Diff', () => {
|
||||
it('should get pull request changes', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping changes test');
|
||||
return;
|
||||
}
|
||||
|
||||
const changes = await client.getPullRequestChanges(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(changes).toBeDefined();
|
||||
expect(changes).toHaveProperty('values');
|
||||
});
|
||||
|
||||
it('should get pull request diff', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping diff test');
|
||||
return;
|
||||
}
|
||||
|
||||
const diff = await client.getPullRequestDiff(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(diff).toBeDefined();
|
||||
});
|
||||
|
||||
it('should get pull request patch', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping patch test');
|
||||
return;
|
||||
}
|
||||
|
||||
const patch = await client.getPullRequestPatch(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(patch).toHaveProperty('patch');
|
||||
expect(typeof patch.patch).toBe('string');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Pull Request Tasks', () => {
|
||||
it('should get pull request tasks', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping tasks test');
|
||||
return;
|
||||
}
|
||||
|
||||
const tasks = await client.getPullRequestTasks(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(tasks).toBeDefined();
|
||||
});
|
||||
|
||||
it('should get pull request task count', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping task count test');
|
||||
return;
|
||||
}
|
||||
|
||||
const taskCount = await client.getPullRequestTaskCount(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(taskCount).toBeDefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('Full Pull Request', () => {
|
||||
it('should get full pull request details', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG);
|
||||
if (prs.length === 0) {
|
||||
console.log('⚠️ No PRs found, skipping full PR test');
|
||||
return;
|
||||
}
|
||||
|
||||
const fullPR = await client.getFullPullRequest(WORKSPACE, REPO_SLUG, prs[0].id);
|
||||
expect(fullPR).toHaveProperty('id');
|
||||
expect(fullPR).toHaveProperty('title');
|
||||
expect(fullPR).toHaveProperty('source');
|
||||
expect(fullPR).toHaveProperty('destination');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Bitbucket Router Integration', () => {
|
||||
let router: BitbucketRouter;
|
||||
|
||||
beforeAll(() => {
|
||||
delete process.env.DEFAULT_WORKSPACE;
|
||||
delete process.env.DEFAULT_REPO;
|
||||
router = new BitbucketRouter();
|
||||
});
|
||||
|
||||
beforeEach(() => {
|
||||
delete process.env.DEFAULT_WORKSPACE;
|
||||
delete process.env.DEFAULT_REPO;
|
||||
});
|
||||
|
||||
describe('Router parameter validation', () => {
|
||||
it('should require workspace for list_pull_requests', async () => {
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
repository: REPO_SLUG
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
it('should require workspace and pullRequestId for get_pull_request', async () => {
|
||||
const result = await router.executeTool('get_pull_request', {
|
||||
workspace: WORKSPACE
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle unknown tool', async () => {
|
||||
const result = await router.executeTool('unknown_tool', {}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('Unknown tool');
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('Error Handling', () => {
|
||||
it('should handle invalid pull request ID', async () => {
|
||||
if (!HAS_TOKEN) return;
|
||||
try {
|
||||
await new BitbucketClient().getPullRequest(WORKSPACE, REPO_SLUG, 999999999);
|
||||
} catch (error) {
|
||||
expect(error).toBeDefined();
|
||||
}
|
||||
});
|
||||
});
|
||||
141
tests/unit/config.test.ts
Normal file
141
tests/unit/config.test.ts
Normal file
@@ -0,0 +1,141 @@
|
||||
/**
|
||||
* Unit tests for TokenConfigLoader
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||
import { TokenConfigLoader, TokenConfig, DefaultConfigLoader, DefaultConfig } from '../../src/config.js';
|
||||
|
||||
describe('TokenConfigLoader', () => {
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = { ...originalEnv };
|
||||
delete process.env.BITBUCKET_MCP_TOKEN;
|
||||
delete process.env.DEFAULT_WORKSPACE;
|
||||
delete process.env.DEFAULT_REPO;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('token validation', () => {
|
||||
it('should accept valid tokens (alphanumeric with special chars)', () => {
|
||||
const validPattern = /^[A-Za-z0-9=._-]+$/;
|
||||
|
||||
expect(validPattern.test('abc123def45678901234567')).toBe(true);
|
||||
expect(validPattern.test('ATBB1234567890abcdefgh')).toBe(true);
|
||||
expect(validPattern.test('Bearer_token.with=chars12')).toBe(true);
|
||||
expect(validPattern.test('a'.repeat(20))).toBe(true);
|
||||
});
|
||||
|
||||
it('should reject tokens that are too short', () => {
|
||||
const shortToken = 'abc123';
|
||||
expect(shortToken.length).toBeLessThan(20);
|
||||
});
|
||||
|
||||
it('should reject tokens with invalid characters', () => {
|
||||
const invalidToken = 'token with spaces!@#$%';
|
||||
const validPattern = /^[A-Za-z0-9=._-]+$/;
|
||||
expect(validPattern.test(invalidToken)).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('environment variable loading', () => {
|
||||
it('should load token from BITBUCKET_MCP_TOKEN env var', async () => {
|
||||
process.env.BITBUCKET_MCP_EMAIL = 'test@example.com';
|
||||
process.env.BITBUCKET_MCP_TOKEN = 'valid_test_token_12345678901234567';
|
||||
|
||||
const config = await TokenConfigLoader.load();
|
||||
|
||||
expect(config.token).toBe('valid_test_token_12345678901234567');
|
||||
expect(config.source).toBe('env');
|
||||
});
|
||||
});
|
||||
|
||||
describe('TokenConfig interface', () => {
|
||||
it('should return correct config structure for env source', async () => {
|
||||
process.env.BITBUCKET_MCP_TOKEN = 'test_token_12345678901234567';
|
||||
|
||||
const config = await TokenConfigLoader.load();
|
||||
|
||||
expect(config).toHaveProperty('token');
|
||||
expect(config).toHaveProperty('source');
|
||||
expect(['env', 'dotenv', 'prompt']).toContain(config.source);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe('DefaultConfigLoader', () => {
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = { ...originalEnv };
|
||||
delete process.env.DEFAULT_WORKSPACE;
|
||||
delete process.env.DEFAULT_REPO;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
describe('load', () => {
|
||||
it('should return null values when env vars are not set', () => {
|
||||
const config = DefaultConfigLoader.load();
|
||||
|
||||
expect(config.workspace).toBeNull();
|
||||
expect(config.repo).toBeNull();
|
||||
});
|
||||
|
||||
it('should return workspace and repo from env vars when set', () => {
|
||||
process.env.DEFAULT_WORKSPACE = 'my-workspace';
|
||||
process.env.DEFAULT_REPO = 'my-repo';
|
||||
|
||||
const config = DefaultConfigLoader.load();
|
||||
|
||||
expect(config.workspace).toBe('my-workspace');
|
||||
expect(config.repo).toBe('my-repo');
|
||||
});
|
||||
|
||||
it('should return partial config when only workspace is set', () => {
|
||||
process.env.DEFAULT_WORKSPACE = 'only-workspace';
|
||||
|
||||
const config = DefaultConfigLoader.load();
|
||||
|
||||
expect(config.workspace).toBe('only-workspace');
|
||||
expect(config.repo).toBeNull();
|
||||
});
|
||||
|
||||
it('should return partial config when only repo is set', () => {
|
||||
process.env.DEFAULT_REPO = 'only-repo';
|
||||
|
||||
const config = DefaultConfigLoader.load();
|
||||
|
||||
expect(config.workspace).toBeNull();
|
||||
expect(config.repo).toBe('only-repo');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getWorkspace', () => {
|
||||
it('should return null when DEFAULT_WORKSPACE is not set', () => {
|
||||
expect(DefaultConfigLoader.getWorkspace()).toBeNull();
|
||||
});
|
||||
|
||||
it('should return workspace value when set', () => {
|
||||
process.env.DEFAULT_WORKSPACE = 'test-workspace';
|
||||
expect(DefaultConfigLoader.getWorkspace()).toBe('test-workspace');
|
||||
});
|
||||
});
|
||||
|
||||
describe('getRepo', () => {
|
||||
it('should return null when DEFAULT_REPO is not set', () => {
|
||||
expect(DefaultConfigLoader.getRepo()).toBeNull();
|
||||
});
|
||||
|
||||
it('should return repo value when set', () => {
|
||||
process.env.DEFAULT_REPO = 'test-repo';
|
||||
expect(DefaultConfigLoader.getRepo()).toBe('test-repo');
|
||||
});
|
||||
});
|
||||
});
|
||||
444
tests/unit/router.test.ts
Normal file
444
tests/unit/router.test.ts
Normal file
@@ -0,0 +1,444 @@
|
||||
/**
|
||||
* Unit tests for BitbucketRouter
|
||||
*/
|
||||
|
||||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { BitbucketRouter, ToolResult } from '../../src/router.js';
|
||||
|
||||
// Mock the BitbucketClient
|
||||
vi.mock('../../src/bitbucket-client.js', () => {
|
||||
return {
|
||||
BitbucketClient: vi.fn().mockImplementation(() => ({
|
||||
listPullRequests: vi.fn().mockResolvedValue([
|
||||
{ id: 1, title: 'Test PR 1', state: 'OPEN' },
|
||||
{ id: 2, title: 'Test PR 2', state: 'MERGED' }
|
||||
]),
|
||||
getPullRequest: vi.fn().mockResolvedValue({
|
||||
id: 1,
|
||||
title: 'Test PR',
|
||||
state: 'OPEN',
|
||||
source: { branch: { name: 'feature' } },
|
||||
destination: { branch: { name: 'main' } }
|
||||
}),
|
||||
getPullRequestStatus: vi.fn().mockResolvedValue({
|
||||
id: 1,
|
||||
title: 'Test PR',
|
||||
state: 'OPEN',
|
||||
status: 'NORMAL'
|
||||
}),
|
||||
getPullRequestActivities: vi.fn().mockResolvedValue({
|
||||
values: [{ action: 'OPEN' }]
|
||||
}),
|
||||
getPullRequestChanges: vi.fn().mockResolvedValue({
|
||||
values: [{ type: 'modified', path: 'src/test.ts' }]
|
||||
}),
|
||||
getPullRequestComments: vi.fn().mockResolvedValue({
|
||||
values: [{ id: 1, content: { raw: 'Test comment' } }]
|
||||
}),
|
||||
getPullRequestComment: vi.fn().mockResolvedValue({
|
||||
id: 1,
|
||||
content: { raw: 'Test comment' }
|
||||
}),
|
||||
getPullRequestCommits: vi.fn().mockResolvedValue({
|
||||
values: [{ hash: 'abc123' }]
|
||||
}),
|
||||
getPullRequestDiff: vi.fn().mockResolvedValue({
|
||||
diff: '--- test.ts\n+++ test.ts\n@@ -1 +1 @@'
|
||||
}),
|
||||
getPullRequestPatch: vi.fn().mockResolvedValue({
|
||||
patch: '--- original\n+++ modified'
|
||||
}),
|
||||
getPullRequestParticipants: vi.fn().mockResolvedValue({
|
||||
values: [{ user: { display_name: 'Test User' } }]
|
||||
}),
|
||||
getPullRequestReviewers: vi.fn().mockResolvedValue([
|
||||
{ user: { display_name: 'Reviewer 1' } }
|
||||
]),
|
||||
getPullRequestTasks: vi.fn().mockResolvedValue({
|
||||
values: []
|
||||
}),
|
||||
getPullRequestTaskCount: vi.fn().mockResolvedValue({ count: 0 }),
|
||||
getFullPullRequest: vi.fn().mockResolvedValue({
|
||||
id: 1,
|
||||
title: 'Test PR',
|
||||
description: 'Test description'
|
||||
})
|
||||
}))
|
||||
};
|
||||
});
|
||||
|
||||
describe('BitbucketRouter', () => {
|
||||
let router: BitbucketRouter;
|
||||
const originalEnv = { ...process.env };
|
||||
|
||||
beforeEach(() => {
|
||||
process.env = { ...originalEnv };
|
||||
delete process.env.DEFAULT_WORKSPACE;
|
||||
delete process.env.DEFAULT_REPO;
|
||||
router = new BitbucketRouter();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
process.env = originalEnv;
|
||||
});
|
||||
|
||||
describe('list_pull_requests', () => {
|
||||
it('should return pull requests for valid parameters', async () => {
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('pull_requests');
|
||||
expect(result.data.pull_requests).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('should fail without workspace', async () => {
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
repository: 'test-repo'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('Missing required parameters');
|
||||
});
|
||||
|
||||
it('should fail without repository', async () => {
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
workspace: 'test-workspace'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('Missing required parameters');
|
||||
});
|
||||
|
||||
it('should accept "repo" as alias for "repository"', async () => {
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
workspace: 'test-workspace',
|
||||
repo: 'test-repo'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should use DEFAULT_WORKSPACE when workspace is not provided', async () => {
|
||||
process.env.DEFAULT_WORKSPACE = 'default-workspace';
|
||||
process.env.DEFAULT_REPO = 'default-repo';
|
||||
|
||||
const result = await router.executeTool('list_pull_requests', {}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should use DEFAULT_REPO when repository is not provided', async () => {
|
||||
process.env.DEFAULT_REPO = 'default-repo';
|
||||
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
workspace: 'test-workspace'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should use both DEFAULT_WORKSPACE and DEFAULT_REPO when neither is provided', async () => {
|
||||
process.env.DEFAULT_WORKSPACE = 'default-workspace';
|
||||
process.env.DEFAULT_REPO = 'default-repo';
|
||||
|
||||
const result = await router.executeTool('list_pull_requests', {}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should prefer explicit params over defaults', async () => {
|
||||
process.env.DEFAULT_WORKSPACE = 'default-workspace';
|
||||
process.env.DEFAULT_REPO = 'default-repo';
|
||||
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
workspace: 'explicit-workspace',
|
||||
repository: 'explicit-repo'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should still fail when no workspace and no default available', async () => {
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
repository: 'test-repo'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('Missing required parameters');
|
||||
});
|
||||
|
||||
it('should still fail when no repository and no default available', async () => {
|
||||
const result = await router.executeTool('list_pull_requests', {
|
||||
workspace: 'test-workspace'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('Missing required parameters');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request', () => {
|
||||
it('should return pull request for valid parameters', async () => {
|
||||
const result = await router.executeTool('get_pull_request', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('id', 1);
|
||||
});
|
||||
|
||||
it('should accept pullRequestId as pr_id', async () => {
|
||||
const result = await router.executeTool('get_pull_request', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pr_id: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
|
||||
it('should fail with invalid pullRequestId', async () => {
|
||||
const result = await router.executeTool('get_pull_request', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 'invalid'
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_activities', () => {
|
||||
it('should return activities for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_activities', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('values');
|
||||
});
|
||||
|
||||
it('should accept pagination parameters', async () => {
|
||||
const result = await router.executeTool('get_pull_request_activities', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1,
|
||||
limit: 10,
|
||||
start: 0
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_changes', () => {
|
||||
it('should return changes for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_changes', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('values');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_comments', () => {
|
||||
it('should return comments for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_comments', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('values');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_comment', () => {
|
||||
it('should return specific comment', async () => {
|
||||
const result = await router.executeTool('get_pull_request_comment', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1,
|
||||
commentId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('id', 1);
|
||||
});
|
||||
|
||||
it('should fail without commentId', async () => {
|
||||
const result = await router.executeTool('get_pull_request_comment', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_commits', () => {
|
||||
it('should return commits for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_commits', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('values');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_diff', () => {
|
||||
it('should return diff for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_diff', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('diff');
|
||||
});
|
||||
|
||||
it('should accept path and context parameters', async () => {
|
||||
const result = await router.executeTool('get_pull_request_diff', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1,
|
||||
path: 'src/test.ts',
|
||||
context: 3
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_patch', () => {
|
||||
it('should return patch for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_patch', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('patch');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_participants', () => {
|
||||
it('should return participants for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_participants', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('values');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_reviewers', () => {
|
||||
it('should return reviewers for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_reviewers', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_status', () => {
|
||||
it('should return status for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_status', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('state');
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_tasks', () => {
|
||||
it('should return tasks for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_tasks', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_pull_request_task_count', () => {
|
||||
it('should return task count for valid PR', async () => {
|
||||
const result = await router.executeTool('get_pull_request_task_count', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('get_full_pull_request', () => {
|
||||
it('should return full PR details', async () => {
|
||||
const result = await router.executeTool('get_full_pull_request', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo',
|
||||
pullRequestId: 1
|
||||
}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(true);
|
||||
expect(result.data).toHaveProperty('id');
|
||||
expect(result.data).toHaveProperty('description');
|
||||
});
|
||||
});
|
||||
|
||||
describe('unknown tool', () => {
|
||||
it('should return error for unknown tool', async () => {
|
||||
const result = await router.executeTool('unknown_tool', {}) as ToolResult;
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
expect(result.error).toContain('Unknown tool');
|
||||
});
|
||||
});
|
||||
|
||||
describe('error handling', () => {
|
||||
it('should handle API errors gracefully', async () => {
|
||||
// Create router with a mock that throws
|
||||
const errorRouter = new BitbucketRouter();
|
||||
|
||||
const result = await errorRouter.executeTool('list_pull_requests', {
|
||||
workspace: 'test-workspace',
|
||||
repository: 'test-repo'
|
||||
}) as ToolResult;
|
||||
|
||||
// With our mock, this should succeed, but error cases should return proper format
|
||||
if (!result.success) {
|
||||
expect(result.error).toBeDefined();
|
||||
expect(typeof result.error).toBe('string');
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user