Skip to main content
Glama
deepsource-compliance-reports.test.ts10.9 kB
import nock from 'nock'; import { vi } from 'vitest'; import { DeepSourceClient, ReportType } from '../deepsource'; import { handleDeepsourceComplianceReport } from '../handlers'; describe('DeepSource Compliance Reports', () => { const API_KEY = 'test-api-key'; const API_URL = 'https://api.deepsource.io'; const PROJECT_KEY = 'test-project-key'; const LOGIN = 'test-login'; const NAME = 'test-repo'; const PROVIDER = 'GITHUB'; let client: DeepSourceClient; beforeEach(() => { // Create a fresh client instance client = new DeepSourceClient(API_KEY); // Clean up any existing nock interceptors nock.cleanAll(); // Mock the environment variable process.env.DEEPSOURCE_API_KEY = API_KEY; // Mock the listProjects request nock(API_URL) .post('/graphql/') .reply(200, { data: { viewer: { accounts: { edges: [ { node: { login: LOGIN, repositories: { edges: [ { node: { name: NAME, dsn: PROJECT_KEY, vcsProvider: PROVIDER, isPrivate: false, isActivated: true, }, }, ], }, }, }, ], }, }, }, }); }); afterEach(() => { nock.cleanAll(); delete process.env.DEEPSOURCE_API_KEY; }); describe('getComplianceReport', () => { it('should fetch OWASP Top 10 compliance report', async () => { // Mock the report request nock(API_URL) .post('/graphql/') .reply(200, { data: { repository: { name: NAME, id: 'repo-id', reports: { owaspTop10: { key: 'OWASP_TOP_10', title: 'OWASP Top 10', currentValue: 85, status: 'FAILING', securityIssueStats: [ { key: 'A1', title: 'Injection', occurrence: { critical: 2, major: 3, minor: 1, total: 6, }, }, { key: 'A2', title: 'Broken Authentication', occurrence: { critical: 1, major: 1, minor: 2, total: 4, }, }, ], trends: [ { label: 'Issues', value: 10, changePercentage: -5.2, }, ], }, }, }, }, }); // Call the method const report = await client.getComplianceReport(PROJECT_KEY, ReportType.OWASP_TOP_10); // Assertions expect(report).not.toBeNull(); if (report) { expect(report.key).toBe(ReportType.OWASP_TOP_10); expect(report.title).toBe('OWASP Top 10'); expect(report.currentValue).toBe(85); expect(report.status).toBe('FAILING'); expect(report.securityIssueStats.length).toBe(2); expect(report.securityIssueStats[0].key).toBe('A1'); expect(report.securityIssueStats[0].occurrence.critical).toBe(2); expect(report.securityIssueStats[0].occurrence.total).toBe(6); expect(report.trends?.length).toBe(1); expect(report.trends?.[0].changePercentage).toBe(-5.2); } }); it('should return null if project not found', async () => { // Mock the report request with a project not found response nock(API_URL) .post('/graphql/') .reply(200, { data: { repository: null, }, }); // Call the method const report = await client.getComplianceReport( 'non-existent-project', ReportType.OWASP_TOP_10 ); // Assert expect(report).toBeNull(); }); it('should throw an error for an invalid report type', async () => { // Call the method with an invalid report type await expect( client.getComplianceReport(PROJECT_KEY, 'INVALID_TYPE' as ReportType) ).rejects.toThrow(/Invalid report type/); }); it('should handle GraphQL errors', async () => { // Mock the report request with errors nock(API_URL) .post('/graphql/') .reply(200, { errors: [{ message: 'Test GraphQL error' }], }); // Call the method and expect it to throw await expect( client.getComplianceReport(PROJECT_KEY, ReportType.OWASP_TOP_10) ).rejects.toThrow(/GraphQL Errors/); }); it('should return null when error contains NoneType (line 2655)', async () => { // Create a custom client that will be used for testing const customClient = new DeepSourceClient(API_KEY); // Mock the validateProjectKey method to do nothing (avoid needing to mock listProjects) // We're specifically testing the error handling in the getComplianceReport method, // so we bypass this validation step // @ts-expect-error - accessing private method vi.spyOn(DeepSourceClient, 'validateProjectKey').mockImplementation(() => { // Intentionally empty - validation is not relevant to this error handling test }); // Mock the client's post method to throw a NoneType error vi.spyOn(customClient['client'], 'post').mockImplementation(() => { throw new Error('NoneType object has no attribute get'); }); // Call the actual getComplianceReport method, which should invoke the catch block with line 2655 const result = await customClient.getComplianceReport(PROJECT_KEY, ReportType.OWASP_TOP_10); // Assert the expected behavior expect(result).toBeNull(); }); it('should return null when error contains not found message (line 2655)', async () => { // Create a custom client that will be used for testing const customClient = new DeepSourceClient(API_KEY); // Mock the validateProjectKey method to do nothing (avoid needing to mock listProjects) // We're specifically testing the error handling in the getComplianceReport method, // so we bypass this validation step // @ts-expect-error - accessing private method vi.spyOn(DeepSourceClient, 'validateProjectKey').mockImplementation(() => { // Intentionally empty - validation is not relevant to this error handling test }); // Mock the client's post method to throw a not found error vi.spyOn(customClient['client'], 'post').mockImplementation(() => { throw new Error('Repository not found in database'); }); // Call the actual getComplianceReport method, which should invoke the catch block with line 2655 const result = await customClient.getComplianceReport(PROJECT_KEY, ReportType.SANS_TOP_25); // Assert the expected behavior expect(result).toBeNull(); }); }); describe('handleDeepsourceComplianceReport', () => { it.skip('should return formatted report with analysis', async () => { // Mock the report request nock(API_URL) .post('/graphql/') .reply(200, { data: { repository: { name: NAME, id: 'repo-id', reports: { sansTop25: { key: 'SANS_TOP_25', title: 'SANS Top 25', currentValue: 92, status: 'PASSING', securityIssueStats: [ { key: 'S1', title: 'Buffer Overflow', occurrence: { critical: 0, major: 1, minor: 2, total: 3, }, }, ], trends: [ { label: 'Issues', value: 3, changePercentage: -15.0, }, ], }, }, }, }, }); // Call the handler const result = await handleDeepsourceComplianceReport({ projectKey: PROJECT_KEY, reportType: ReportType.SANS_TOP_25, }); // Parse the response const response = JSON.parse(result.content[0].text); // Assertions expect(response.key).toBe(ReportType.SANS_TOP_25); expect(response.title).toBe('SANS Top 25'); expect(response.status).toBe('PASSING'); expect(response.securityIssueStats.length).toBe(1); // Verify analysis was included expect(response.analysis).toBeDefined(); expect(response.analysis.critical_issues).toBe(0); expect(response.analysis.major_issues).toBe(1); expect(response.analysis.minor_issues).toBe(2); expect(response.analysis.total_issues).toBe(3); // Verify recommendations expect(response.recommendations).toBeDefined(); expect(response.recommendations.actions).toContain('Continue monitoring security compliance'); expect(response.recommendations.resources[0]).toContain('SANS Top 25'); }); it('should throw error when API key is not set', async () => { // Remove the API key delete process.env.DEEPSOURCE_API_KEY; // Call the handler and expect it to throw await expect( handleDeepsourceComplianceReport({ projectKey: PROJECT_KEY, reportType: ReportType.MISRA_C, }) ).rejects.toThrow('Configuration error: DeepSource API key is required but not configured'); }); it.skip('should throw error when report is not found', async () => { // Mock the report request with a null report nock(API_URL) .post('/graphql/') .reply(200, { data: { repository: { reports: { misraC: null, }, }, }, }); // Call the handler and expect it to throw await expect( handleDeepsourceComplianceReport({ projectKey: PROJECT_KEY, reportType: ReportType.MISRA_C, }) ).rejects.toThrow(/Report of type.*not found/); }); }); });

Latest Blog Posts

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/sapientpants/deepsource-mcp-server'

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