Skip to main content
Glama
deepsource-internal-utils.test.ts22.7 kB
import { DeepSourceClient } from '../deepsource'; import { vi } from 'vitest'; // Import types from metrics.ts import { MetricDirection } from '../types/metrics'; // We need to access private static methods, so we'll create a way to access them type MetricHistoryValue = { value: number; valueDisplay: string; threshold: number | null; thresholdStatus: string; commitOid: string; createdAt: string; }; // Create a test subclass to expose private methods class TestableDeepSourceClient extends DeepSourceClient { static testCalculateTrendDirection( values: MetricHistoryValue[], positiveDirection: string | MetricDirection ): boolean { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.calculateTrendDirection(values, positiveDirection); } static testGetReportField(reportType: string): string { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.getReportField(reportType); } static testIsAxiosErrorWithCriteria( error: unknown, statusCode?: number, errorCode?: string ): boolean { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.isAxiosErrorWithCriteria(error, statusCode, errorCode); } static testHandleNetworkError(error: unknown): never | false { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.handleNetworkError(error); } static testHandleHttpStatusError(error: unknown): never | false { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.handleHttpStatusError(error); } static testHandleGraphQLError(error: unknown): never { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.handleGraphQLError(error); } static testHandleGraphQLSpecificError(error: unknown): never | false { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.handleGraphQLSpecificError(error); } static testValidateNumber(value: unknown): number | null { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.validateNumber(value); } } describe('DeepSource Internal Utilities', () => { describe('calculateTrendDirection', () => { it('should return true when there are fewer than 2 values', () => { const values: MetricHistoryValue[] = [ { value: 85.5, valueDisplay: '85.5%', threshold: 80, thresholdStatus: 'PASSING', commitOid: 'commit1', createdAt: '2023-01-01T12:00:00Z', }, ]; const result = TestableDeepSourceClient.testCalculateTrendDirection(values, 'UPWARD'); expect(result).toBe(true); }); it('should calculate positive trend for UPWARD direction when value increases', () => { const values: MetricHistoryValue[] = [ { value: 75.2, valueDisplay: '75.2%', threshold: 80, thresholdStatus: 'FAILING', commitOid: 'commit1', createdAt: '2023-01-01T12:00:00Z', }, { value: 85.5, valueDisplay: '85.5%', threshold: 80, thresholdStatus: 'PASSING', commitOid: 'commit2', createdAt: '2023-01-15T12:00:00Z', }, ]; const result = TestableDeepSourceClient.testCalculateTrendDirection(values, 'UPWARD'); expect(result).toBe(true); }); it('should calculate negative trend for UPWARD direction when value decreases', () => { const values: MetricHistoryValue[] = [ { value: 85.5, valueDisplay: '85.5%', threshold: 80, thresholdStatus: 'PASSING', commitOid: 'commit1', createdAt: '2023-01-01T12:00:00Z', }, { value: 75.2, valueDisplay: '75.2%', threshold: 80, thresholdStatus: 'FAILING', commitOid: 'commit2', createdAt: '2023-01-15T12:00:00Z', }, ]; const result = TestableDeepSourceClient.testCalculateTrendDirection(values, 'UPWARD'); expect(result).toBe(false); }); it('should calculate positive trend for DOWNWARD direction when value decreases', () => { const values: MetricHistoryValue[] = [ { value: 12.4, valueDisplay: '12.4%', threshold: 10, thresholdStatus: 'FAILING', commitOid: 'commit1', createdAt: '2023-01-01T12:00:00Z', }, { value: 8.1, valueDisplay: '8.1%', threshold: 10, thresholdStatus: 'PASSING', commitOid: 'commit2', createdAt: '2023-01-15T12:00:00Z', }, ]; const result = TestableDeepSourceClient.testCalculateTrendDirection(values, 'DOWNWARD'); expect(result).toBe(true); }); it('should calculate negative trend for DOWNWARD direction when value increases', () => { const values: MetricHistoryValue[] = [ { value: 8.1, valueDisplay: '8.1%', threshold: 10, thresholdStatus: 'PASSING', commitOid: 'commit1', createdAt: '2023-01-01T12:00:00Z', }, { value: 12.4, valueDisplay: '12.4%', threshold: 10, thresholdStatus: 'FAILING', commitOid: 'commit2', createdAt: '2023-01-15T12:00:00Z', }, ]; const result = TestableDeepSourceClient.testCalculateTrendDirection(values, 'DOWNWARD'); expect(result).toBe(false); }); it('should handle string values for positiveDirection', () => { const values: MetricHistoryValue[] = [ { value: 75.2, valueDisplay: '75.2%', threshold: 80, thresholdStatus: 'FAILING', commitOid: 'commit1', createdAt: '2023-01-01T12:00:00Z', }, { value: 85.5, valueDisplay: '85.5%', threshold: 80, thresholdStatus: 'PASSING', commitOid: 'commit2', createdAt: '2023-01-15T12:00:00Z', }, ]; // Test with string value 'UPWARD' const resultUpward = TestableDeepSourceClient.testCalculateTrendDirection(values, 'UPWARD'); expect(resultUpward).toBe(true); // Test with string value 'DOWNWARD' const resultDownward = TestableDeepSourceClient.testCalculateTrendDirection( values, 'DOWNWARD' ); expect(resultDownward).toBe(false); }); it('should handle no change in value', () => { const values: MetricHistoryValue[] = [ { value: 85.5, valueDisplay: '85.5%', threshold: 80, thresholdStatus: 'PASSING', commitOid: 'commit1', createdAt: '2023-01-01T12:00:00Z', }, { value: 85.5, valueDisplay: '85.5%', threshold: 80, thresholdStatus: 'PASSING', commitOid: 'commit2', createdAt: '2023-01-15T12:00:00Z', }, ]; // For UPWARD, no change should be considered positive const resultUpward = TestableDeepSourceClient.testCalculateTrendDirection(values, 'UPWARD'); expect(resultUpward).toBe(true); // For DOWNWARD, no change should be considered positive const resultDownward = TestableDeepSourceClient.testCalculateTrendDirection( values, 'DOWNWARD' ); expect(resultDownward).toBe(true); }); }); describe('getReportField', () => { it('should return the correct field name for OWASP_TOP_10', () => { const result = TestableDeepSourceClient.testGetReportField('OWASP_TOP_10'); expect(result).toBe('owaspTop10'); }); it('should return the correct field name for SANS_TOP_25', () => { const result = TestableDeepSourceClient.testGetReportField('SANS_TOP_25'); expect(result).toBe('sansTop25'); }); it('should return the correct field name for MISRA_C', () => { const result = TestableDeepSourceClient.testGetReportField('MISRA_C'); expect(result).toBe('misraC'); }); it('should return the correct field name for other report types', () => { const result = TestableDeepSourceClient.testGetReportField('CODE_COVERAGE'); expect(result).toBe('codeCoverage'); }); }); describe('isAxiosErrorWithCriteria', () => { it('should return false for null error', () => { const result = TestableDeepSourceClient.testIsAxiosErrorWithCriteria(null); expect(result).toBe(false); }); it('should return false for undefined error', () => { const result = TestableDeepSourceClient.testIsAxiosErrorWithCriteria(undefined); expect(result).toBe(false); }); it('should return false for non-object errors', () => { expect(TestableDeepSourceClient.testIsAxiosErrorWithCriteria('string error')).toBe(false); expect(TestableDeepSourceClient.testIsAxiosErrorWithCriteria(123)).toBe(false); expect(TestableDeepSourceClient.testIsAxiosErrorWithCriteria(true)).toBe(false); }); it('should return false for empty object', () => { const result = TestableDeepSourceClient.testIsAxiosErrorWithCriteria({}); expect(result).toBe(false); }); it('should return true for axios error with isAxiosError property', () => { const axiosError = { isAxiosError: true, response: { status: 400, data: {}, }, code: 'ECONNREFUSED', }; const result = TestableDeepSourceClient.testIsAxiosErrorWithCriteria(axiosError); expect(result).toBe(true); }); it('should filter by status code when provided', () => { const axiosError = { isAxiosError: true, response: { status: 404, data: {}, }, }; expect(TestableDeepSourceClient.testIsAxiosErrorWithCriteria(axiosError, 404)).toBe(true); expect(TestableDeepSourceClient.testIsAxiosErrorWithCriteria(axiosError, 500)).toBe(false); }); it('should filter by error code when provided', () => { const axiosError = { isAxiosError: true, code: 'ECONNREFUSED', }; expect( TestableDeepSourceClient.testIsAxiosErrorWithCriteria(axiosError, undefined, 'ECONNREFUSED') ).toBe(true); expect( TestableDeepSourceClient.testIsAxiosErrorWithCriteria(axiosError, undefined, 'TIMEOUT') ).toBe(false); }); it('should filter by both status code and error code when provided', () => { const axiosError = { isAxiosError: true, response: { status: 400, data: {}, }, code: 'ECONNREFUSED', }; expect( TestableDeepSourceClient.testIsAxiosErrorWithCriteria(axiosError, 400, 'ECONNREFUSED') ).toBe(true); expect( TestableDeepSourceClient.testIsAxiosErrorWithCriteria(axiosError, 400, 'TIMEOUT') ).toBe(false); expect( TestableDeepSourceClient.testIsAxiosErrorWithCriteria(axiosError, 500, 'ECONNREFUSED') ).toBe(false); }); }); describe('handleNetworkError', () => { it('should throw connection error for ECONNREFUSED', () => { const axiosError = { isAxiosError: true, code: 'ECONNREFUSED', message: 'connect ECONNREFUSED', }; expect(() => TestableDeepSourceClient.testHandleNetworkError(axiosError)).toThrow( 'Connection error: Unable to connect to DeepSource API' ); }); it('should throw timeout error for ETIMEDOUT', () => { const axiosError = { isAxiosError: true, code: 'ETIMEDOUT', message: 'connect ETIMEDOUT', }; expect(() => TestableDeepSourceClient.testHandleNetworkError(axiosError)).toThrow( 'Timeout error: DeepSource API request timed out' ); }); it('should return false for non-network errors', () => { const axiosError = { isAxiosError: true, code: 'ENOTFOUND', message: 'connect ENOTFOUND', }; const result = TestableDeepSourceClient.testHandleNetworkError(axiosError); expect(result).toBe(false); }); it('should return false for non-axios errors', () => { const regularError = new Error('Regular error'); const result = TestableDeepSourceClient.testHandleNetworkError(regularError); expect(result).toBe(false); }); it('should return false for null error', () => { const result = TestableDeepSourceClient.testHandleNetworkError(null); expect(result).toBe(false); }); }); describe('handleHttpStatusError', () => { it('should throw authentication error for 401', () => { const axiosError = { isAxiosError: true, response: { status: 401, data: {}, }, }; expect(() => TestableDeepSourceClient.testHandleHttpStatusError(axiosError)).toThrow( 'Authentication error: Invalid or expired API key' ); }); it('should throw rate limit error for 429', () => { const axiosError = { isAxiosError: true, response: { status: 429, data: {}, }, }; expect(() => TestableDeepSourceClient.testHandleHttpStatusError(axiosError)).toThrow( 'Rate limit exceeded: Too many requests to DeepSource API' ); }); it('should throw not found error for 404', () => { const axiosError = { isAxiosError: true, response: { status: 404, data: {}, }, }; expect(() => TestableDeepSourceClient.testHandleHttpStatusError(axiosError)).toThrow( 'Not found (404): The requested resource was not found' ); }); it('should throw server error for 500+', () => { const axiosError = { isAxiosError: true, response: { status: 500, data: {}, }, }; expect(() => TestableDeepSourceClient.testHandleHttpStatusError(axiosError)).toThrow( 'Server error (500): DeepSource API server error' ); const error503 = { isAxiosError: true, response: { status: 503, data: {}, }, }; expect(() => TestableDeepSourceClient.testHandleHttpStatusError(error503)).toThrow( 'Server error (503): DeepSource API server error' ); }); it('should throw client error for 400-499 range', () => { const axiosError = { isAxiosError: true, response: { status: 403, data: {}, statusText: 'Forbidden', }, }; expect(() => TestableDeepSourceClient.testHandleHttpStatusError(axiosError)).toThrow( 'Client error (403): Forbidden' ); }); it('should return false for non-http-status errors', () => { const axiosError = { isAxiosError: true, code: 'ENOTFOUND', }; const result = TestableDeepSourceClient.testHandleHttpStatusError(axiosError); expect(result).toBe(false); }); it('should return false for non-axios errors', () => { const regularError = new Error('Regular error'); const result = TestableDeepSourceClient.testHandleHttpStatusError(regularError); expect(result).toBe(false); }); }); describe('handleGraphQLError', () => { // Mock implementations for sub-handlers to simulate different behaviors let originalGraphQLHandler: (_error: unknown, _retryCount: number) => never; let originalNetworkHandler: (_error: unknown, _retryCount: number) => never; let originalHttpStatusHandler: (_error: unknown, _retryCount: number) => never; beforeEach(() => { // Store original handlers // @ts-expect-error - Accessing private static method for testing originalGraphQLHandler = DeepSourceClient.handleGraphQLSpecificError; // @ts-expect-error - Accessing private static method for testing originalNetworkHandler = DeepSourceClient.handleNetworkError; // @ts-expect-error - Accessing private static method for testing originalHttpStatusHandler = DeepSourceClient.handleHttpStatusError; }); afterEach(() => { // Restore original handlers // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleGraphQLSpecificError = originalGraphQLHandler; // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleNetworkError = originalNetworkHandler; // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleHttpStatusError = originalHttpStatusHandler; }); it('should throw error for GraphQL specific error', () => { // Create a situation where GraphQL handler returns true instead of throwing // This would trigger the unreachable code at line 811 // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleGraphQLSpecificError = vi.fn().mockReturnValue(true); const error = new Error('Test GraphQL error'); expect(() => TestableDeepSourceClient.testHandleGraphQLError(error)).toThrow( 'Unreachable code - handleGraphQLSpecificError should have thrown' ); }); it('should throw error for network error', () => { // Create a situation where GraphQL handler returns false (no error handled) // but Network handler returns true instead of throwing // This would trigger the unreachable code at line 815 // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleGraphQLSpecificError = vi.fn().mockReturnValue(false); // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleNetworkError = vi.fn().mockReturnValue(true); const error = new Error('Test network error'); expect(() => TestableDeepSourceClient.testHandleGraphQLError(error)).toThrow( 'Unreachable code - handleNetworkError should have thrown' ); }); it('should throw error for HTTP status error', () => { // Create a situation where GraphQL and Network handlers return false // but HTTP Status handler returns true instead of throwing // This would trigger the unreachable code at line 819 // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleGraphQLSpecificError = vi.fn().mockReturnValue(false); // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleNetworkError = vi.fn().mockReturnValue(false); // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleHttpStatusError = vi.fn().mockReturnValue(true); const error = new Error('Test HTTP status error'); expect(() => TestableDeepSourceClient.testHandleGraphQLError(error)).toThrow( 'Unreachable code - handleHttpStatusError should have thrown' ); }); it('should throw classified error for standard Error objects', () => { // All handlers return false, but the error is a standard Error // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleGraphQLSpecificError = vi.fn().mockReturnValue(false); // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleNetworkError = vi.fn().mockReturnValue(false); // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleHttpStatusError = vi.fn().mockReturnValue(false); const error = new Error('Standard Error'); expect(() => TestableDeepSourceClient.testHandleGraphQLError(error)).toThrow( 'DeepSource API error: Standard Error' ); }); it('should throw generic error for non-Error objects', () => { // All handlers return false, and the "error" is not an Error object // This would trigger line 829 // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleGraphQLSpecificError = vi.fn().mockReturnValue(false); // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleNetworkError = vi.fn().mockReturnValue(false); // @ts-expect-error - Accessing private static method for testing DeepSourceClient.handleHttpStatusError = vi.fn().mockReturnValue(false); const nonError = {}; // A non-Error object without a message property expect(() => TestableDeepSourceClient.testHandleGraphQLError(nonError)).toThrow( 'Unknown error occurred while communicating with DeepSource API' ); }); }); describe('validateNumber', () => { it('should return the value when it is a number', () => { expect(TestableDeepSourceClient.testValidateNumber(0)).toBe(0); expect(TestableDeepSourceClient.testValidateNumber(42)).toBe(42); expect(TestableDeepSourceClient.testValidateNumber(-1.5)).toBe(-1.5); expect(TestableDeepSourceClient.testValidateNumber(Infinity)).toBe(Infinity); expect(TestableDeepSourceClient.testValidateNumber(Number.MAX_SAFE_INTEGER)).toBe( Number.MAX_SAFE_INTEGER ); }); it('should return null for non-number values', () => { expect(TestableDeepSourceClient.testValidateNumber('42')).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber(null)).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber(undefined)).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber({})).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber([])).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber(true)).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber(false)).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber(new Date())).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber(() => 42)).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber(Symbol('test'))).toBeNull(); expect(TestableDeepSourceClient.testValidateNumber(BigInt(42))).toBeNull(); }); it('should handle NaN as a special case', () => { // NaN is a number type in JavaScript but often needs special handling expect(TestableDeepSourceClient.testValidateNumber(NaN)).toBe(NaN); // Need to use isNaN to check since NaN !== NaN expect(isNaN(TestableDeepSourceClient.testValidateNumber(NaN) as number)).toBe(true); }); }); });

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