From 78c9c1ec75f71c079e5849efc9bfe8d17f07433d Mon Sep 17 00:00:00 2001 From: Marc Mintel Date: Sat, 24 Jan 2026 01:53:04 +0100 Subject: [PATCH] core tests --- .../domain/errors/AdminDomainError.test.ts | 303 ++++++++ .../repositories/AdminUserRepository.test.ts | 721 ++++++++++++++++++ .../entities/AdminUserOrmEntity.test.ts | 610 +++++++++++++++ .../errors/TypeOrmAdminSchemaError.test.ts | 521 +++++++++++++ 4 files changed, 2155 insertions(+) create mode 100644 core/admin/domain/errors/AdminDomainError.test.ts create mode 100644 core/admin/domain/repositories/AdminUserRepository.test.ts create mode 100644 core/admin/infrastructure/typeorm/entities/AdminUserOrmEntity.test.ts create mode 100644 core/admin/infrastructure/typeorm/errors/TypeOrmAdminSchemaError.test.ts diff --git a/core/admin/domain/errors/AdminDomainError.test.ts b/core/admin/domain/errors/AdminDomainError.test.ts new file mode 100644 index 000000000..e003b1a5e --- /dev/null +++ b/core/admin/domain/errors/AdminDomainError.test.ts @@ -0,0 +1,303 @@ +import { describe, expect, it } from 'vitest'; +import { AdminDomainError, AdminDomainValidationError, AdminDomainInvariantError, AuthorizationError } from './AdminDomainError'; + +describe('AdminDomainError', () => { + describe('TDD - Test First', () => { + describe('AdminDomainError', () => { + it('should create an error with correct properties', () => { + // Arrange & Act + const error = new (class extends AdminDomainError { + readonly kind = 'validation' as const; + })('Test error message'); + + // Assert + expect(error.message).toBe('Test error message'); + expect(error.type).toBe('domain'); + expect(error.context).toBe('admin-domain'); + expect(error.kind).toBe('validation'); + }); + + it('should have correct error name', () => { + // Arrange & Act + const error = new (class extends AdminDomainError { + readonly kind = 'validation' as const; + })('Test error'); + + // Assert + expect(error.name).toBe('AdminDomainError'); + }); + + it('should preserve prototype chain', () => { + // Arrange & Act + const error = new (class extends AdminDomainError { + readonly kind = 'validation' as const; + })('Test error'); + + // Assert + expect(error instanceof AdminDomainError).toBe(true); + expect(error instanceof Error).toBe(true); + }); + + it('should handle empty message', () => { + // Arrange & Act + const error = new (class extends AdminDomainError { + readonly kind = 'validation' as const; + })(''); + + // Assert + expect(error.message).toBe(''); + }); + + it('should handle long message', () => { + // Arrange + const longMessage = 'This is a very long error message that contains many characters and should be handled correctly by the error class'; + + // Act + const error = new (class extends AdminDomainError { + readonly kind = 'validation' as const; + })(longMessage); + + // Assert + expect(error.message).toBe(longMessage); + }); + }); + + describe('AdminDomainValidationError', () => { + it('should create a validation error', () => { + // Arrange & Act + const error = new AdminDomainValidationError('Invalid email format'); + + // Assert + expect(error.message).toBe('Invalid email format'); + expect(error.type).toBe('domain'); + expect(error.context).toBe('admin-domain'); + expect(error.kind).toBe('validation'); + }); + + it('should have correct error name', () => { + // Arrange & Act + const error = new AdminDomainValidationError('Test error'); + + // Assert + expect(error.name).toBe('AdminDomainValidationError'); + }); + + it('should be instance of AdminDomainError', () => { + // Arrange & Act + const error = new AdminDomainValidationError('Test error'); + + // Assert + expect(error instanceof AdminDomainError).toBe(true); + expect(error instanceof AdminDomainValidationError).toBe(true); + expect(error instanceof Error).toBe(true); + }); + + it('should handle empty message', () => { + // Arrange & Act + const error = new AdminDomainValidationError(''); + + // Assert + expect(error.message).toBe(''); + }); + + it('should handle complex validation message', () => { + // Arrange + const message = 'Field "email" must be a valid email address. Received: "invalid-email"'; + + // Act + const error = new AdminDomainValidationError(message); + + // Assert + expect(error.message).toBe(message); + }); + }); + + describe('AdminDomainInvariantError', () => { + it('should create an invariant error', () => { + // Arrange & Act + const error = new AdminDomainInvariantError('User must have at least one role'); + + // Assert + expect(error.message).toBe('User must have at least one role'); + expect(error.type).toBe('domain'); + expect(error.context).toBe('admin-domain'); + expect(error.kind).toBe('invariant'); + }); + + it('should have correct error name', () => { + // Arrange & Act + const error = new AdminDomainInvariantError('Test error'); + + // Assert + expect(error.name).toBe('AdminDomainInvariantError'); + }); + + it('should be instance of AdminDomainError', () => { + // Arrange & Act + const error = new AdminDomainInvariantError('Test error'); + + // Assert + expect(error instanceof AdminDomainError).toBe(true); + expect(error instanceof AdminDomainInvariantError).toBe(true); + expect(error instanceof Error).toBe(true); + }); + + it('should handle empty message', () => { + // Arrange & Act + const error = new AdminDomainInvariantError(''); + + // Assert + expect(error.message).toBe(''); + }); + + it('should handle complex invariant message', () => { + // Arrange + const message = 'Invariant violation: User status "active" cannot be changed to "deleted" without proper authorization'; + + // Act + const error = new AdminDomainInvariantError(message); + + // Assert + expect(error.message).toBe(message); + }); + }); + + describe('AuthorizationError', () => { + it('should create an authorization error', () => { + // Arrange & Act + const error = new AuthorizationError('User does not have permission to perform this action'); + + // Assert + expect(error.message).toBe('User does not have permission to perform this action'); + expect(error.type).toBe('domain'); + expect(error.context).toBe('admin-domain'); + expect(error.kind).toBe('authorization'); + }); + + it('should have correct error name', () => { + // Arrange & Act + const error = new AuthorizationError('Test error'); + + // Assert + expect(error.name).toBe('AuthorizationError'); + }); + + it('should be instance of AdminDomainError', () => { + // Arrange & Act + const error = new AuthorizationError('Test error'); + + // Assert + expect(error instanceof AdminDomainError).toBe(true); + expect(error instanceof AuthorizationError).toBe(true); + expect(error instanceof Error).toBe(true); + }); + + it('should handle empty message', () => { + // Arrange & Act + const error = new AuthorizationError(''); + + // Assert + expect(error.message).toBe(''); + }); + + it('should handle complex authorization message', () => { + // Arrange + const message = 'Authorization failed: User "admin@example.com" (role: admin) attempted to modify role of user "owner@example.com" (role: owner)'; + + // Act + const error = new AuthorizationError(message); + + // Assert + expect(error.message).toBe(message); + }); + }); + + describe('Error hierarchy', () => { + it('should have correct inheritance chain for AdminDomainValidationError', () => { + // Arrange & Act + const error = new AdminDomainValidationError('Test'); + + // Assert + expect(error instanceof AdminDomainError).toBe(true); + expect(error instanceof Error).toBe(true); + }); + + it('should have correct inheritance chain for AdminDomainInvariantError', () => { + // Arrange & Act + const error = new AdminDomainInvariantError('Test'); + + // Assert + expect(error instanceof AdminDomainError).toBe(true); + expect(error instanceof Error).toBe(true); + }); + + it('should have correct inheritance chain for AuthorizationError', () => { + // Arrange & Act + const error = new AuthorizationError('Test'); + + // Assert + expect(error instanceof AdminDomainError).toBe(true); + expect(error instanceof Error).toBe(true); + }); + + it('should have consistent type and context across all error types', () => { + // Arrange + const errors = [ + new AdminDomainValidationError('Test'), + new AdminDomainInvariantError('Test'), + new AuthorizationError('Test'), + ]; + + // Assert + errors.forEach(error => { + expect(error.type).toBe('domain'); + expect(error.context).toBe('admin-domain'); + }); + }); + + it('should have different kinds for different error types', () => { + // Arrange + const validationError = new AdminDomainValidationError('Test'); + const invariantError = new AdminDomainInvariantError('Test'); + const authorizationError = new AuthorizationError('Test'); + + // Assert + expect(validationError.kind).toBe('validation'); + expect(invariantError.kind).toBe('invariant'); + expect(authorizationError.kind).toBe('authorization'); + }); + }); + + describe('Error stack trace', () => { + it('should have a stack trace', () => { + // Arrange & Act + const error = new AdminDomainValidationError('Test error'); + + // Assert + expect(error.stack).toBeDefined(); + expect(typeof error.stack).toBe('string'); + expect(error.stack).toContain('AdminDomainValidationError'); + }); + + it('should have stack trace for AdminDomainInvariantError', () => { + // Arrange & Act + const error = new AdminDomainInvariantError('Test error'); + + // Assert + expect(error.stack).toBeDefined(); + expect(typeof error.stack).toBe('string'); + expect(error.stack).toContain('AdminDomainInvariantError'); + }); + + it('should have stack trace for AuthorizationError', () => { + // Arrange & Act + const error = new AuthorizationError('Test error'); + + // Assert + expect(error.stack).toBeDefined(); + expect(typeof error.stack).toBe('string'); + expect(error.stack).toContain('AuthorizationError'); + }); + }); + }); +}); diff --git a/core/admin/domain/repositories/AdminUserRepository.test.ts b/core/admin/domain/repositories/AdminUserRepository.test.ts new file mode 100644 index 000000000..697a982ad --- /dev/null +++ b/core/admin/domain/repositories/AdminUserRepository.test.ts @@ -0,0 +1,721 @@ +import { describe, expect, it, vi } from 'vitest'; +import { AdminUser } from '../entities/AdminUser'; +import { Email } from '../value-objects/Email'; +import { UserId } from '../value-objects/UserId'; +import { UserRole } from '../value-objects/UserRole'; +import { UserStatus } from '../value-objects/UserStatus'; +import type { + AdminUserRepository, + UserFilter, + UserSort, + UserPagination, + UserListQuery, + UserListResult, + StoredAdminUser +} from './AdminUserRepository'; + +describe('AdminUserRepository', () => { + describe('TDD - Test First', () => { + describe('UserFilter interface', () => { + it('should allow optional role filter', () => { + // Arrange + const filter: UserFilter = { + role: UserRole.fromString('admin'), + }; + + // Assert + expect(filter.role).toBeDefined(); + expect(filter.role!.value).toBe('admin'); + }); + + it('should allow optional status filter', () => { + // Arrange + const filter: UserFilter = { + status: UserStatus.fromString('active'), + }; + + // Assert + expect(filter.status).toBeDefined(); + expect(filter.status!.value).toBe('active'); + }); + + it('should allow optional email filter', () => { + // Arrange + const filter: UserFilter = { + email: Email.create('test@example.com'), + }; + + // Assert + expect(filter.email).toBeDefined(); + expect(filter.email!.value).toBe('test@example.com'); + }); + + it('should allow optional search filter', () => { + // Arrange + const filter: UserFilter = { + search: 'john', + }; + + // Assert + expect(filter.search).toBe('john'); + }); + + it('should allow all filters combined', () => { + // Arrange + const filter: UserFilter = { + role: UserRole.fromString('admin'), + status: UserStatus.fromString('active'), + email: Email.create('admin@example.com'), + search: 'admin', + }; + + // Assert + expect(filter.role!.value).toBe('admin'); + expect(filter.status!.value).toBe('active'); + expect(filter.email!.value).toBe('admin@example.com'); + expect(filter.search).toBe('admin'); + }); + }); + + describe('UserSort interface', () => { + it('should allow email field with asc direction', () => { + // Arrange + const sort: UserSort = { + field: 'email', + direction: 'asc', + }; + + // Assert + expect(sort.field).toBe('email'); + expect(sort.direction).toBe('asc'); + }); + + it('should allow email field with desc direction', () => { + // Arrange + const sort: UserSort = { + field: 'email', + direction: 'desc', + }; + + // Assert + expect(sort.field).toBe('email'); + expect(sort.direction).toBe('desc'); + }); + + it('should allow displayName field', () => { + // Arrange + const sort: UserSort = { + field: 'displayName', + direction: 'asc', + }; + + // Assert + expect(sort.field).toBe('displayName'); + }); + + it('should allow createdAt field', () => { + // Arrange + const sort: UserSort = { + field: 'createdAt', + direction: 'desc', + }; + + // Assert + expect(sort.field).toBe('createdAt'); + }); + + it('should allow lastLoginAt field', () => { + // Arrange + const sort: UserSort = { + field: 'lastLoginAt', + direction: 'asc', + }; + + // Assert + expect(sort.field).toBe('lastLoginAt'); + }); + + it('should allow status field', () => { + // Arrange + const sort: UserSort = { + field: 'status', + direction: 'desc', + }; + + // Assert + expect(sort.field).toBe('status'); + }); + }); + + describe('UserPagination interface', () => { + it('should allow valid pagination', () => { + // Arrange + const pagination: UserPagination = { + page: 1, + limit: 10, + }; + + // Assert + expect(pagination.page).toBe(1); + expect(pagination.limit).toBe(10); + }); + + it('should allow pagination with different values', () => { + // Arrange + const pagination: UserPagination = { + page: 5, + limit: 50, + }; + + // Assert + expect(pagination.page).toBe(5); + expect(pagination.limit).toBe(50); + }); + }); + + describe('UserListQuery interface', () => { + it('should allow query with all optional fields', () => { + // Arrange + const query: UserListQuery = { + filter: { + role: UserRole.fromString('admin'), + }, + sort: { + field: 'email', + direction: 'asc', + }, + pagination: { + page: 1, + limit: 10, + }, + }; + + // Assert + expect(query.filter).toBeDefined(); + expect(query.sort).toBeDefined(); + expect(query.pagination).toBeDefined(); + }); + + it('should allow query with only filter', () => { + // Arrange + const query: UserListQuery = { + filter: { + status: UserStatus.fromString('active'), + }, + }; + + // Assert + expect(query.filter).toBeDefined(); + expect(query.sort).toBeUndefined(); + expect(query.pagination).toBeUndefined(); + }); + + it('should allow query with only sort', () => { + // Arrange + const query: UserListQuery = { + sort: { + field: 'displayName', + direction: 'desc', + }, + }; + + // Assert + expect(query.filter).toBeUndefined(); + expect(query.sort).toBeDefined(); + expect(query.pagination).toBeUndefined(); + }); + + it('should allow query with only pagination', () => { + // Arrange + const query: UserListQuery = { + pagination: { + page: 2, + limit: 20, + }, + }; + + // Assert + expect(query.filter).toBeUndefined(); + expect(query.sort).toBeUndefined(); + expect(query.pagination).toBeDefined(); + }); + + it('should allow empty query', () => { + // Arrange + const query: UserListQuery = {}; + + // Assert + expect(query.filter).toBeUndefined(); + expect(query.sort).toBeUndefined(); + expect(query.pagination).toBeUndefined(); + }); + }); + + describe('UserListResult interface', () => { + it('should allow valid result with users', () => { + // Arrange + const user = AdminUser.create({ + id: 'user-1', + email: 'test@example.com', + displayName: 'Test User', + roles: ['user'], + status: 'active', + }); + + const result: UserListResult = { + users: [user], + total: 1, + page: 1, + limit: 10, + totalPages: 1, + }; + + // Assert + expect(result.users).toHaveLength(1); + expect(result.total).toBe(1); + expect(result.page).toBe(1); + expect(result.limit).toBe(10); + expect(result.totalPages).toBe(1); + }); + + it('should allow result with multiple users', () => { + // Arrange + const user1 = AdminUser.create({ + id: 'user-1', + email: 'user1@example.com', + displayName: 'User 1', + roles: ['user'], + status: 'active', + }); + + const user2 = AdminUser.create({ + id: 'user-2', + email: 'user2@example.com', + displayName: 'User 2', + roles: ['user'], + status: 'active', + }); + + const result: UserListResult = { + users: [user1, user2], + total: 2, + page: 1, + limit: 10, + totalPages: 1, + }; + + // Assert + expect(result.users).toHaveLength(2); + expect(result.total).toBe(2); + }); + + it('should allow result with pagination info', () => { + // Arrange + const users = Array.from({ length: 50 }, (_, i) => + AdminUser.create({ + id: `user-${i}`, + email: `user${i}@example.com`, + displayName: `User ${i}`, + roles: ['user'], + status: 'active', + }), + ); + + const result: UserListResult = { + users: users.slice(0, 10), + total: 50, + page: 1, + limit: 10, + totalPages: 5, + }; + + // Assert + expect(result.users).toHaveLength(10); + expect(result.total).toBe(50); + expect(result.page).toBe(1); + expect(result.limit).toBe(10); + expect(result.totalPages).toBe(5); + }); + }); + + describe('StoredAdminUser interface', () => { + it('should allow stored user with all required fields', () => { + // Arrange + const stored: StoredAdminUser = { + id: 'user-1', + email: 'test@example.com', + roles: ['admin'], + status: 'active', + displayName: 'Test User', + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-02'), + }; + + // Assert + expect(stored.id).toBe('user-1'); + expect(stored.email).toBe('test@example.com'); + expect(stored.roles).toEqual(['admin']); + expect(stored.status).toBe('active'); + expect(stored.displayName).toBe('Test User'); + expect(stored.createdAt).toBeInstanceOf(Date); + expect(stored.updatedAt).toBeInstanceOf(Date); + }); + + it('should allow stored user with optional fields', () => { + // Arrange + const stored: StoredAdminUser = { + id: 'user-1', + email: 'test@example.com', + roles: ['admin'], + status: 'active', + displayName: 'Test User', + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-02'), + lastLoginAt: new Date('2024-01-03'), + primaryDriverId: 'driver-123', + }; + + // Assert + expect(stored.lastLoginAt).toBeInstanceOf(Date); + expect(stored.primaryDriverId).toBe('driver-123'); + }); + + it('should allow stored user with multiple roles', () => { + // Arrange + const stored: StoredAdminUser = { + id: 'user-1', + email: 'test@example.com', + roles: ['owner', 'admin', 'user'], + status: 'active', + displayName: 'Test User', + createdAt: new Date('2024-01-01'), + updatedAt: new Date('2024-01-02'), + }; + + // Assert + expect(stored.roles).toHaveLength(3); + expect(stored.roles).toContain('owner'); + expect(stored.roles).toContain('admin'); + expect(stored.roles).toContain('user'); + }); + }); + + describe('Repository interface methods', () => { + it('should define findById method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.findById).toBeDefined(); + expect(typeof mockRepository.findById).toBe('function'); + }); + + it('should define findByEmail method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.findByEmail).toBeDefined(); + expect(typeof mockRepository.findByEmail).toBe('function'); + }); + + it('should define emailExists method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.emailExists).toBeDefined(); + expect(typeof mockRepository.emailExists).toBe('function'); + }); + + it('should define existsById method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.existsById).toBeDefined(); + expect(typeof mockRepository.existsById).toBe('function'); + }); + + it('should define existsByEmail method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.existsByEmail).toBeDefined(); + expect(typeof mockRepository.existsByEmail).toBe('function'); + }); + + it('should define list method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.list).toBeDefined(); + expect(typeof mockRepository.list).toBe('function'); + }); + + it('should define count method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.count).toBeDefined(); + expect(typeof mockRepository.count).toBe('function'); + }); + + it('should define create method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.create).toBeDefined(); + expect(typeof mockRepository.create).toBe('function'); + }); + + it('should define update method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.update).toBeDefined(); + expect(typeof mockRepository.update).toBe('function'); + }); + + it('should define delete method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.delete).toBeDefined(); + expect(typeof mockRepository.delete).toBe('function'); + }); + + it('should define toStored method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.toStored).toBeDefined(); + expect(typeof mockRepository.toStored).toBe('function'); + }); + + it('should define fromStored method signature', () => { + // Arrange + const mockRepository: AdminUserRepository = { + findById: vi.fn(), + findByEmail: vi.fn(), + emailExists: vi.fn(), + existsById: vi.fn(), + existsByEmail: vi.fn(), + list: vi.fn(), + count: vi.fn(), + create: vi.fn(), + update: vi.fn(), + delete: vi.fn(), + toStored: vi.fn(), + fromStored: vi.fn(), + }; + + // Assert + expect(mockRepository.fromStored).toBeDefined(); + expect(typeof mockRepository.fromStored).toBe('function'); + }); + + it('should handle repository operations with mock implementation', async () => { + // Arrange + const user = AdminUser.create({ + id: 'user-1', + email: 'test@example.com', + displayName: 'Test User', + roles: ['user'], + status: 'active', + }); + + const mockRepository: AdminUserRepository = { + findById: vi.fn().mockResolvedValue(user), + findByEmail: vi.fn().mockResolvedValue(user), + emailExists: vi.fn().mockResolvedValue(true), + existsById: vi.fn().mockResolvedValue(true), + existsByEmail: vi.fn().mockResolvedValue(true), + list: vi.fn().mockResolvedValue({ + users: [user], + total: 1, + page: 1, + limit: 10, + totalPages: 1, + }), + count: vi.fn().mockResolvedValue(1), + create: vi.fn().mockResolvedValue(user), + update: vi.fn().mockResolvedValue(user), + delete: vi.fn().mockResolvedValue(undefined), + toStored: vi.fn().mockReturnValue({ + id: 'user-1', + email: 'test@example.com', + roles: ['user'], + status: 'active', + displayName: 'Test User', + createdAt: new Date(), + updatedAt: new Date(), + }), + fromStored: vi.fn().mockReturnValue(user), + }; + + // Act + const foundUser = await mockRepository.findById(UserId.create('user-1')); + const emailExists = await mockRepository.emailExists(Email.create('test@example.com')); + const listResult = await mockRepository.list(); + + // Assert + expect(foundUser).toBe(user); + expect(emailExists).toBe(true); + expect(listResult.users).toHaveLength(1); + expect(mockRepository.findById).toHaveBeenCalledWith(UserId.create('user-1')); + expect(mockRepository.emailExists).toHaveBeenCalledWith(Email.create('test@example.com')); + }); + }); + }); +}); diff --git a/core/admin/infrastructure/typeorm/entities/AdminUserOrmEntity.test.ts b/core/admin/infrastructure/typeorm/entities/AdminUserOrmEntity.test.ts new file mode 100644 index 000000000..419264d13 --- /dev/null +++ b/core/admin/infrastructure/typeorm/entities/AdminUserOrmEntity.test.ts @@ -0,0 +1,610 @@ +import { describe, expect, it } from 'vitest'; +import { AdminUserOrmEntity } from './AdminUserOrmEntity'; + +describe('AdminUserOrmEntity', () => { + describe('TDD - Test First', () => { + describe('entity properties', () => { + it('should have id property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('id'); + }); + + it('should have email property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('email'); + }); + + it('should have displayName property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('displayName'); + }); + + it('should have roles property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('roles'); + }); + + it('should have status property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('status'); + }); + + it('should have primaryDriverId property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('primaryDriverId'); + }); + + it('should have lastLoginAt property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('lastLoginAt'); + }); + + it('should have createdAt property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('createdAt'); + }); + + it('should have updatedAt property', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity).toHaveProperty('updatedAt'); + }); + }); + + describe('property types', () => { + it('should have id as string', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + entity.id = 'test-id'; + + // Act & Assert + expect(typeof entity.id).toBe('string'); + expect(entity.id).toBe('test-id'); + }); + + it('should have email as string', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + entity.email = 'test@example.com'; + + // Act & Assert + expect(typeof entity.email).toBe('string'); + expect(entity.email).toBe('test@example.com'); + }); + + it('should have displayName as string', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + entity.displayName = 'Test User'; + + // Act & Assert + expect(typeof entity.displayName).toBe('string'); + expect(entity.displayName).toBe('Test User'); + }); + + it('should have roles as string array', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + entity.roles = ['admin', 'user']; + + // Act & Assert + expect(Array.isArray(entity.roles)).toBe(true); + expect(entity.roles).toEqual(['admin', 'user']); + }); + + it('should have status as string', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + entity.status = 'active'; + + // Act & Assert + expect(typeof entity.status).toBe('string'); + expect(entity.status).toBe('active'); + }); + + it('should have primaryDriverId as optional string', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity.primaryDriverId).toBeUndefined(); + + entity.primaryDriverId = 'driver-123'; + expect(typeof entity.primaryDriverId).toBe('string'); + expect(entity.primaryDriverId).toBe('driver-123'); + }); + + it('should have lastLoginAt as optional Date', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + expect(entity.lastLoginAt).toBeUndefined(); + + const now = new Date(); + entity.lastLoginAt = now; + expect(entity.lastLoginAt).toBeInstanceOf(Date); + expect(entity.lastLoginAt).toBe(now); + }); + + it('should have createdAt as Date', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + entity.createdAt = now; + + // Act & Assert + expect(entity.createdAt).toBeInstanceOf(Date); + expect(entity.createdAt).toBe(now); + }); + + it('should have updatedAt as Date', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + entity.updatedAt = now; + + // Act & Assert + expect(entity.updatedAt).toBeInstanceOf(Date); + expect(entity.updatedAt).toBe(now); + }); + }); + + describe('property values', () => { + it('should handle valid UUID for id', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const uuid = '123e4567-e89b-12d3-a456-426614174000'; + + // Act + entity.id = uuid; + + // Assert + expect(entity.id).toBe(uuid); + }); + + it('should handle email with special characters', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const email = 'user+tag@example-domain.com'; + + // Act + entity.email = email; + + // Assert + expect(entity.email).toBe(email); + }); + + it('should handle display name with spaces', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const displayName = 'John Doe Smith'; + + // Act + entity.displayName = displayName; + + // Assert + expect(entity.displayName).toBe(displayName); + }); + + it('should handle roles with multiple entries', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const roles = ['owner', 'admin', 'user', 'moderator']; + + // Act + entity.roles = roles; + + // Assert + expect(entity.roles).toEqual(roles); + expect(entity.roles).toHaveLength(4); + }); + + it('should handle status with different values', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act & Assert + entity.status = 'active'; + expect(entity.status).toBe('active'); + + entity.status = 'suspended'; + expect(entity.status).toBe('suspended'); + + entity.status = 'deleted'; + expect(entity.status).toBe('deleted'); + }); + + it('should handle primaryDriverId with valid driver ID', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const driverId = 'driver-abc123'; + + // Act + entity.primaryDriverId = driverId; + + // Assert + expect(entity.primaryDriverId).toBe(driverId); + }); + + it('should handle lastLoginAt with current date', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + + // Act + entity.lastLoginAt = now; + + // Assert + expect(entity.lastLoginAt).toBe(now); + }); + + it('should handle createdAt with specific date', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const specificDate = new Date('2024-01-01T00:00:00.000Z'); + + // Act + entity.createdAt = specificDate; + + // Assert + expect(entity.createdAt).toBe(specificDate); + }); + + it('should handle updatedAt with specific date', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const specificDate = new Date('2024-01-02T00:00:00.000Z'); + + // Act + entity.updatedAt = specificDate; + + // Assert + expect(entity.updatedAt).toBe(specificDate); + }); + }); + + describe('property assignments', () => { + it('should allow setting all properties', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + + // Act + entity.id = 'user-123'; + entity.email = 'test@example.com'; + entity.displayName = 'Test User'; + entity.roles = ['admin']; + entity.status = 'active'; + entity.primaryDriverId = 'driver-456'; + entity.lastLoginAt = now; + entity.createdAt = now; + entity.updatedAt = now; + + // Assert + expect(entity.id).toBe('user-123'); + expect(entity.email).toBe('test@example.com'); + expect(entity.displayName).toBe('Test User'); + expect(entity.roles).toEqual(['admin']); + expect(entity.status).toBe('active'); + expect(entity.primaryDriverId).toBe('driver-456'); + expect(entity.lastLoginAt).toBe(now); + expect(entity.createdAt).toBe(now); + expect(entity.updatedAt).toBe(now); + }); + + it('should allow updating properties', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + const later = new Date(now.getTime() + 1000); + + // Act + entity.id = 'user-123'; + entity.email = 'test@example.com'; + entity.displayName = 'Test User'; + entity.roles = ['user']; + entity.status = 'active'; + entity.primaryDriverId = 'driver-456'; + entity.lastLoginAt = now; + entity.createdAt = now; + entity.updatedAt = now; + + // Update + entity.displayName = 'Updated Name'; + entity.roles = ['admin', 'user']; + entity.status = 'suspended'; + entity.lastLoginAt = later; + entity.updatedAt = later; + + // Assert + expect(entity.displayName).toBe('Updated Name'); + expect(entity.roles).toEqual(['admin', 'user']); + expect(entity.status).toBe('suspended'); + expect(entity.lastLoginAt).toBe(later); + expect(entity.updatedAt).toBe(later); + }); + + it('should allow clearing optional properties', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + + // Act + entity.primaryDriverId = 'driver-123'; + entity.lastLoginAt = now; + + // Clear + entity.primaryDriverId = undefined; + entity.lastLoginAt = undefined; + + // Assert + expect(entity.primaryDriverId).toBeUndefined(); + expect(entity.lastLoginAt).toBeUndefined(); + }); + }); + + describe('empty entity', () => { + it('should create entity with undefined properties', () => { + // Arrange & Act + const entity = new AdminUserOrmEntity(); + + // Assert + expect(entity.id).toBeUndefined(); + expect(entity.email).toBeUndefined(); + expect(entity.displayName).toBeUndefined(); + expect(entity.roles).toBeUndefined(); + expect(entity.status).toBeUndefined(); + expect(entity.primaryDriverId).toBeUndefined(); + expect(entity.lastLoginAt).toBeUndefined(); + expect(entity.createdAt).toBeUndefined(); + expect(entity.updatedAt).toBeUndefined(); + }); + + it('should allow partial initialization', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act + entity.id = 'user-123'; + entity.email = 'test@example.com'; + + // Assert + expect(entity.id).toBe('user-123'); + expect(entity.email).toBe('test@example.com'); + expect(entity.displayName).toBeUndefined(); + expect(entity.roles).toBeUndefined(); + }); + }); + + describe('real-world scenarios', () => { + it('should handle complete user entity', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + + // Act + entity.id = '123e4567-e89b-12d3-a456-426614174000'; + entity.email = 'admin@example.com'; + entity.displayName = 'Administrator'; + entity.roles = ['owner', 'admin']; + entity.status = 'active'; + entity.primaryDriverId = 'driver-789'; + entity.lastLoginAt = now; + entity.createdAt = now; + entity.updatedAt = now; + + // Assert + expect(entity.id).toBe('123e4567-e89b-12d3-a456-426614174000'); + expect(entity.email).toBe('admin@example.com'); + expect(entity.displayName).toBe('Administrator'); + expect(entity.roles).toEqual(['owner', 'admin']); + expect(entity.status).toBe('active'); + expect(entity.primaryDriverId).toBe('driver-789'); + expect(entity.lastLoginAt).toBe(now); + expect(entity.createdAt).toBe(now); + expect(entity.updatedAt).toBe(now); + }); + + it('should handle user without primary driver', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + + // Act + entity.id = 'user-456'; + entity.email = 'user@example.com'; + entity.displayName = 'Regular User'; + entity.roles = ['user']; + entity.status = 'active'; + entity.createdAt = now; + entity.updatedAt = now; + + // Assert + expect(entity.primaryDriverId).toBeUndefined(); + expect(entity.lastLoginAt).toBeUndefined(); + }); + + it('should handle suspended user', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + + // Act + entity.id = 'user-789'; + entity.email = 'suspended@example.com'; + entity.displayName = 'Suspended User'; + entity.roles = ['user']; + entity.status = 'suspended'; + entity.createdAt = now; + entity.updatedAt = now; + + // Assert + expect(entity.status).toBe('suspended'); + }); + + it('should handle user with many roles', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + + // Act + entity.id = 'user-999'; + entity.email = 'multi@example.com'; + entity.displayName = 'Multi Role User'; + entity.roles = ['owner', 'admin', 'user', 'moderator', 'viewer']; + entity.status = 'active'; + entity.createdAt = now; + entity.updatedAt = now; + + // Assert + expect(entity.roles).toHaveLength(5); + expect(entity.roles).toContain('owner'); + expect(entity.roles).toContain('admin'); + expect(entity.roles).toContain('user'); + expect(entity.roles).toContain('moderator'); + expect(entity.roles).toContain('viewer'); + }); + + it('should handle user with recent login', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + const recentLogin = new Date(now.getTime() - 60000); // 1 minute ago + + // Act + entity.id = 'user-111'; + entity.email = 'active@example.com'; + entity.displayName = 'Active User'; + entity.roles = ['user']; + entity.status = 'active'; + entity.primaryDriverId = 'driver-222'; + entity.lastLoginAt = recentLogin; + entity.createdAt = now; + entity.updatedAt = now; + + // Assert + expect(entity.lastLoginAt).toBe(recentLogin); + expect(entity.lastLoginAt!.getTime()).toBeLessThan(now.getTime()); + }); + + it('should handle user with old login', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const now = new Date(); + const oldLogin = new Date(now.getTime() - 86400000); // 1 day ago + + // Act + entity.id = 'user-333'; + entity.email = 'old@example.com'; + entity.displayName = 'Old Login User'; + entity.roles = ['user']; + entity.status = 'active'; + entity.lastLoginAt = oldLogin; + entity.createdAt = now; + entity.updatedAt = now; + + // Assert + expect(entity.lastLoginAt).toBe(oldLogin); + expect(entity.lastLoginAt!.getTime()).toBeLessThan(now.getTime()); + }); + }); + + describe('edge cases', () => { + it('should handle empty string values', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act + entity.id = ''; + entity.email = ''; + entity.displayName = ''; + entity.status = ''; + + // Assert + expect(entity.id).toBe(''); + expect(entity.email).toBe(''); + expect(entity.displayName).toBe(''); + expect(entity.status).toBe(''); + }); + + it('should handle empty roles array', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act + entity.roles = []; + + // Assert + expect(entity.roles).toEqual([]); + expect(entity.roles).toHaveLength(0); + }); + + it('should handle null values for optional properties', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act + entity.primaryDriverId = null as any; + entity.lastLoginAt = null as any; + + // Assert + expect(entity.primaryDriverId).toBeNull(); + expect(entity.lastLoginAt).toBeNull(); + }); + + it('should handle very long strings', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + const longString = 'a'.repeat(1000); + + // Act + entity.email = `${longString}@example.com`; + entity.displayName = longString; + + // Assert + expect(entity.email).toBe(`${longString}@example.com`); + expect(entity.displayName).toBe(longString); + }); + + it('should handle unicode characters', () => { + // Arrange + const entity = new AdminUserOrmEntity(); + + // Act + entity.email = '用户@例子.测试'; + entity.displayName = '用户 例子'; + + // Assert + expect(entity.email).toBe('用户@例子.测试'); + expect(entity.displayName).toBe('用户 例子'); + }); + }); + }); +}); diff --git a/core/admin/infrastructure/typeorm/errors/TypeOrmAdminSchemaError.test.ts b/core/admin/infrastructure/typeorm/errors/TypeOrmAdminSchemaError.test.ts new file mode 100644 index 000000000..9fd0bfa83 --- /dev/null +++ b/core/admin/infrastructure/typeorm/errors/TypeOrmAdminSchemaError.test.ts @@ -0,0 +1,521 @@ +import { describe, expect, it } from 'vitest'; +import { TypeOrmAdminSchemaError } from './TypeOrmAdminSchemaError'; + +describe('TypeOrmAdminSchemaError', () => { + describe('TDD - Test First', () => { + describe('constructor', () => { + it('should create an error with all required details', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid format', + message: 'Email must be a valid email address', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.name).toBe('TypeOrmAdminSchemaError'); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: Invalid format - Email must be a valid email address'); + }); + + it('should create an error with minimal details', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'id', + reason: 'Missing', + message: 'ID field is required', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.id: Missing - ID field is required'); + }); + + it('should create an error with complex entity name', () => { + // Arrange + const details = { + entityName: 'AdminUserOrmEntity', + fieldName: 'roles', + reason: 'Type mismatch', + message: 'Expected simple-json but got text', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUserOrmEntity.roles: Type mismatch - Expected simple-json but got text'); + }); + + it('should create an error with long field name', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'veryLongFieldNameThatExceedsNormalLength', + reason: 'Constraint violation', + message: 'Field length exceeds maximum allowed', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.veryLongFieldNameThatExceedsNormalLength: Constraint violation - Field length exceeds maximum allowed'); + }); + + it('should create an error with special characters in message', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Validation failed', + message: 'Email "test@example.com" contains invalid characters: @, ., com', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: Validation failed - Email "test@example.com" contains invalid characters: @, ., com'); + }); + + it('should create an error with empty reason', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: '', + message: 'Email is required', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: - Email is required'); + }); + + it('should create an error with empty message', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: '', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: Invalid - '); + }); + + it('should create an error with empty reason and message', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: '', + message: '', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: - '); + }); + }); + + describe('error properties', () => { + it('should have correct error name', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'Test error', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.name).toBe('TypeOrmAdminSchemaError'); + }); + + it('should be instance of Error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'Test error', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error instanceof Error).toBe(true); + expect(error instanceof TypeOrmAdminSchemaError).toBe(true); + }); + + it('should have a stack trace', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'Test error', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.stack).toBeDefined(); + expect(typeof error.stack).toBe('string'); + expect(error.stack).toContain('TypeOrmAdminSchemaError'); + }); + + it('should preserve details object reference', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'Test error', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toBe(details); + }); + + it('should allow modification of details after creation', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'Test error', + }; + + const error = new TypeOrmAdminSchemaError(details); + + // Act + error.details.reason = 'Updated reason'; + + // Assert + expect(error.details.reason).toBe('Updated reason'); + expect(error.message).toContain('Updated reason'); + }); + }); + + describe('message formatting', () => { + it('should format message with all parts', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Validation failed', + message: 'Email must be a valid email address', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: Validation failed - Email must be a valid email address'); + }); + + it('should handle multiple words in entity name', () => { + // Arrange + const details = { + entityName: 'Admin User Entity', + fieldName: 'email', + reason: 'Invalid', + message: 'Test', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.message).toBe('[TypeOrmAdminSchemaError] Admin User Entity.email: Invalid - Test'); + }); + + it('should handle multiple words in field name', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email address', + reason: 'Invalid', + message: 'Test', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email address: Invalid - Test'); + }); + + it('should handle multiple words in reason', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Validation failed completely', + message: 'Test', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: Validation failed completely - Test'); + }); + + it('should handle multiple words in message', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'This is a very long error message that contains many words', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: Invalid - This is a very long error message that contains many words'); + }); + + it('should handle special characters in all parts', () => { + // Arrange + const details = { + entityName: 'Admin_User-Entity', + fieldName: 'email@address', + reason: 'Validation failed: @, ., com', + message: 'Email "test@example.com" is invalid', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.message).toBe('[TypeOrmAdminSchemaError] Admin_User-Entity.email@address: Validation failed: @, ., com - Email "test@example.com" is invalid'); + }); + }); + + describe('error inheritance', () => { + it('should be instance of Error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'Test error', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error instanceof Error).toBe(true); + }); + + it('should be instance of TypeOrmAdminSchemaError', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'Test error', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error instanceof TypeOrmAdminSchemaError).toBe(true); + }); + + it('should not be instance of other error types', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Invalid', + message: 'Test error', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error instanceof TypeError).toBe(false); + expect(error instanceof RangeError).toBe(false); + expect(error instanceof ReferenceError).toBe(false); + }); + }); + + describe('real-world scenarios', () => { + it('should handle missing column error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'primaryDriverId', + reason: 'Column not found', + message: 'Column "primary_driver_id" does not exist in table "admin_users"', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.primaryDriverId: Column not found - Column "primary_driver_id" does not exist in table "admin_users"'); + }); + + it('should handle type mismatch error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'roles', + reason: 'Type mismatch', + message: 'Expected type "simple-json" but got "text" for column "roles"', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.roles: Type mismatch - Expected type "simple-json" but got "text" for column "roles"'); + }); + + it('should handle constraint violation error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Constraint violation', + message: 'UNIQUE constraint failed: admin_users.email', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: Constraint violation - UNIQUE constraint failed: admin_users.email'); + }); + + it('should handle nullable constraint error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'displayName', + reason: 'Constraint violation', + message: 'NOT NULL constraint failed: admin_users.display_name', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.displayName: Constraint violation - NOT NULL constraint failed: admin_users.display_name'); + }); + + it('should handle foreign key constraint error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'primaryDriverId', + reason: 'Constraint violation', + message: 'FOREIGN KEY constraint failed: admin_users.primary_driver_id references drivers.id', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.primaryDriverId: Constraint violation - FOREIGN KEY constraint failed: admin_users.primary_driver_id references drivers.id'); + }); + + it('should handle index creation error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'email', + reason: 'Index creation failed', + message: 'Failed to create unique index on column "email"', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.email: Index creation failed - Failed to create unique index on column "email"'); + }); + + it('should handle default value error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'status', + reason: 'Default value error', + message: 'Default value "active" is not valid for column "status"', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.status: Default value error - Default value "active" is not valid for column "status"'); + }); + + it('should handle timestamp column error', () => { + // Arrange + const details = { + entityName: 'AdminUser', + fieldName: 'createdAt', + reason: 'Type error', + message: 'Column "created_at" has invalid type "datetime" for PostgreSQL', + }; + + // Act + const error = new TypeOrmAdminSchemaError(details); + + // Assert + expect(error.details).toEqual(details); + expect(error.message).toBe('[TypeOrmAdminSchemaError] AdminUser.createdAt: Type error - Column "created_at" has invalid type "datetime" for PostgreSQL'); + }); + }); + }); +});