Compare commits
7 Commits
a7121bc185
...
v1.1.1
| Author | SHA1 | Date | |
|---|---|---|---|
| 2df53cbf10 | |||
| 8a410c7fd7 | |||
| 5d911d91a7 | |||
| 148fe7e51b | |||
| 9c7d983df4 | |||
| ab73c92e6d | |||
| 980d79d8d1 |
16
.env.template
Normal file
16
.env.template
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
BITBUCKET_MCP_EMAIL=your_email@example.com
|
||||||
|
BITBUCKET_MCP_TOKEN=your_api_token
|
||||||
|
# Find these in your Bitbucket repo URL: https://bitbucket.org/my-workspace/my-repo/pull-requests
|
||||||
|
DEFAULT_WORKSPACE=my-workspace
|
||||||
|
DEFAULT_REPO=my-repo
|
||||||
|
|
||||||
|
# Integration test write operations
|
||||||
|
# ---------------------------------------------------------------------------------
|
||||||
|
# Set RUN_WRITE_TESTS=true to enable tests that create/modify/delete data.
|
||||||
|
#
|
||||||
|
# TEST_PR_ID must also be set — write tests will only run against this specific PR
|
||||||
|
# to avoid accidentally modifying unrelated pull requests.
|
||||||
|
# ---------------------------------------------------------------------------------
|
||||||
|
# RUN_WRITE_TESTS=true
|
||||||
|
# TEST_PR_ID=123
|
||||||
|
# ---------------------------------------------------------------------------------
|
||||||
6
.gitignore
vendored
6
.gitignore
vendored
@@ -6,9 +6,9 @@ dist/
|
|||||||
tsconfig.tsbuildinfo
|
tsconfig.tsbuildinfo
|
||||||
|
|
||||||
# Environment
|
# Environment
|
||||||
.env
|
*.env
|
||||||
.env.local
|
*.env.*
|
||||||
.env.*.local
|
!*.env.template
|
||||||
|
|
||||||
# IDE
|
# IDE
|
||||||
.idea/
|
.idea/
|
||||||
|
|||||||
10
CLAUDE.md
10
CLAUDE.md
@@ -41,9 +41,15 @@ The server is a 3-layer pipeline: **MCP protocol** -> **Router** -> **Bitbucket
|
|||||||
|----------|---------|
|
|----------|---------|
|
||||||
| `BITBUCKET_MCP_EMAIL` | Bitbucket account email (Basic Auth username) |
|
| `BITBUCKET_MCP_EMAIL` | Bitbucket account email (Basic Auth username) |
|
||||||
| `BITBUCKET_MCP_TOKEN` | Bitbucket API token (Basic Auth password) |
|
| `BITBUCKET_MCP_TOKEN` | Bitbucket API token (Basic Auth password) |
|
||||||
| `DEFAULT_WORKSPACE` | Optional default workspace slug |
|
| `DEFAULT_WORKSPACE` | Optional default workspace slug (from your repo URL: `https://bitbucket.org/{workspace}/{repo}/...`) |
|
||||||
| `DEFAULT_REPO` | Optional default repository slug |
|
| `DEFAULT_REPO` | Optional default repository slug (from your repo URL: `https://bitbucket.org/{workspace}/{repo}/...`) |
|
||||||
|
| `RUN_WRITE_TESTS` | Set to `true` to enable write integration tests |
|
||||||
|
| `TEST_PR_ID` | PR ID to use for write integration tests (required when `RUN_WRITE_TESTS=true`) |
|
||||||
|
|
||||||
|
Copy `.env.template` to `.env` and fill in your values for local development and testing.
|
||||||
|
|
||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Tests use vitest with 30s timeouts. Unit tests mock the Bitbucket client; integration tests call the real API and require valid credentials in the environment.
|
Tests use vitest with 30s timeouts. Unit tests mock the Bitbucket client; integration tests call the real API and require valid credentials in the environment.
|
||||||
|
|
||||||
|
Write integration tests (approve/unapprove, comments, tasks) are skipped by default. They require both `RUN_WRITE_TESTS=true` and `TEST_PR_ID` to be set. Write tests never pick a random PR — if `TEST_PR_ID` is missing the test is skipped with a warning.
|
||||||
|
|||||||
76
README.md
76
README.md
@@ -15,16 +15,19 @@
|
|||||||
|
|
||||||
## Overview
|
## Overview
|
||||||
|
|
||||||
This MCP server exposes **read-only** Bitbucket Cloud Pull Request operations as tools that AI agents (Claude, etc.) can invoke over stdio. It connects your AI workflow directly to your Bitbucket repositories.
|
This MCP server exposes Bitbucket Cloud operations as tools that AI agents (Claude, etc.) can invoke over stdio. It connects your AI workflow directly to your Bitbucket repositories — covering everything from read-only PR inspection to full write operations.
|
||||||
|
|
||||||
### Capabilities
|
### Capabilities
|
||||||
|
|
||||||
| Category | Operations |
|
| Category | Operations |
|
||||||
|----------|-----------|
|
|----------|-----------|
|
||||||
| **Pull Requests** | List, get details, get full expanded PR |
|
| **Workspaces & Repos** | List workspaces, list/get repositories, list branches |
|
||||||
|
| **Pull Requests (read)** | List, get details, get full expanded PR, status |
|
||||||
|
| **Pull Requests (write)** | Create, update, merge, decline |
|
||||||
| **Code Review** | Diff, patch, file changes, commits |
|
| **Code Review** | Diff, patch, file changes, commits |
|
||||||
| **Collaboration** | Comments, activities, participants, reviewers |
|
| **Collaboration** | Comments (read/add/edit/delete), activities, participants, reviewers |
|
||||||
| **Workflow** | Status, tasks, task count |
|
| **Review Workflow** | Approve, unapprove, request changes, remove request-changes |
|
||||||
|
| **Tasks** | Get tasks, task count, create/update/delete tasks |
|
||||||
| **Auth** | Token validation |
|
| **Auth** | Token validation |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -51,7 +54,7 @@ npm run build
|
|||||||
|
|
||||||
Add the MCP server to your Claude configuration:
|
Add the MCP server to your Claude configuration:
|
||||||
|
|
||||||
**Claude Code** — edit `~/.claude/settings.json` (global) or `.claude/settings.json` (per-project):
|
**Claude Code** — edit `~/.claude.json` (global) or `.claude/settings.json` (per-project):
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{
|
{
|
||||||
@@ -92,6 +95,8 @@ Add the MCP server to your Claude configuration:
|
|||||||
> **Important:**
|
> **Important:**
|
||||||
> - Replace `/absolute/path/to/bitbucket-mcp` with the actual absolute path where you cloned the repo.
|
> - Replace `/absolute/path/to/bitbucket-mcp` with the actual absolute path where you cloned the repo.
|
||||||
> - `DEFAULT_WORKSPACE` and `DEFAULT_REPO` are optional but convenient — they let you omit those parameters from every tool call.
|
> - `DEFAULT_WORKSPACE` and `DEFAULT_REPO` are optional but convenient — they let you omit those parameters from every tool call.
|
||||||
|
> - Read them directly from your Bitbucket repo URL: `https://bitbucket.org/{workspace}/{repository}/pull-requests`
|
||||||
|
> For example, `https://bitbucket.org/my-workspace/my-repo/pull-requests` → `DEFAULT_WORKSPACE=my-workspace`, `DEFAULT_REPO=my-repo`
|
||||||
> - Get your API token from [Atlassian API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens).
|
> - Get your API token from [Atlassian API Tokens](https://id.atlassian.com/manage-profile/security/api-tokens).
|
||||||
|
|
||||||
### 4. Verify
|
### 4. Verify
|
||||||
@@ -108,6 +113,15 @@ Restart Claude (or reload MCP servers), then ask Claude to run the `validate_tok
|
|||||||
|------|-------------|
|
|------|-------------|
|
||||||
| `validate_token` | Verify credentials are valid |
|
| `validate_token` | Verify credentials are valid |
|
||||||
|
|
||||||
|
### Workspaces & Repositories
|
||||||
|
|
||||||
|
| Tool | Description | Key Parameters |
|
||||||
|
|------|-------------|----------------|
|
||||||
|
| `list_workspaces` | List all workspaces the authenticated user belongs to | `page?`, `pagelen?` |
|
||||||
|
| `list_repositories` | List repositories in a workspace | `workspace`, `role?`, `page?`, `pagelen?` |
|
||||||
|
| `get_repository` | Get metadata for a specific repository | `workspace`, `repository` |
|
||||||
|
| `list_branches` | List branches in a repository | `workspace`, `repository`, `filter_by_name?`, `page?`, `pagelen?` |
|
||||||
|
|
||||||
### Pull Request Discovery
|
### Pull Request Discovery
|
||||||
|
|
||||||
| Tool | Description | Key Parameters |
|
| Tool | Description | Key Parameters |
|
||||||
@@ -117,6 +131,24 @@ Restart Claude (or reload MCP servers), then ask Claude to run the `validate_tok
|
|||||||
| `get_full_pull_request` | Get PR with all fields expanded | `workspace`, `repository`, `pullRequestId` |
|
| `get_full_pull_request` | Get PR with all fields expanded | `workspace`, `repository`, `pullRequestId` |
|
||||||
| `get_pull_request_status` | Get PR state (open/merged/declined) | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_status` | Get PR state (open/merged/declined) | `workspace`, `repository`, `pullRequestId` |
|
||||||
|
|
||||||
|
### Pull Request Write Operations
|
||||||
|
|
||||||
|
| Tool | Description | Key Parameters |
|
||||||
|
|------|-------------|----------------|
|
||||||
|
| `create_pull_request` | Create a new pull request | `workspace`, `repository`, `title`, `source_branch`, `destination_branch`, `description?`, `reviewers?`, `close_source_branch?` |
|
||||||
|
| `update_pull_request` | Update title, description, reviewers, or destination branch | `workspace`, `repository`, `pullRequestId`, `title?`, `description?`, `reviewers?`, `destination_branch?` |
|
||||||
|
| `merge_pull_request` | Merge an open pull request | `workspace`, `repository`, `pullRequestId`, `merge_strategy?`, `commit_message?`, `close_source_branch?` |
|
||||||
|
| `decline_pull_request` | Decline (close) an open pull request | `workspace`, `repository`, `pullRequestId` |
|
||||||
|
|
||||||
|
### Review Workflow
|
||||||
|
|
||||||
|
| Tool | Description | Key Parameters |
|
||||||
|
|------|-------------|----------------|
|
||||||
|
| `approve_pull_request` | Approve a pull request | `workspace`, `repository`, `pullRequestId` |
|
||||||
|
| `unapprove_pull_request` | Remove your approval | `workspace`, `repository`, `pullRequestId` |
|
||||||
|
| `request_changes_pull_request` | Request changes on a pull request | `workspace`, `repository`, `pullRequestId` |
|
||||||
|
| `remove_request_changes_pull_request` | Remove a request-changes vote | `workspace`, `repository`, `pullRequestId` |
|
||||||
|
|
||||||
### Code Changes
|
### Code Changes
|
||||||
|
|
||||||
| Tool | Description | Key Parameters |
|
| Tool | Description | Key Parameters |
|
||||||
@@ -126,12 +158,20 @@ Restart Claude (or reload MCP servers), then ask Claude to run the `validate_tok
|
|||||||
| `get_pull_request_changes` | List modified files | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_changes` | List modified files | `workspace`, `repository`, `pullRequestId` |
|
||||||
| `get_pull_request_commits` | List commits in PR | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_commits` | List commits in PR | `workspace`, `repository`, `pullRequestId` |
|
||||||
|
|
||||||
### Collaboration
|
### Comments
|
||||||
|
|
||||||
| Tool | Description | Key Parameters |
|
| Tool | Description | Key Parameters |
|
||||||
|------|-------------|----------------|
|
|------|-------------|----------------|
|
||||||
| `get_pull_request_comments` | Get all comments | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_comments` | Get all comments | `workspace`, `repository`, `pullRequestId` |
|
||||||
| `get_pull_request_comment` | Get a specific comment | `workspace`, `repository`, `pullRequestId`, `commentId` |
|
| `get_pull_request_comment` | Get a specific comment | `workspace`, `repository`, `pullRequestId`, `commentId` |
|
||||||
|
| `add_pull_request_comment` | Add a general or inline comment | `workspace`, `repository`, `pullRequestId`, `content`, `inline_path?`, `inline_line?`, `parent_comment_id?` |
|
||||||
|
| `update_pull_request_comment` | Edit an existing comment | `workspace`, `repository`, `pullRequestId`, `commentId`, `content` |
|
||||||
|
| `delete_pull_request_comment` | Delete a comment | `workspace`, `repository`, `pullRequestId`, `commentId` |
|
||||||
|
|
||||||
|
### Activities & Participants
|
||||||
|
|
||||||
|
| Tool | Description | Key Parameters |
|
||||||
|
|------|-------------|----------------|
|
||||||
| `get_pull_request_activities` | Get activity feed | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_activities` | Get activity feed | `workspace`, `repository`, `pullRequestId` |
|
||||||
| `get_pull_request_participants` | Get all participants | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_participants` | Get all participants | `workspace`, `repository`, `pullRequestId` |
|
||||||
| `get_pull_request_reviewers` | Get assigned reviewers | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_reviewers` | Get assigned reviewers | `workspace`, `repository`, `pullRequestId` |
|
||||||
@@ -142,8 +182,11 @@ Restart Claude (or reload MCP servers), then ask Claude to run the `validate_tok
|
|||||||
|------|-------------|----------------|
|
|------|-------------|----------------|
|
||||||
| `get_pull_request_tasks` | Get PR tasks | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_tasks` | Get PR tasks | `workspace`, `repository`, `pullRequestId` |
|
||||||
| `get_pull_request_task_count` | Get task count | `workspace`, `repository`, `pullRequestId` |
|
| `get_pull_request_task_count` | Get task count | `workspace`, `repository`, `pullRequestId` |
|
||||||
|
| `create_pull_request_task` | Create a review task | `workspace`, `repository`, `pullRequestId`, `content`, `comment_id?` |
|
||||||
|
| `update_pull_request_task` | Update or resolve/unresolve a task | `workspace`, `repository`, `pullRequestId`, `taskId`, `content?`, `state?` |
|
||||||
|
| `delete_pull_request_task` | Delete a task | `workspace`, `repository`, `pullRequestId`, `taskId` |
|
||||||
|
|
||||||
> **Pagination:** Tools that return lists support `limit` and `start` parameters.
|
> **Pagination:** Tools that return lists support `page` and `pagelen` parameters.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -184,13 +227,17 @@ Response:
|
|||||||
|
|
||||||
### Running Locally
|
### Running Locally
|
||||||
|
|
||||||
For development, you can use a `.env` file instead of configuring credentials in the MCP client:
|
For development, you can use a `.env` file instead of configuring credentials in the MCP client. Copy `.env.template` to `.env` and fill in your values:
|
||||||
|
|
||||||
```env
|
```env
|
||||||
BITBUCKET_MCP_EMAIL=your_email@example.com
|
BITBUCKET_MCP_EMAIL=your_email@example.com
|
||||||
BITBUCKET_MCP_TOKEN=your_api_token
|
BITBUCKET_MCP_TOKEN=your_api_token
|
||||||
DEFAULT_WORKSPACE=my-workspace
|
DEFAULT_WORKSPACE=my-workspace
|
||||||
DEFAULT_REPO=my-repo
|
DEFAULT_REPO=my-repo
|
||||||
|
|
||||||
|
# Only needed when running write integration tests (see Testing below)
|
||||||
|
# RUN_WRITE_TESTS=true
|
||||||
|
# TEST_PR_ID=123
|
||||||
```
|
```
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
@@ -204,13 +251,13 @@ npm start
|
|||||||
### Testing
|
### Testing
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
# Run tests
|
# Run all tests (unit only by default)
|
||||||
npm test
|
npm test
|
||||||
|
|
||||||
# Run tests in watch mode
|
# Run tests in watch mode
|
||||||
npm run test:watch
|
npm run test:watch
|
||||||
|
|
||||||
# Integration tests (requires valid credentials)
|
# Integration tests — read-only, requires valid credentials in .env
|
||||||
npm run test:integration
|
npm run test:integration
|
||||||
|
|
||||||
# Type check
|
# Type check
|
||||||
@@ -220,6 +267,15 @@ npx tsc --noEmit
|
|||||||
npm run test:coverage
|
npm run test:coverage
|
||||||
```
|
```
|
||||||
|
|
||||||
|
**Write operation tests** (approve/unapprove, comments, tasks) are disabled by default and require two additional variables in your `.env`:
|
||||||
|
|
||||||
|
| Variable | Description |
|
||||||
|
|----------|-------------|
|
||||||
|
| `RUN_WRITE_TESTS=true` | Opt in to running write tests |
|
||||||
|
| `TEST_PR_ID=<id>` | ID of a PR you own and can safely modify |
|
||||||
|
|
||||||
|
`TEST_PR_ID` is mandatory — write tests will never fall back to a random PR from the repository. If it is not set, each write test is skipped with a warning. This prevents accidental modifications to PRs you don't intend to touch.
|
||||||
|
|
||||||
### Project Structure
|
### Project Structure
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ export class RepositoryClient extends BaseClient {
|
|||||||
const params: Record<string, any> = {};
|
const params: Record<string, any> = {};
|
||||||
if (options?.page) params.page = options.page;
|
if (options?.page) params.page = options.page;
|
||||||
if (options?.pagelen) params.pagelen = options.pagelen;
|
if (options?.pagelen) params.pagelen = options.pagelen;
|
||||||
const response = await this.axiosInstance.get('/workspaces', { params });
|
const response = await this.axiosInstance.get('/user/workspaces', { params });
|
||||||
return response.data;
|
return response.data;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
throw new Error(`Failed to list workspaces: ${this.formatError(error)}`);
|
throw new Error(`Failed to list workspaces: ${this.formatError(error)}`);
|
||||||
|
|||||||
@@ -294,6 +294,7 @@ describe('Error Handling', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
const RUN_WRITES = process.env.RUN_WRITE_TESTS === 'true';
|
const RUN_WRITES = process.env.RUN_WRITE_TESTS === 'true';
|
||||||
|
const TEST_PR_ID = process.env.TEST_PR_ID ? parseInt(process.env.TEST_PR_ID, 10) : undefined;
|
||||||
|
|
||||||
describe('Repository / Workspace (read)', () => {
|
describe('Repository / Workspace (read)', () => {
|
||||||
let client: BitbucketClient;
|
let client: BitbucketClient;
|
||||||
@@ -340,31 +341,28 @@ describe('Repository / Workspace (read)', () => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('PR Write Operations (requires RUN_WRITE_TESTS=true)', () => {
|
describe('PR Write Operations (requires RUN_WRITE_TESTS=true and TEST_PR_ID)', () => {
|
||||||
let client: BitbucketClient;
|
let client: BitbucketClient;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
client = new BitbucketClient();
|
client = new BitbucketClient();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should approve a PR', async () => {
|
it('should approve then immediately unapprove a PR', async () => {
|
||||||
if (!HAS_TOKEN || !RUN_WRITES) return;
|
if (!HAS_TOKEN || !RUN_WRITES) return;
|
||||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG, { state: 'OPEN' });
|
if (!TEST_PR_ID) { console.log('⚠️ TEST_PR_ID not set, skipping approve/unapprove test'); return; }
|
||||||
if (prs.length === 0) { console.log('No open PRs, skipping'); return; }
|
|
||||||
const result = await client.approvePullRequest(WORKSPACE, REPO_SLUG, prs[0].id);
|
|
||||||
expect(result).toBeDefined();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should unapprove a PR', async () => {
|
await client.approvePullRequest(WORKSPACE, REPO_SLUG, TEST_PR_ID);
|
||||||
if (!HAS_TOKEN || !RUN_WRITES) return;
|
try {
|
||||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG, { state: 'OPEN' });
|
const unapproved = await client.unapprovePullRequest(WORKSPACE, REPO_SLUG, TEST_PR_ID);
|
||||||
if (prs.length === 0) { console.log('No open PRs, skipping'); return; }
|
expect(unapproved).toHaveProperty('success', true);
|
||||||
const result = await client.unapprovePullRequest(WORKSPACE, REPO_SLUG, prs[0].id);
|
} finally {
|
||||||
expect(result).toHaveProperty('success', true);
|
await client.unapprovePullRequest(WORKSPACE, REPO_SLUG, TEST_PR_ID).catch(() => {});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Comment / Task Write Operations (requires RUN_WRITE_TESTS=true)', () => {
|
describe('Comment / Task Write Operations (requires RUN_WRITE_TESTS=true and TEST_PR_ID)', () => {
|
||||||
let client: BitbucketClient;
|
let client: BitbucketClient;
|
||||||
|
|
||||||
beforeAll(() => {
|
beforeAll(() => {
|
||||||
@@ -373,35 +371,33 @@ describe('Comment / Task Write Operations (requires RUN_WRITE_TESTS=true)', () =
|
|||||||
|
|
||||||
it('should add, update, then delete a comment', async () => {
|
it('should add, update, then delete a comment', async () => {
|
||||||
if (!HAS_TOKEN || !RUN_WRITES) return;
|
if (!HAS_TOKEN || !RUN_WRITES) return;
|
||||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG, { state: 'OPEN' });
|
if (!TEST_PR_ID) { console.log('⚠️ TEST_PR_ID not set, skipping comment test'); return; }
|
||||||
if (prs.length === 0) { console.log('No open PRs, skipping'); return; }
|
|
||||||
const prId = prs[0].id;
|
|
||||||
|
|
||||||
const added = await client.addPullRequestComment(WORKSPACE, REPO_SLUG, prId, { content: 'Integration test comment' });
|
const added = await client.addPullRequestComment(WORKSPACE, REPO_SLUG, TEST_PR_ID, { content: 'Integration test comment' });
|
||||||
expect(added).toHaveProperty('id');
|
expect(added).toHaveProperty('id');
|
||||||
const commentId = added.id;
|
const commentId = added.id;
|
||||||
|
|
||||||
const updated = await client.updatePullRequestComment(WORKSPACE, REPO_SLUG, prId, commentId, { content: 'Updated comment' });
|
try {
|
||||||
expect(updated).toHaveProperty('id', commentId);
|
const updated = await client.updatePullRequestComment(WORKSPACE, REPO_SLUG, TEST_PR_ID, commentId, { content: 'Updated comment' });
|
||||||
|
expect(updated).toHaveProperty('id', commentId);
|
||||||
const deleted = await client.deletePullRequestComment(WORKSPACE, REPO_SLUG, prId, commentId);
|
} finally {
|
||||||
expect(deleted).toHaveProperty('success', true);
|
await client.deletePullRequestComment(WORKSPACE, REPO_SLUG, TEST_PR_ID, commentId).catch(() => {});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create, resolve, then delete a task', async () => {
|
it('should create, resolve, then delete a task', async () => {
|
||||||
if (!HAS_TOKEN || !RUN_WRITES) return;
|
if (!HAS_TOKEN || !RUN_WRITES) return;
|
||||||
const prs = await client.listPullRequests(WORKSPACE, REPO_SLUG, { state: 'OPEN' });
|
if (!TEST_PR_ID) { console.log('⚠️ TEST_PR_ID not set, skipping task test'); return; }
|
||||||
if (prs.length === 0) { console.log('No open PRs, skipping'); return; }
|
|
||||||
const prId = prs[0].id;
|
|
||||||
|
|
||||||
const created = await client.createPullRequestTask(WORKSPACE, REPO_SLUG, prId, { content: 'Integration test task' });
|
const created = await client.createPullRequestTask(WORKSPACE, REPO_SLUG, TEST_PR_ID, { content: 'Integration test task' });
|
||||||
expect(created).toHaveProperty('id');
|
expect(created).toHaveProperty('id');
|
||||||
const taskId = created.id;
|
const taskId = created.id;
|
||||||
|
|
||||||
const resolved = await client.updatePullRequestTask(WORKSPACE, REPO_SLUG, prId, taskId, { state: 'RESOLVED' });
|
try {
|
||||||
expect(resolved).toBeDefined();
|
const resolved = await client.updatePullRequestTask(WORKSPACE, REPO_SLUG, TEST_PR_ID, taskId, { state: 'RESOLVED' });
|
||||||
|
expect(resolved).toBeDefined();
|
||||||
const deleted = await client.deletePullRequestTask(WORKSPACE, REPO_SLUG, prId, taskId);
|
} finally {
|
||||||
expect(deleted).toHaveProperty('success', true);
|
await client.deletePullRequestTask(WORKSPACE, REPO_SLUG, TEST_PR_ID, taskId).catch(() => {});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user