inmemory to postgres

This commit is contained in:
2025-12-29 20:50:03 +01:00
parent 12ae6e1dad
commit 3f610c1cb6
64 changed files with 3689 additions and 63 deletions

View File

@@ -15,7 +15,7 @@ describe('UserOrmMapper', () => {
entity.email = 'alice@example.com';
entity.displayName = 'Alice';
entity.passwordHash = 'bcrypt-hash';
entity.salt = '';
entity.salt = 'test-salt';
entity.primaryDriverId = null;
entity.createdAt = new Date('2025-01-01T00:00:00.000Z');
@@ -45,7 +45,7 @@ describe('UserOrmMapper', () => {
entity.email = 123 as unknown as string;
entity.displayName = 'Alice';
entity.passwordHash = 'bcrypt-hash';
entity.salt = '';
entity.salt = 'test-salt';
entity.primaryDriverId = null;
entity.createdAt = new Date('2025-01-01T00:00:00.000Z');

View File

@@ -0,0 +1,72 @@
import { describe, expect, it, vi } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import type { DataSource, Repository } from 'typeorm';
import { TypeOrmAuthRepository } from './TypeOrmAuthRepository';
import { UserOrmEntity } from '../entities/UserOrmEntity';
import { UserOrmMapper } from '../mappers/UserOrmMapper';
import { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress';
import { User } from '@core/identity/domain/entities/User';
import { PasswordHash } from '@core/identity/domain/value-objects/PasswordHash';
import { UserId } from '@core/identity/domain/value-objects/UserId';
describe('TypeOrmAuthRepository', () => {
it('does not construct its own mapper dependencies', () => {
const sourcePath = path.resolve(__dirname, 'TypeOrmAuthRepository.ts');
const source = fs.readFileSync(sourcePath, 'utf8');
expect(source).not.toMatch(/new\s+UserOrmMapper\s*\(/);
expect(source).not.toMatch(/=\s*new\s+UserOrmMapper\s*\(/);
});
it('requires mapper injection via constructor (no default mapper)', () => {
expect(TypeOrmAuthRepository.length).toBe(2);
});
it('uses the injected mapper at runtime (DB-free)', async () => {
const ormRepo = {
findOne: vi.fn().mockResolvedValue({ id: 'u1', email: 'test@example.com' }),
save: vi.fn().mockResolvedValue({ id: 'u1' }),
} as unknown as Repository<UserOrmEntity>;
const dataSource = {
getRepository: vi.fn().mockReturnValue(ormRepo),
} as unknown as DataSource;
const mapper = {
toDomain: vi.fn().mockReturnValue({
getId: () => ({ value: 'u1' }),
getEmail: () => 'test@example.com',
getDisplayName: () => 'Test User',
getPasswordHash: () => ({ value: 'hash' }),
getPrimaryDriverId: () => null,
}),
toOrmEntity: vi.fn().mockReturnValue({ id: 'u1', email: 'test@example.com' }),
} as unknown as UserOrmMapper;
const repo = new TypeOrmAuthRepository(dataSource, mapper);
// Test findByEmail
const email = EmailAddress.create('TEST@EXAMPLE.COM');
const user = await repo.findByEmail(email);
expect(dataSource.getRepository).toHaveBeenCalledTimes(1);
expect(ormRepo.findOne).toHaveBeenCalledWith({ where: { email: 'test@example.com' } });
expect(mapper.toDomain).toHaveBeenCalledTimes(1);
expect(user).toBeDefined();
// Test save
const userId = UserId.create();
const passwordHash = PasswordHash.fromHash('hash');
const domainUser = User.create({
id: userId,
email: 'test@example.com',
displayName: 'Test User',
passwordHash,
});
await repo.save(domainUser);
expect(ormRepo.save).toHaveBeenCalled();
});
});

View File

@@ -4,8 +4,8 @@ import { User } from '@core/identity/domain/entities/User';
import type { IAuthRepository } from '@core/identity/domain/repositories/IAuthRepository';
import type { EmailAddress } from '@core/identity/domain/value-objects/EmailAddress';
import { UserOrmEntity } from './entities/UserOrmEntity';
import { UserOrmMapper } from './mappers/UserOrmMapper';
import { UserOrmEntity } from '../entities/UserOrmEntity';
import { UserOrmMapper } from '../mappers/UserOrmMapper';
export class TypeOrmAuthRepository implements IAuthRepository {
constructor(

View File

@@ -1,8 +1,11 @@
import { describe, expect, it, vi } from 'vitest';
import * as fs from 'node:fs';
import * as path from 'node:path';
import type { DataSource, Repository } from 'typeorm';
import { TypeOrmUserRepository } from './TypeOrmUserRepository';
import { UserOrmEntity } from '../entities/UserOrmEntity';
import { UserOrmMapper } from '../mappers/UserOrmMapper';
describe('TypeOrmUserRepository', () => {
it('does not construct its own mapper dependencies', () => {
@@ -20,18 +23,18 @@ describe('TypeOrmUserRepository', () => {
it('uses the injected mapper at runtime (DB-free)', async () => {
const ormRepo = {
findOne: vi.fn().mockResolvedValue({ id: 'u1' }),
};
} as unknown as Repository<UserOrmEntity>;
const dataSource = {
getRepository: vi.fn().mockReturnValue(ormRepo),
};
} as unknown as DataSource;
const mapper = {
toStored: vi.fn().mockReturnValue({ id: 'stored-u1' }),
toOrmEntity: vi.fn(),
};
} as unknown as UserOrmMapper;
const repo = new TypeOrmUserRepository(dataSource as any, mapper as any);
const repo = new TypeOrmUserRepository(dataSource, mapper);
const user = await repo.findByEmail('ALICE@EXAMPLE.COM');

View File

@@ -2,8 +2,8 @@ import type { DataSource } from 'typeorm';
import type { IUserRepository, StoredUser } from '@core/identity/domain/repositories/IUserRepository';
import { UserOrmEntity } from './entities/UserOrmEntity';
import { UserOrmMapper } from './mappers/UserOrmMapper';
import { UserOrmEntity } from '../entities/UserOrmEntity';
import { UserOrmMapper } from '../mappers/UserOrmMapper';
export class TypeOrmUserRepository implements IUserRepository {
constructor(