Files
SerpentRace/SerpentRace_Backend/tests/comprehensive-repository-coverage.test.ts
T
Donat 86211923db Backend Complete: Interface Refactoring & Service Container Enhancements
Repository Interface Optimization:
- Created IBaseRepository.ts and IPaginatedRepository.ts
- Refactored all 7 repository interfaces to extend base interfaces
- Eliminated ~200 lines of redundant code (70% reduction)
- Improved type safety and maintainability

 Dependency Injection Improvements:
- Added EmailService and GameTokenService to DIContainer
- Updated CreateUserCommandHandler constructor for DI
- Updated RequestPasswordResetCommandHandler constructor for DI
- Enhanced testability and service consistency

 Environment Configuration:
- Created comprehensive .env.example with 40+ variables
- Organized into 12 logical sections (Database, Security, Email, etc.)
- Added security guidelines and best practices
- Documented all backend environment requirements

 Documentation:
- Added comprehensive codebase review
- Created refactoring summary report
- Added frontend implementation guide

Impact: Improved code quality, reduced maintenance overhead, enhanced developer experience
2025-09-21 03:27:57 +02:00

287 lines
12 KiB
TypeScript

// Comprehensive test coverage for Repository layer
import { IUserRepository } from '../src/Domain/IRepository/IUserRepository';
import { IDeckRepository } from '../src/Domain/IRepository/IDeckRepository';
import { IOrganizationRepository } from '../src/Domain/IRepository/IOrganizationRepository';
import { IContactRepository } from '../src/Domain/IRepository/IContactRepository';
import { UserAggregate, UserState } from '../src/Domain/User/UserAggregate';
import { DeckAggregate, Type as DeckType } from '../src/Domain/Deck/DeckAggregate';
import { OrganizationAggregate } from '../src/Domain/Organization/OrganizationAggregate';
import { ContactAggregate } from '../src/Domain/Contact/ContactAggregate';
import {
createMockUser,
createMockDeck,
createMockOrganization,
createMockContact,
createMockUserRepository,
createMockDeckRepository,
createMockOrganizationRepository,
createMockContactRepository
} from './testUtils';
describe('Repository Layer - Comprehensive Coverage', () => {
describe('IUserRepository Interface Coverage', () => {
let mockUserRepository: jest.Mocked<IUserRepository>;
beforeEach(() => {
mockUserRepository = createMockUserRepository();
});
it('should implement all required methods', () => {
expect(mockUserRepository.create).toBeDefined();
expect(mockUserRepository.findByPage).toBeDefined();
expect(mockUserRepository.findByPageIncludingDeleted).toBeDefined();
expect(mockUserRepository.findById).toBeDefined();
expect(mockUserRepository.findByIdIncludingDeleted).toBeDefined();
expect(mockUserRepository.findByUsername).toBeDefined();
expect(mockUserRepository.findByEmail).toBeDefined();
expect(mockUserRepository.findByToken).toBeDefined();
expect(mockUserRepository.search).toBeDefined();
expect(mockUserRepository.searchIncludingDeleted).toBeDefined();
expect(mockUserRepository.update).toBeDefined();
expect(mockUserRepository.delete).toBeDefined();
expect(mockUserRepository.softDelete).toBeDefined();
expect(mockUserRepository.deactivate).toBeDefined();
});
it('should handle user creation', async () => {
const userData = { username: 'testuser', email: 'test@example.com' };
const mockUser = createMockUser(userData);
mockUserRepository.create.mockResolvedValue(mockUser);
const result = await mockUserRepository.create(userData);
expect(result).toEqual(mockUser);
expect(mockUserRepository.create).toHaveBeenCalledWith(userData);
});
it('should handle paginated user retrieval', async () => {
const mockUsers = [createMockUser(), createMockUser({ id: 'user2' })];
mockUserRepository.findByPage.mockResolvedValue({ users: mockUsers, totalCount: 2 });
const result = await mockUserRepository.findByPage(0, 10);
expect(result.users).toHaveLength(2);
expect(result.totalCount).toBe(2);
});
it('should handle user search operations', async () => {
const mockUsers = [createMockUser({ username: 'searchtest' })];
mockUserRepository.search.mockResolvedValue({ users: mockUsers, totalCount: 1 });
const result = await mockUserRepository.search('searchtest');
expect(result.users).toHaveLength(1);
expect(result.users[0].username).toBe('searchtest');
});
it('should handle user state transitions', async () => {
const mockUser = createMockUser({ state: UserState.VERIFIED_REGULAR });
mockUserRepository.deactivate.mockResolvedValue(mockUser);
const result = await mockUserRepository.deactivate('user-id');
expect(result).toEqual(mockUser);
});
});
describe('IDeckRepository Interface Coverage', () => {
let mockDeckRepository: jest.Mocked<IDeckRepository>;
beforeEach(() => {
mockDeckRepository = createMockDeckRepository();
});
it('should implement all required methods including new ones', () => {
expect(mockDeckRepository.create).toBeDefined();
expect(mockDeckRepository.findByPage).toBeDefined();
expect(mockDeckRepository.findByPageIncludingDeleted).toBeDefined();
expect(mockDeckRepository.findById).toBeDefined();
expect(mockDeckRepository.findByIdIncludingDeleted).toBeDefined();
expect(mockDeckRepository.search).toBeDefined();
expect(mockDeckRepository.searchIncludingDeleted).toBeDefined();
expect(mockDeckRepository.update).toBeDefined();
expect(mockDeckRepository.delete).toBeDefined();
expect(mockDeckRepository.softDelete).toBeDefined();
expect(mockDeckRepository.countActiveByUserId).toBeDefined();
expect(mockDeckRepository.countOrganizationalByUserId).toBeDefined();
expect(mockDeckRepository.findFilteredDecks).toBeDefined();
});
it('should handle deck counting operations', async () => {
mockDeckRepository.countActiveByUserId.mockResolvedValue(5);
mockDeckRepository.countOrganizationalByUserId.mockResolvedValue(3);
const activeCount = await mockDeckRepository.countActiveByUserId('user-id');
const orgCount = await mockDeckRepository.countOrganizationalByUserId('user-id');
expect(activeCount).toBe(5);
expect(orgCount).toBe(3);
});
it('should handle filtered deck retrieval', async () => {
const mockDecks = [createMockDeck(), createMockDeck({ id: 'deck2' })];
mockDeckRepository.findFilteredDecks.mockResolvedValue({ decks: mockDecks, totalCount: 2 });
const result = await mockDeckRepository.findFilteredDecks('user-id', 'org-id', false, 0, 10);
expect(result.decks).toHaveLength(2);
expect(result.totalCount).toBe(2);
});
it('should handle different deck types', async () => {
const jokerDeck = createMockDeck({ type: DeckType.JOKER });
const luckDeck = createMockDeck({ type: DeckType.LUCK });
const questionDeck = createMockDeck({ type: DeckType.QUESTION });
mockDeckRepository.create.mockResolvedValueOnce(jokerDeck);
mockDeckRepository.create.mockResolvedValueOnce(luckDeck);
mockDeckRepository.create.mockResolvedValueOnce(questionDeck);
const result1 = await mockDeckRepository.create({ type: DeckType.JOKER });
const result2 = await mockDeckRepository.create({ type: DeckType.LUCK });
const result3 = await mockDeckRepository.create({ type: DeckType.QUESTION });
expect(result1.type).toBe(DeckType.JOKER);
expect(result2.type).toBe(DeckType.LUCK);
expect(result3.type).toBe(DeckType.QUESTION);
});
});
describe('IOrganizationRepository Interface Coverage', () => {
let mockOrgRepository: jest.Mocked<IOrganizationRepository>;
beforeEach(() => {
mockOrgRepository = createMockOrganizationRepository();
});
it('should implement all required methods', () => {
expect(mockOrgRepository.create).toBeDefined();
expect(mockOrgRepository.findByPage).toBeDefined();
expect(mockOrgRepository.findByPageIncludingDeleted).toBeDefined();
expect(mockOrgRepository.findById).toBeDefined();
expect(mockOrgRepository.findByIdIncludingDeleted).toBeDefined();
expect(mockOrgRepository.search).toBeDefined();
expect(mockOrgRepository.searchIncludingDeleted).toBeDefined();
expect(mockOrgRepository.update).toBeDefined();
expect(mockOrgRepository.delete).toBeDefined();
expect(mockOrgRepository.softDelete).toBeDefined();
});
it('should handle organization CRUD operations', async () => {
const orgData = { name: 'Test Org', contactemail: 'test@org.com' };
const mockOrg = createMockOrganization(orgData);
mockOrgRepository.create.mockResolvedValue(mockOrg);
mockOrgRepository.findById.mockResolvedValue(mockOrg);
mockOrgRepository.update.mockResolvedValue(mockOrg);
mockOrgRepository.softDelete.mockResolvedValue(mockOrg);
const created = await mockOrgRepository.create(orgData);
const found = await mockOrgRepository.findById('org-id');
const updated = await mockOrgRepository.update('org-id', { name: 'Updated Org' });
const deleted = await mockOrgRepository.softDelete('org-id');
expect(created.name).toBe('Test Org');
expect(found).toEqual(mockOrg);
expect(updated).toEqual(mockOrg);
expect(deleted).toEqual(mockOrg);
});
});
describe('IContactRepository Interface Coverage', () => {
let mockContactRepository: jest.Mocked<IContactRepository>;
beforeEach(() => {
mockContactRepository = createMockContactRepository();
});
it('should implement all required methods', () => {
expect(mockContactRepository.create).toBeDefined();
expect(mockContactRepository.findById).toBeDefined();
expect(mockContactRepository.findByPage).toBeDefined();
expect(mockContactRepository.findByPageIncludingDeleted).toBeDefined();
expect(mockContactRepository.findByIdIncludingDeleted).toBeDefined();
expect(mockContactRepository.search).toBeDefined();
expect(mockContactRepository.searchIncludingDeleted).toBeDefined();
expect(mockContactRepository.update).toBeDefined();
expect(mockContactRepository.delete).toBeDefined();
expect(mockContactRepository.softDelete).toBeDefined();
});
it('should handle contact search operations', async () => {
const mockContacts = [createMockContact({ email: 'test@example.com' })];
mockContactRepository.search.mockResolvedValue(mockContacts);
mockContactRepository.searchIncludingDeleted.mockResolvedValue(mockContacts);
const activeResults = await mockContactRepository.search('test');
const allResults = await mockContactRepository.searchIncludingDeleted('test');
expect(activeResults).toHaveLength(1);
expect(allResults).toHaveLength(1);
});
it('should handle contact lifecycle', async () => {
const contactData = { email: 'user@example.com', message: 'Help Request' };
const mockContact = createMockContact(contactData);
mockContactRepository.create.mockResolvedValue(mockContact);
mockContactRepository.findById.mockResolvedValue(mockContact);
mockContactRepository.findByIdIncludingDeleted.mockResolvedValue(mockContact);
const created = await mockContactRepository.create(contactData);
const found = await mockContactRepository.findById('contact-id');
const foundWithDeleted = await mockContactRepository.findByIdIncludingDeleted('contact-id');
expect(created.email).toBe('user@example.com');
expect(found).toEqual(mockContact);
expect(foundWithDeleted).toEqual(mockContact);
});
});
describe('Cross-Repository Integration Tests', () => {
let userRepo: jest.Mocked<IUserRepository>;
let deckRepo: jest.Mocked<IDeckRepository>;
let orgRepo: jest.Mocked<IOrganizationRepository>;
beforeEach(() => {
userRepo = createMockUserRepository();
deckRepo = createMockDeckRepository();
orgRepo = createMockOrganizationRepository();
});
it('should simulate user-deck relationship operations', async () => {
const mockUser = createMockUser({ id: 'user-123' });
const mockDecks = [
createMockDeck({ userid: 'user-123', name: 'Deck 1' }),
createMockDeck({ userid: 'user-123', name: 'Deck 2' })
];
userRepo.findById.mockResolvedValue(mockUser);
deckRepo.findFilteredDecks.mockResolvedValue({ decks: mockDecks, totalCount: 2 });
deckRepo.countActiveByUserId.mockResolvedValue(2);
const user = await userRepo.findById('user-123');
const userDecks = await deckRepo.findFilteredDecks('user-123');
const deckCount = await deckRepo.countActiveByUserId('user-123');
expect(user).toBeDefined();
expect(userDecks.decks).toHaveLength(2);
expect(deckCount).toBe(2);
expect(userDecks.decks.every(deck => deck.userid === 'user-123')).toBe(true);
});
it('should simulate organization-user relationship operations', async () => {
const mockOrg = createMockOrganization({ id: 'org-123', name: 'Test Organization' });
const mockUsers = [
createMockUser({ orgid: 'org-123' }),
createMockUser({ orgid: 'org-123', id: 'user-2' })
];
orgRepo.findById.mockResolvedValue(mockOrg);
userRepo.findByPage.mockResolvedValue({ users: mockUsers, totalCount: 2 });
const org = await orgRepo.findById('org-123');
const orgUsers = await userRepo.findByPage(0, 10);
expect(org).toBeDefined();
expect(orgUsers.users).toHaveLength(2);
expect(orgUsers.users.every(user => user.orgid === 'org-123')).toBe(true);
});
});
});