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
This commit is contained in:
@@ -0,0 +1,278 @@
|
||||
import { describe, it, expect, beforeAll, afterAll, beforeEach } from '@jest/globals';
|
||||
import { AppDataSource } from '../../../src/Infrastructure/ormconfig';
|
||||
import { ChatRepository } from '../../../src/Infrastructure/Repository/ChatRepository';
|
||||
import { ChatArchiveRepository } from '../../../src/Infrastructure/Repository/ChatArchiveRepository';
|
||||
import { UserRepository } from '../../../src/Infrastructure/Repository/UserRepository';
|
||||
import { ChatType } from '../../../src/Domain/Chat/ChatAggregate';
|
||||
import { UserState } from '../../../src/Domain/User/UserAggregate';
|
||||
import { v4 as uuidv4 } from 'uuid';
|
||||
|
||||
describe('Chat Messaging System', () => {
|
||||
let chatRepository: ChatRepository;
|
||||
let chatArchiveRepository: ChatArchiveRepository;
|
||||
let userRepository: UserRepository;
|
||||
|
||||
let testUser1: any;
|
||||
let testUser2: any;
|
||||
let testPremiumUser: any;
|
||||
|
||||
beforeAll(async () => {
|
||||
if (!AppDataSource.isInitialized) {
|
||||
await AppDataSource.initialize();
|
||||
}
|
||||
|
||||
chatRepository = new ChatRepository();
|
||||
chatArchiveRepository = new ChatArchiveRepository();
|
||||
userRepository = new UserRepository();
|
||||
});
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create test users
|
||||
testUser1 = await userRepository.create({
|
||||
username: `testuser1_${Date.now()}`,
|
||||
email: `test1_${Date.now()}@example.com`,
|
||||
password: 'hashedpassword',
|
||||
fname: 'Test',
|
||||
lname: 'User1',
|
||||
type: 'regular',
|
||||
state: UserState.VERIFIED_REGULAR
|
||||
});
|
||||
|
||||
testUser2 = await userRepository.create({
|
||||
username: `testuser2_${Date.now()}`,
|
||||
email: `test2_${Date.now()}@example.com`,
|
||||
password: 'hashedpassword',
|
||||
fname: 'Test',
|
||||
lname: 'User2',
|
||||
type: 'regular',
|
||||
state: UserState.VERIFIED_REGULAR
|
||||
});
|
||||
|
||||
testPremiumUser = await userRepository.create({
|
||||
username: `premiumuser_${Date.now()}`,
|
||||
email: `premium_${Date.now()}@example.com`,
|
||||
password: 'hashedpassword',
|
||||
fname: 'Premium',
|
||||
lname: 'User',
|
||||
type: 'premium',
|
||||
state: UserState.VERIFIED_PREMIUM
|
||||
});
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
if (AppDataSource.isInitialized) {
|
||||
await AppDataSource.destroy();
|
||||
}
|
||||
});
|
||||
|
||||
describe('Direct Chat Creation', () => {
|
||||
it('should create a direct chat between two users', async () => {
|
||||
const chat = await chatRepository.create({
|
||||
type: ChatType.DIRECT,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
expect(chat).toBeDefined();
|
||||
expect(chat.type).toBe(ChatType.DIRECT);
|
||||
expect(chat.users).toEqual([testUser1.id, testUser2.id]);
|
||||
expect(chat.messages).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Group Chat Creation', () => {
|
||||
it('should create a group chat', async () => {
|
||||
const chat = await chatRepository.create({
|
||||
type: ChatType.GROUP,
|
||||
name: 'Test Group',
|
||||
createdBy: testPremiumUser.id,
|
||||
users: [testPremiumUser.id, testUser1.id, testUser2.id],
|
||||
messages: [],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
expect(chat).toBeDefined();
|
||||
expect(chat.type).toBe(ChatType.GROUP);
|
||||
expect(chat.name).toBe('Test Group');
|
||||
expect(chat.createdBy).toBe(testPremiumUser.id);
|
||||
expect(chat.users.length).toBe(3);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Game Chat Creation', () => {
|
||||
it('should create a game chat', async () => {
|
||||
const gameId = uuidv4();
|
||||
|
||||
const chat = await chatRepository.create({
|
||||
type: ChatType.GAME,
|
||||
name: 'Test Game Chat',
|
||||
gameId: gameId,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
expect(chat).toBeDefined();
|
||||
expect(chat.type).toBe(ChatType.GAME);
|
||||
expect(chat.gameId).toBe(gameId);
|
||||
expect(chat.name).toBe('Test Game Chat');
|
||||
});
|
||||
|
||||
it('should find game chat by game id', async () => {
|
||||
const gameId = uuidv4();
|
||||
|
||||
await chatRepository.create({
|
||||
type: ChatType.GAME,
|
||||
name: 'Test Game Chat',
|
||||
gameId: gameId,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
const foundChat = await chatRepository.findByGameId(gameId);
|
||||
expect(foundChat).toBeDefined();
|
||||
expect(foundChat!.gameId).toBe(gameId);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Message Management', () => {
|
||||
it('should add and retrieve messages', async () => {
|
||||
const chat = await chatRepository.create({
|
||||
type: ChatType.DIRECT,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
const message = {
|
||||
id: uuidv4(),
|
||||
date: new Date(),
|
||||
userid: testUser1.id,
|
||||
text: 'Hello, this is a test message!'
|
||||
};
|
||||
|
||||
await chatRepository.update(chat.id, {
|
||||
messages: [message],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
const updatedChat = await chatRepository.findById(chat.id);
|
||||
expect(updatedChat!.messages).toHaveLength(1);
|
||||
expect(updatedChat!.messages[0].text).toBe('Hello, this is a test message!');
|
||||
expect(updatedChat!.messages[0].userid).toBe(testUser1.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Chat Archiving', () => {
|
||||
it('should archive a chat with messages', async () => {
|
||||
const message = {
|
||||
id: uuidv4(),
|
||||
date: new Date(),
|
||||
userid: testUser1.id,
|
||||
text: 'Message to be archived'
|
||||
};
|
||||
|
||||
const chat = await chatRepository.create({
|
||||
type: ChatType.DIRECT,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [message],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
const archive = await chatRepository.archiveChat(chat);
|
||||
|
||||
expect(archive).toBeDefined();
|
||||
expect(archive.chatId).toBe(chat.id);
|
||||
expect(archive.archivedMessages).toHaveLength(1);
|
||||
expect(archive.archivedMessages[0].text).toBe('Message to be archived');
|
||||
|
||||
// Check that chat messages were cleared
|
||||
const archivedChat = await chatRepository.findById(chat.id);
|
||||
expect(archivedChat!.messages).toEqual([]);
|
||||
expect(archivedChat!.archiveDate).toBeDefined();
|
||||
});
|
||||
|
||||
it('should retrieve archived chat', async () => {
|
||||
const message = {
|
||||
id: uuidv4(),
|
||||
date: new Date(),
|
||||
userid: testUser1.id,
|
||||
text: 'Archived message'
|
||||
};
|
||||
|
||||
const chat = await chatRepository.create({
|
||||
type: ChatType.DIRECT,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [message],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
await chatRepository.archiveChat(chat);
|
||||
|
||||
const archive = await chatRepository.getArchivedChat(chat.id);
|
||||
expect(archive).toBeDefined();
|
||||
expect(archive!.archivedMessages).toHaveLength(1);
|
||||
expect(archive!.archivedMessages[0].text).toBe('Archived message');
|
||||
});
|
||||
});
|
||||
|
||||
describe('Chat Queries', () => {
|
||||
it('should find chats by user id', async () => {
|
||||
const chat1 = await chatRepository.create({
|
||||
type: ChatType.DIRECT,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
const chat2 = await chatRepository.create({
|
||||
type: ChatType.GROUP,
|
||||
name: 'Test Group',
|
||||
createdBy: testPremiumUser.id,
|
||||
users: [testPremiumUser.id, testUser1.id],
|
||||
messages: [],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
const userChats = await chatRepository.findByUserId(testUser1.id);
|
||||
expect(userChats.length).toBeGreaterThanOrEqual(2);
|
||||
|
||||
const chatIds = userChats.map(c => c.id);
|
||||
expect(chatIds).toContain(chat1.id);
|
||||
expect(chatIds).toContain(chat2.id);
|
||||
});
|
||||
|
||||
it('should find active chats for user', async () => {
|
||||
await chatRepository.create({
|
||||
type: ChatType.DIRECT,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [],
|
||||
lastActivity: new Date()
|
||||
});
|
||||
|
||||
const activeChats = await chatRepository.findActiveChatsForUser(testUser1.id);
|
||||
expect(activeChats.length).toBeGreaterThanOrEqual(1);
|
||||
|
||||
// All returned chats should be active
|
||||
activeChats.forEach(chat => {
|
||||
expect(chat.users).toContain(testUser1.id);
|
||||
});
|
||||
});
|
||||
|
||||
it('should find inactive chats', async () => {
|
||||
const oldDate = new Date(Date.now() - 2 * 60 * 60 * 1000); // 2 hours ago
|
||||
|
||||
await chatRepository.create({
|
||||
type: ChatType.DIRECT,
|
||||
users: [testUser1.id, testUser2.id],
|
||||
messages: [],
|
||||
lastActivity: oldDate
|
||||
});
|
||||
|
||||
const inactiveChats = await chatRepository.findInactiveChats(60); // 60 minutes
|
||||
expect(inactiveChats.length).toBeGreaterThanOrEqual(1);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user