Skip to main content
Glama
deepsource-validation.test.ts10.6 kB
/** * @vitest-environment node */ import { vi } from 'vitest'; import { DeepSourceClient } from '../deepsource.js'; // Create a test subclass to expose private methods class TestableDeepSourceClient extends DeepSourceClient { static testValidateProjectKey(projectKey: string): void { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.validateProjectKey(projectKey); } static testIsValidVulnerabilityNode(node: unknown): boolean { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.isValidVulnerabilityNode(node); } static testValidateProjectRepository(project: Record<string, unknown>, projectKey: string): void { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.validateProjectRepository(project, projectKey); } static testValidateNumber(value: unknown): number | null { // @ts-expect-error - Accessing private method for testing return DeepSourceClient.validateNumber(value); } } // We need to access private methods for testing // @ts-expect-error - Accessing private static method for testing const isValidVulnerabilityNode = DeepSourceClient['isValidVulnerabilityNode']; describe('DeepSourceClient validation methods', () => { // Create a spy for the logger const originalLogger = DeepSourceClient['logger']; const mockWarn = vi.fn(); beforeEach(() => { // Mock the logger's warn method DeepSourceClient['logger'] = { warn: mockWarn, // Add other required methods to satisfy TypeScript debug: vi.fn(), info: vi.fn(), error: vi.fn(), }; vi.clearAllMocks(); }); afterAll(() => { // Restore the original logger DeepSourceClient['logger'] = originalLogger; }); describe('isValidVulnerabilityNode', () => { it('should return false for null or undefined', () => { expect(isValidVulnerabilityNode(null)).toBe(false); expect(isValidVulnerabilityNode(undefined)).toBe(false); expect(mockWarn).toHaveBeenCalledTimes(2); }); it('should return false for non-object values', () => { expect(isValidVulnerabilityNode('string')).toBe(false); expect(isValidVulnerabilityNode(123)).toBe(false); expect(isValidVulnerabilityNode(true)).toBe(false); expect(mockWarn).toHaveBeenCalledTimes(3); }); it('should return false for object without an id', () => { const node = { someField: 'value' }; expect(isValidVulnerabilityNode(node)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability node with missing or invalid ID', node ); }); it('should return false for object with non-string id', () => { const node = { id: 123 }; expect(isValidVulnerabilityNode(node)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability node with missing or invalid ID', node ); }); it('should return false when package is missing or not an object', () => { const node = { id: 'vuln1' }; expect(isValidVulnerabilityNode(node)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability node with missing or invalid package', node ); const nodeWithNonObjectPackage = { id: 'vuln1', package: 'not-an-object' }; expect(isValidVulnerabilityNode(nodeWithNonObjectPackage)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability node with missing or invalid package', nodeWithNonObjectPackage ); }); it('should return false when packageVersion is missing or not an object', () => { const node = { id: 'vuln1', package: {} }; expect(isValidVulnerabilityNode(node)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability node with missing or invalid packageVersion', node ); const nodeWithNonObjectPackageVersion = { id: 'vuln1', package: {}, packageVersion: 'not-an-object', }; expect(isValidVulnerabilityNode(nodeWithNonObjectPackageVersion)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability node with missing or invalid packageVersion', nodeWithNonObjectPackageVersion ); }); it('should return false when vulnerability is missing or not an object', () => { const node = { id: 'vuln1', package: {}, packageVersion: {} }; expect(isValidVulnerabilityNode(node)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability node with missing or invalid vulnerability', node ); const nodeWithNonObjectVulnerability = { id: 'vuln1', package: {}, packageVersion: {}, vulnerability: 'not-an-object', }; expect(isValidVulnerabilityNode(nodeWithNonObjectVulnerability)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability node with missing or invalid vulnerability', nodeWithNonObjectVulnerability ); }); it('should return false when package is missing required fields', () => { const node = { id: 'vuln1', package: { missing: 'required fields' }, packageVersion: {}, vulnerability: {}, }; expect(isValidVulnerabilityNode(node)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability with incomplete package information', node.package ); }); it('should return false when packageVersion is missing required fields', () => { const node = { id: 'vuln1', package: { id: 'pkg1', ecosystem: 'npm', name: 'express' }, packageVersion: { missing: 'required fields' }, vulnerability: {}, }; expect(isValidVulnerabilityNode(node)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability with incomplete package version information', node.packageVersion ); }); it('should return false when vulnerability is missing required fields', () => { const node = { id: 'vuln1', package: { id: 'pkg1', ecosystem: 'npm', name: 'express' }, packageVersion: { id: 'ver1', version: '1.0.0' }, vulnerability: { missing: 'required fields' }, }; expect(isValidVulnerabilityNode(node)).toBe(false); expect(mockWarn).toHaveBeenCalledWith( 'Skipping vulnerability with incomplete vulnerability information', node.vulnerability ); }); it('should return true for valid vulnerability node', () => { const validNode = { id: 'vuln1', package: { id: 'pkg1', ecosystem: 'npm', name: 'express' }, packageVersion: { id: 'ver1', version: '1.0.0' }, vulnerability: { id: 'cve1', identifier: 'CVE-2022-1234' }, }; expect(isValidVulnerabilityNode(validNode)).toBe(true); expect(mockWarn).not.toHaveBeenCalled(); }); }); describe('validateProjectKey', () => { it('should throw an error for null or undefined project keys', () => { expect(() => { // @ts-expect-error - Testing with invalid type TestableDeepSourceClient.testValidateProjectKey(null); }).toThrow('Invalid project key: Project key must be a non-empty string'); expect(() => { // @ts-expect-error - Testing with invalid type TestableDeepSourceClient.testValidateProjectKey(undefined); }).toThrow('Invalid project key: Project key must be a non-empty string'); }); it('should throw an error for non-string project keys', () => { expect(() => { // @ts-expect-error - Testing with invalid type TestableDeepSourceClient.testValidateProjectKey(123); }).toThrow('Invalid project key: Project key must be a non-empty string'); expect(() => { // @ts-expect-error - Testing with invalid type TestableDeepSourceClient.testValidateProjectKey({}); }).toThrow('Invalid project key: Project key must be a non-empty string'); expect(() => { // @ts-expect-error - Testing with invalid type TestableDeepSourceClient.testValidateProjectKey(true); }).toThrow('Invalid project key: Project key must be a non-empty string'); }); it('should throw an error for empty string project keys', () => { expect(() => { TestableDeepSourceClient.testValidateProjectKey(''); }).toThrow('Invalid project key: Project key must be a non-empty string'); }); it('should not throw for valid project keys', () => { expect(() => { TestableDeepSourceClient.testValidateProjectKey('valid-project-key'); }).not.toThrow(); expect(() => { TestableDeepSourceClient.testValidateProjectKey('12345abcdef'); }).not.toThrow(); }); }); describe('validateNumber', () => { it('should validate and return number values', () => { expect(TestableDeepSourceClient.testValidateNumber(42)).toBe(42); expect(TestableDeepSourceClient.testValidateNumber(0)).toBe(0); expect(TestableDeepSourceClient.testValidateNumber(-1)).toBe(-1); expect(TestableDeepSourceClient.testValidateNumber(3.14)).toBe(3.14); }); 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(); }); }); describe('validateProjectRepository', () => { it('should not throw for valid project data', () => { const project = { name: 'Test Project', repository: { login: 'test-org', provider: 'github', }, }; expect(() => { TestableDeepSourceClient.testValidateProjectRepository(project, 'test-key'); }).not.toThrow(); }); it('should throw for invalid project data', () => { const invalidProject = { name: 'Test Project', // Missing repository property }; expect(() => { TestableDeepSourceClient.testValidateProjectRepository(invalidProject, 'test-key'); }).toThrow(); }); }); });

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