UNPKG

deepsource-mcp-server

Version:
340 lines (275 loc) 10.8 kB
/** * @fileoverview Tests for ProjectRepository */ import { describe, it, expect, beforeEach } from 'vitest'; import { ProjectRepository } from '../project.repository.js'; import { ProjectsClient } from '../../../client/projects-client.js'; import { DeepSourceProject } from '../../../models/projects.js'; import { asProjectKey } from '../../../types/branded.js'; import { Project } from '../../../domain/aggregates/project/project.aggregate.js'; // Mock the ProjectsClient vi.mock('../../../client/projects-client.js'); // Mock the logger vi.mock('../../../utils/logging/logger.js', () => ({ createLogger: vi.fn(() => ({ debug: vi.fn(), info: vi.fn(), warn: vi.fn(), error: vi.fn(), })), })); describe('ProjectRepository', () => { let repository: ProjectRepository; let mockProjectsClient: anyed<ProjectsClient>; let mockApiProjects: DeepSourceProject[]; beforeEach(() => { // Reset all mocks vi.clearAllMocks(); // Create mock ProjectsClient with mocked methods mockProjectsClient = { listProjects: vi.fn(), } as unknown as anyed<ProjectsClient>; // Create test data mockApiProjects = [ { key: asProjectKey('project-1'), name: 'Project One', repository: { url: 'https://github.com/user/project-1', provider: 'GITHUB', login: 'user', isPrivate: false, isActivated: true, }, }, { key: asProjectKey('project-2'), name: 'Project Two', repository: { url: 'https://gitlab.com/user/project-2', provider: 'GITLAB', login: 'user', isPrivate: true, isActivated: false, }, }, { key: asProjectKey('project-3'), name: 'Project Three', repository: { url: 'https://github.com/org/project-3', provider: 'GITHUB', login: 'org', isPrivate: false, isActivated: true, }, }, ]; // Setup default mock behavior mockProjectsClient.listProjects.mockResolvedValue(mockApiProjects); // Create repository instance repository = new ProjectRepository(mockProjectsClient); }); describe('findById', () => { it('should find project by id', async () => { const projectKey = asProjectKey('project-1'); const project = await repository.findById(projectKey); expect(project).not.toBeNull(); expect(project?.key).toBe(projectKey); expect(project?.name).toBe('Project One'); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(1); }); it('should return null when project not found', async () => { const projectKey = asProjectKey('non-existent'); const project = await repository.findById(projectKey); expect(project).toBeNull(); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(1); }); it('should propagate client errors', async () => { const error = new Error('API Error'); mockProjectsClient.listProjects.mockRejectedValue(error); await expect(repository.findById(asProjectKey('project-1'))).rejects.toThrow('API Error'); }); }); describe('findByKey', () => { it('should find project by key', async () => { const projectKey = asProjectKey('project-2'); const project = await repository.findByKey(projectKey); expect(project).not.toBeNull(); expect(project?.key).toBe(projectKey); expect(project?.name).toBe('Project Two'); expect(project?.repository.provider).toBe('GITLAB'); expect(project?.status).toBe('INACTIVE'); }); it('should map activated projects to ACTIVE status', async () => { const projectKey = asProjectKey('project-1'); const project = await repository.findByKey(projectKey); expect(project).not.toBeNull(); expect(project?.status).toBe('ACTIVE'); expect(project?.configuration.isActivated).toBe(true); }); it('should map non-activated projects to INACTIVE status', async () => { const projectKey = asProjectKey('project-2'); const project = await repository.findByKey(projectKey); expect(project).not.toBeNull(); expect(project?.status).toBe('INACTIVE'); expect(project?.configuration.isActivated).toBe(false); }); }); describe('findAll', () => { it('should return all projects', async () => { const projects = await repository.findAll(); expect(projects).toHaveLength(3); expect(projects[0]).toBeInstanceOf(Project); expect(projects.map((p) => p.key)).toEqual([ asProjectKey('project-1'), asProjectKey('project-2'), asProjectKey('project-3'), ]); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(1); }); it('should return empty array when no projects exist', async () => { mockProjectsClient.listProjects.mockResolvedValue([]); const projects = await repository.findAll(); expect(projects).toEqual([]); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(1); }); it('should propagate client errors', async () => { const error = new Error('Network Error'); mockProjectsClient.listProjects.mockRejectedValue(error); await expect(repository.findAll()).rejects.toThrow('Network Error'); }); }); describe('findActive', () => { it('should return only active projects', async () => { const projects = await repository.findActive(); expect(projects).toHaveLength(2); expect(projects.map((p) => p.key)).toEqual([ asProjectKey('project-1'), asProjectKey('project-3'), ]); expect(projects.every((p) => p.status === 'ACTIVE')).toBe(true); }); it('should return empty array when no active projects exist', async () => { // Mock all projects as inactive const inactiveProjects = mockApiProjects.map((p) => ({ ...p, repository: { ...p.repository, isActivated: false }, })); mockProjectsClient.listProjects.mockResolvedValue(inactiveProjects); const projects = await repository.findActive(); expect(projects).toEqual([]); }); }); describe('findByProvider', () => { it('should return projects for specific provider', async () => { const projects = await repository.findByProvider('GITHUB'); expect(projects).toHaveLength(2); expect(projects.map((p) => p.key)).toEqual([ asProjectKey('project-1'), asProjectKey('project-3'), ]); expect(projects.every((p) => p.repository.provider === 'GITHUB')).toBe(true); }); it('should return empty array for unknown provider', async () => { const projects = await repository.findByProvider('BITBUCKET'); expect(projects).toEqual([]); }); it('should handle GITLAB provider', async () => { const projects = await repository.findByProvider('GITLAB'); expect(projects).toHaveLength(1); expect(projects[0].key).toBe(asProjectKey('project-2')); expect(projects[0].repository.provider).toBe('GITLAB'); }); }); describe('exists', () => { it('should return true when project exists', async () => { const exists = await repository.exists(asProjectKey('project-1')); expect(exists).toBe(true); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(1); }); it('should return false when project does not exist', async () => { const exists = await repository.exists(asProjectKey('non-existent')); expect(exists).toBe(false); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(1); }); it('should propagate client errors', async () => { const error = new Error('API Error'); mockProjectsClient.listProjects.mockRejectedValue(error); await expect(repository.exists(asProjectKey('project-1'))).rejects.toThrow('API Error'); }); }); describe('count', () => { it('should return total project count', async () => { const count = await repository.count(); expect(count).toBe(3); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(1); }); it('should return 0 when no projects exist', async () => { mockProjectsClient.listProjects.mockResolvedValue([]); const count = await repository.count(); expect(count).toBe(0); }); }); describe('countActive', () => { it('should return active project count', async () => { const count = await repository.countActive(); expect(count).toBe(2); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(1); }); it('should return 0 when no active projects exist', async () => { // Mock all projects as inactive const inactiveProjects = mockApiProjects.map((p) => ({ ...p, repository: { ...p.repository, isActivated: false }, })); mockProjectsClient.listProjects.mockResolvedValue(inactiveProjects); const count = await repository.countActive(); expect(count).toBe(0); }); }); describe('save', () => { it('should throw error indicating operation not supported', async () => { const project = Project.create({ key: asProjectKey('new-project'), name: 'New Project', repository: { url: 'https://github.com/user/new-project', provider: 'GITHUB', login: 'user', isPrivate: false, }, }); await expect(repository.save(project)).rejects.toThrow( 'Save operation is not supported by DeepSource API' ); }); }); describe('delete', () => { it('should throw error indicating operation not supported', async () => { await expect(repository.delete(asProjectKey('project-1'))).rejects.toThrow( 'Delete operation is not supported by DeepSource API' ); }); }); describe('data freshness', () => { it('should fetch fresh data on every request', async () => { // First call await repository.findByKey(asProjectKey('project-1')); // Update mock data mockApiProjects[0].name = 'Updated Project One'; // Second call should get fresh data const project = await repository.findByKey(asProjectKey('project-1')); expect(project?.name).toBe('Updated Project One'); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(2); }); it('should not cache results between different method calls', async () => { await repository.findAll(); await repository.findActive(); await repository.findByProvider('GITHUB'); await repository.count(); await repository.countActive(); expect(mockProjectsClient.listProjects).toHaveBeenCalledTimes(5); }); }); });