Skip to main content
Glama

JIRA MCP Server

issue-operations.integration.test.ts11.2 kB
/** * Integration tests for Issue Operations (Create/Update) * Tests the complete flow from permission checking to actual issue operations */ import { describe, test, expect, beforeAll } from "bun:test"; import { JiraHttpClient } from "@features/jira/client/http"; import { ProjectPermissionRepositoryImpl } from "@features/jira/projects/repositories/project-permission.repository"; import { IssueRepositoryImpl } from "@features/jira/issues/repositories/issue.repository"; import { CreateIssueUseCaseImpl } from "@features/jira/issues/use-cases/create-issue.use-case"; import { UpdateIssueUseCaseImpl } from "@features/jira/issues/use-cases/update-issue.use-case"; import { ProjectValidatorImpl } from "@features/jira/projects/validators/project.validator"; import { IssueTransitionRepositoryImpl } from "@features/jira/issues/repositories/issue-transition.repository"; import { WorklogRepositoryImpl } from "@features/jira/issues/repositories/worklog.repository"; import { JiraConfigService } from "@features/jira/client/config"; import type { Issue } from "@features/jira/issues/models/issue.models"; import { hasJiraCredentials, getJiraCredentialsSkipReason, } from "../utils/jira-credentials"; describe("Issue Operations Integration Tests", () => { let httpClient: JiraHttpClient; let permissionRepository: ProjectPermissionRepositoryImpl; let issueRepository: IssueRepositoryImpl; let createIssueUseCase: CreateIssueUseCaseImpl; let updateIssueUseCase: UpdateIssueUseCaseImpl; let createdIssueKey: string | null = null; // Test configuration - these should be set via environment variables const testConfig = { hostUrl: process.env.JIRA_HOST || "https://example.atlassian.net", username: process.env.JIRA_USERNAME || "test@example.com", apiToken: process.env.JIRA_API_TOKEN || "test-token", }; beforeAll(() => { if (!hasJiraCredentials()) { console.log( "⚠️ Skipping integration tests - no JIRA credentials provided", ); console.log(` Reason: ${getJiraCredentialsSkipReason()}`); console.log( " Set JIRA_HOST, JIRA_USERNAME, and JIRA_API_TOKEN to run integration tests", ); return; } // Initialize dependencies only when credentials are available const configService = new JiraConfigService(testConfig); httpClient = new JiraHttpClient(configService); permissionRepository = new ProjectPermissionRepositoryImpl(httpClient); issueRepository = new IssueRepositoryImpl(httpClient); // Initialize use cases with all required dependencies const projectValidator = new ProjectValidatorImpl(httpClient); const transitionRepository = new IssueTransitionRepositoryImpl(httpClient); const worklogRepository = new WorklogRepositoryImpl(httpClient); createIssueUseCase = new CreateIssueUseCaseImpl( issueRepository, projectValidator, permissionRepository, ); updateIssueUseCase = new UpdateIssueUseCaseImpl( issueRepository, transitionRepository, worklogRepository, permissionRepository, ); }); describe("Permission Validation Flow", () => { test("should validate CREATE_ISSUES permission before creating issue", async () => { if (!hasJiraCredentials()) { console.log("⚠️ Skipping test - no JIRA credentials provided"); return; } console.log("🔍 Testing CREATE_ISSUES permission validation..."); try { const hasPermission = await permissionRepository.hasCreateIssuePermission("SEC"); console.log( `CREATE_ISSUES permission: ${hasPermission ? "✅ GRANTED" : "❌ DENIED"}`, ); expect(typeof hasPermission).toBe("boolean"); if (hasPermission) { console.log( "✅ Permission validation working correctly for CREATE_ISSUES", ); } else { console.warn( "⚠️ CREATE_ISSUES permission denied - check if this is expected", ); } } catch (error) { console.error("❌ Permission validation failed:", error); throw error; } }); test("should validate EDIT_ISSUES permission before updating issue", async () => { if (!hasJiraCredentials()) { console.log("⚠️ Skipping test - no JIRA credentials provided"); return; } console.log("🔍 Testing EDIT_ISSUES permission validation..."); try { const hasPermission = await permissionRepository.hasEditIssuePermission("SEC"); console.log( `EDIT_ISSUES permission: ${hasPermission ? "✅ GRANTED" : "❌ DENIED"}`, ); expect(typeof hasPermission).toBe("boolean"); if (hasPermission) { console.log( "✅ Permission validation working correctly for EDIT_ISSUES", ); } else { console.warn( "⚠️ EDIT_ISSUES permission denied - check if this is expected", ); } } catch (error) { console.error("❌ Permission validation failed:", error); throw error; } }); }); describe("Issue Creation Flow", () => { test("should create issue in SEC project without permission errors", async () => { if (!hasJiraCredentials()) { console.log("⚠️ Skipping test - no JIRA credentials provided"); return; } await testIssueCreation(); }); }); async function testIssueCreation(): Promise<void> { console.log("🔍 Testing issue creation in SEC project..."); try { const createRequest = { projectKey: "SEC", summary: `Integration Test Issue - ${new Date().toISOString()}`, description: "This is a test issue created by integration tests", issueType: "Task", }; const createdIssue = await createIssueUseCase.execute(createRequest); validateCreatedIssue(createdIssue); logCreationSuccess(createdIssue); } catch (error) { handleCreationError(error); throw error; } } function validateCreatedIssue(createdIssue: Issue): void { expect(createdIssue).toBeDefined(); expect(createdIssue.key).toBeDefined(); expect(createdIssue.key).toMatch(/^SEC-\d+$/); expect(createdIssue.fields?.project?.key).toBe("SEC"); expect(createdIssue.fields?.summary).toContain("Integration Test Issue"); // Store for cleanup createdIssueKey = createdIssue.key; } function logCreationSuccess(createdIssue: Issue): void { console.log(`✅ Successfully created issue: ${createdIssue.key}`); console.log("📋 Issue details:", { key: createdIssue.key, summary: createdIssue.fields?.summary, project: createdIssue.fields?.project?.key, status: createdIssue.fields?.status?.name, }); } function handleCreationError(error: unknown): void { console.error("❌ Issue creation failed:", error); if (error instanceof Error) { if (error.message.includes("permission")) { console.error( "🚨 PERMISSION ERROR: This suggests the fix didn't work!", ); } else if (error.message.includes("404")) { console.error("🚨 PROJECT NOT FOUND: SEC project may not exist"); } } } describe("Issue Update Flow", () => { test("should update created issue without permission errors", async () => { if (!hasJiraCredentials()) { console.log("⚠️ Skipping test - no JIRA credentials provided"); return; } await testIssueUpdate(); }); }); async function testIssueUpdate(): Promise<void> { if (!createdIssueKey) { console.log("⚠️ Skipping update test - no issue was created"); return; } console.log(`🔍 Testing issue update for ${createdIssueKey}...`); try { const updateRequest = { issueKey: createdIssueKey, fields: { summary: `Updated Integration Test Issue - ${new Date().toISOString()}`, description: "This issue has been updated by integration tests", }, }; const updatedIssue = await updateIssueUseCase.execute(updateRequest); validateUpdatedIssue(updatedIssue); logUpdateSuccess(updatedIssue); } catch (error) { handleUpdateError(error); throw error; } } function validateUpdatedIssue(updatedIssue: Issue): void { expect(updatedIssue).toBeDefined(); expect(createdIssueKey).toBeDefined(); expect(updatedIssue.key).toBe(createdIssueKey as string); expect(updatedIssue.fields?.summary).toContain( "Updated Integration Test Issue", ); } function logUpdateSuccess(updatedIssue: Issue): void { console.log(`✅ Successfully updated issue: ${updatedIssue.key}`); console.log("📋 Updated issue details:", { key: updatedIssue.key, summary: updatedIssue.fields?.summary, project: updatedIssue.fields?.project?.key, status: updatedIssue.fields?.status?.name, }); } function handleUpdateError(error: unknown): void { console.error("❌ Issue update failed:", error); if (error instanceof Error) { if (error.message.includes("permission")) { console.error( "🚨 PERMISSION ERROR: This suggests the fix didn't work!", ); } else if (error.message.includes("404")) { console.error("🚨 ISSUE NOT FOUND: Issue may have been deleted"); } } } describe("Error Handling", () => { test("should handle non-existent project gracefully", async () => { if (!hasJiraCredentials()) { console.log("⚠️ Skipping test - no JIRA credentials provided"); return; } console.log("🔍 Testing error handling for non-existent project..."); try { const createRequest = { projectKey: "NONEXISTENT", summary: "Test issue for non-existent project", description: "This should fail", issueType: "Task", }; await createIssueUseCase.execute(createRequest); // If we get here, something is wrong throw new Error( "Expected error for non-existent project, but operation succeeded", ); } catch (error) { console.log("✅ Correctly handled non-existent project error"); expect(error).toBeDefined(); if (error instanceof Error) { console.log(`Error message: ${error.message}`); expect(error.message).toMatch(/project|not found|permission/i); } } }); }); }); /** * Test runner instructions: * * To run these integration tests with real JIRA credentials: * * 1. Set environment variables: * export JIRA_HOST="https://your-domain.atlassian.net" * export JIRA_USERNAME="your-email@domain.com" * export JIRA_API_TOKEN="your-api-token" * * 2. Run integration tests: * bun run test:integration * * Without credentials, these tests will be automatically skipped. * * Note: These tests will create and update real issues in your JIRA instance. * Make sure you have appropriate permissions and are testing in a safe environment. */

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/Dsazz/mcp-jira'

If you have feedback or need assistance with the MCP directory API, please join our Discord server