@tiberriver256/mcp-server-azure-devops
Version:
Azure DevOps reference server for the Model Context Protocol (MCP)
337 lines • 16.4 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const feature_1 = require("./feature");
const feature_2 = require("../create-pull-request/feature");
const feature_3 = require("../../work-items/list-work-items/feature");
const test_helpers_1 = require("@/shared/test/test-helpers");
describe('updatePullRequest integration', () => {
let connection = null;
let projectName;
let repositoryName;
let pullRequestId;
let workItemId = null;
// Generate unique identifiers using timestamp
const timestamp = Date.now();
const randomSuffix = Math.floor(Math.random() * 1000);
const uniqueBranchName = `test-branch-${timestamp}-${randomSuffix}`;
const uniqueTitle = `Test PR ${timestamp}-${randomSuffix}`;
const updatedTitle = `Updated PR ${timestamp}-${randomSuffix}`;
beforeAll(async () => {
// Skip if integration tests should be skipped
if ((0, test_helpers_1.shouldSkipIntegrationTest)()) {
return;
}
// Get a real connection using environment variables
connection = await (0, test_helpers_1.getTestConnection)();
// Get project and repository names from environment variables
projectName = process.env.AZURE_DEVOPS_DEFAULT_PROJECT || 'DefaultProject';
repositoryName =
process.env.AZURE_DEVOPS_DEFAULT_REPOSITORY || 'DefaultRepo';
// Find an existing work item to use in tests
if (!connection) {
throw new Error('Connection is null');
}
const workItems = await (0, feature_3.listWorkItems)(connection, {
projectId: projectName,
top: 1, // Just need one work item
});
if (workItems && workItems.length > 0 && workItems[0].id) {
workItemId = workItems[0].id;
}
// Create a test pull request or find an existing one
const gitApi = await connection.getGitApi();
// Get the default branch's object ID
const repository = await gitApi.getRepository(repositoryName, projectName);
const defaultBranch = repository.defaultBranch?.replace('refs/heads/', '') || 'main';
// Get the latest commit on the default branch
const commits = await gitApi.getCommits(repositoryName, {
$top: 1,
itemVersion: {
version: defaultBranch,
versionType: 0, // 0 = branch
},
}, projectName);
if (!commits || commits.length === 0) {
throw new Error('No commits found in repository');
}
// Create a new branch
const refUpdate = {
name: `refs/heads/${uniqueBranchName}`,
oldObjectId: '0000000000000000000000000000000000000000',
newObjectId: commits[0].commitId,
};
const updateResult = await gitApi.updateRefs([refUpdate], repositoryName, projectName);
if (!updateResult ||
updateResult.length === 0 ||
!updateResult[0].success) {
throw new Error('Failed to create new branch');
}
// Create a test pull request
const testPullRequest = await (0, feature_2.createPullRequest)(connection, projectName, repositoryName, {
title: uniqueTitle,
description: 'Test pull request for integration testing',
sourceRefName: `refs/heads/${uniqueBranchName}`,
targetRefName: repository.defaultBranch || 'refs/heads/main',
isDraft: true,
});
pullRequestId = testPullRequest.pullRequestId;
});
afterAll(async () => {
// Clean up created resources
if (!(0, test_helpers_1.shouldSkipIntegrationTest)() && connection && pullRequestId) {
try {
// Check the current state of the pull request
const gitApi = await connection.getGitApi();
const pullRequest = await gitApi.getPullRequestById(pullRequestId, projectName);
// Only try to abandon if it's still active (status 1)
if (pullRequest && pullRequest.status === 1) {
await gitApi.updatePullRequest({
status: 2, // 2 = Abandoned
}, repositoryName, pullRequestId, projectName);
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
}
catch (_) {
// Ignore cleanup errors
}
}
});
test('should update pull request title and description', async () => {
// Skip if integration tests should be skipped
if ((0, test_helpers_1.shouldSkipIntegrationTest)() || !connection) {
console.log('Skipping test due to missing connection');
return;
}
const updatedDescription = 'Updated description for integration testing';
const result = await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
title: updatedTitle,
description: updatedDescription,
});
// Verify the update was successful
expect(result).toBeDefined();
expect(result.pullRequestId).toBe(pullRequestId);
expect(result.title).toBe(updatedTitle);
expect(result.description).toBe(updatedDescription);
}, 30000); // 30 second timeout for integration test
test('should update pull request draft status', async () => {
// Skip if integration tests should be skipped
if ((0, test_helpers_1.shouldSkipIntegrationTest)() || !connection) {
console.log('Skipping test due to missing connection');
return;
}
// Mark as not a draft
const result = await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
isDraft: false,
});
// Verify the update was successful
expect(result).toBeDefined();
expect(result.pullRequestId).toBe(pullRequestId);
expect(result.isDraft).toBe(false);
}, 30000); // 30 second timeout for integration test
test('should add work item links to pull request', async () => {
// Skip if no work items were found
if ((0, test_helpers_1.shouldSkipIntegrationTest)()) {
console.log('Skipping test due to missing connection or work item');
return;
}
// Add the work item link
const result = await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
addWorkItemIds: [workItemId],
});
// Verify the update was successful
expect(result).toBeDefined();
expect(result.pullRequestId).toBe(pullRequestId);
// Get the pull request work items using the proper API
const gitApi = await connection.getGitApi();
// Add a delay to allow Azure DevOps to process the work item link
await new Promise((resolve) => setTimeout(resolve, 5000));
// Use the getPullRequestWorkItemRefs method to get the work items
const workItemRefs = await gitApi.getPullRequestWorkItemRefs(repositoryName, pullRequestId, projectName);
// Verify that work items are linked
expect(workItemRefs).toBeDefined();
expect(Array.isArray(workItemRefs)).toBe(true);
// Check if our work item is in the list
const hasWorkItem = workItemRefs.some((ref) => ref.id !== undefined && Number(ref.id) === workItemId);
expect(hasWorkItem).toBe(true);
}, 60000); // 60 second timeout for integration test
test('should remove work item links from pull request', async () => {
// Skip if no work items were found
if ((0, test_helpers_1.shouldSkipIntegrationTest)()) {
console.log('Skipping test due to missing connection or work item');
return;
}
// First ensure the work item is linked
try {
await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
addWorkItemIds: [workItemId],
});
// Add a delay to allow Azure DevOps to process the work item link
await new Promise((resolve) => setTimeout(resolve, 3000));
}
catch (error) {
// If there's an error adding the link, that's okay
console.log("Error adding work item (already be linked so that's 👍):", error instanceof Error ? error.message : String(error));
}
// Then remove the work item link
const result = await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
removeWorkItemIds: [workItemId],
});
// Verify the update was successful
expect(result).toBeDefined();
expect(result.pullRequestId).toBe(pullRequestId);
// Get the pull request work items using the proper API
const gitApi = await connection.getGitApi();
// Add a delay to allow Azure DevOps to process the work item unlink
await new Promise((resolve) => setTimeout(resolve, 5000));
// Use the getPullRequestWorkItemRefs method to get the work items
const workItemRefs = await gitApi.getPullRequestWorkItemRefs(repositoryName, pullRequestId, projectName);
// Verify that work items are properly unlinked
expect(workItemRefs).toBeDefined();
expect(Array.isArray(workItemRefs)).toBe(true);
// Check if our work item is not in the list
const hasWorkItem = workItemRefs.some((ref) => ref.id !== undefined && Number(ref.id) === workItemId);
expect(hasWorkItem).toBe(false);
}, 60000); // 60 second timeout for integration test
test('should add reviewers to pull request', async () => {
// Skip if integration tests should be skipped
if ((0, test_helpers_1.shouldSkipIntegrationTest)() || !connection) {
console.log('Skipping test due to missing connection');
return;
}
// Find an actual user in the organization to use as a reviewer
const gitApi = await connection.getGitApi();
// Get the pull request creator as a reviewer (they always exist)
const pullRequest = await gitApi.getPullRequestById(pullRequestId, projectName);
// Use the pull request creator's ID as the reviewer
const reviewer = pullRequest.createdBy.id;
// Add the reviewer
const result = await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
addReviewers: [reviewer],
});
// Verify the update was successful
expect(result).toBeDefined();
expect(result.pullRequestId).toBe(pullRequestId);
// Add a delay to allow Azure DevOps to process the reviewer addition
await new Promise((resolve) => setTimeout(resolve, 1000));
const reviewers = await gitApi.getPullRequestReviewers(repositoryName, pullRequestId, projectName);
// Verify that the reviewer was added
expect(reviewers).toBeDefined();
expect(Array.isArray(reviewers)).toBe(true);
// Check if our reviewer is in the list by ID
const hasReviewer = reviewers.some((r) => r.id === reviewer);
expect(hasReviewer).toBe(true);
}, 60000); // 60 second timeout for integration test
test('should remove reviewers from pull request', async () => {
// Skip if integration tests should be skipped
if ((0, test_helpers_1.shouldSkipIntegrationTest)() || !connection) {
console.log('Skipping test due to missing connection');
return;
}
// Find an actual user in the organization to use as a reviewer
const gitApi = await connection.getGitApi();
// Get the pull request creator as a reviewer (they always exist)
const pullRequest = await gitApi.getPullRequestById(pullRequestId, projectName);
if (!pullRequest || !pullRequest.createdBy || !pullRequest.createdBy.id) {
throw new Error('Could not determine pull request creator');
}
// Use the pull request creator's ID as the reviewer
const reviewer = pullRequest.createdBy.id;
// First ensure the reviewer is added
try {
await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
addReviewers: [reviewer],
});
// Add a delay to allow Azure DevOps to process the reviewer addition
await new Promise((resolve) => setTimeout(resolve, 3000));
}
catch (error) {
// If there's an error adding the reviewer, that's okay
console.log('Error adding reviewer (might already be added):', error instanceof Error ? error.message : String(error));
}
// Then remove the reviewer
const result = await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
removeReviewers: [reviewer],
});
// Verify the update was successful
expect(result).toBeDefined();
expect(result.pullRequestId).toBe(pullRequestId);
// Add a delay to allow Azure DevOps to process the reviewer removal
await new Promise((resolve) => setTimeout(resolve, 3000));
const reviewers = await gitApi.getPullRequestReviewers(repositoryName, pullRequestId, projectName);
// Verify that the reviewer was removed
expect(reviewers).toBeDefined();
expect(Array.isArray(reviewers)).toBe(true);
// Check if our reviewer is not in the list
const hasReviewer = reviewers.some((r) => r.id === reviewer);
expect(hasReviewer).toBe(false);
}, 60000); // 60 second timeout for integration test
test('should update pull request with additional properties', async () => {
// Skip if integration tests should be skipped
if ((0, test_helpers_1.shouldSkipIntegrationTest)() || !connection) {
console.log('Skipping test due to missing connection');
return;
}
// Use a custom property that Azure DevOps supports
const customProperty = 'autoComplete';
const customValue = true;
const result = await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
additionalProperties: {
[customProperty]: customValue,
},
});
// Verify the update was successful
expect(result).toBeDefined();
expect(result.pullRequestId).toBe(pullRequestId);
// For autoComplete specifically, we can check if it's in the response
if (customProperty in result) {
expect(result[customProperty]).toBe(customValue);
}
}, 30000); // 30 second timeout for integration test
test('should update pull request status to abandoned', async () => {
// Skip if integration tests should be skipped
if ((0, test_helpers_1.shouldSkipIntegrationTest)() || !connection) {
console.log('Skipping test due to missing connection');
return;
}
// Abandon the pull request instead of completing it
// Completing requires additional setup that's complex for integration tests
const result = await (0, feature_1.updatePullRequest)({
projectId: projectName,
repositoryId: repositoryName,
pullRequestId,
status: 'abandoned',
});
// Verify the update was successful
expect(result).toBeDefined();
expect(result.pullRequestId).toBe(pullRequestId);
expect(result.status).toBe(2); // 2 = Abandoned
}, 30000); // 30 second timeout for integration test
});
//# sourceMappingURL=feature.spec.int.js.map