fix adapters
This commit is contained in:
@@ -0,0 +1,56 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import type { UserAchievement } from '@core/identity';
|
||||
import { InMemoryAchievementRepository } from './InMemoryAchievementRepository';
|
||||
|
||||
describe('InMemoryAchievementRepository (identity)', () => {
|
||||
let repository: InMemoryAchievementRepository;
|
||||
let logger: Logger;
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
repository = new InMemoryAchievementRepository(logger);
|
||||
});
|
||||
|
||||
it('seeds predefined achievements', async () => {
|
||||
const all = await repository.findAllAchievements();
|
||||
expect(all.length).toBeGreaterThan(0);
|
||||
|
||||
const first = all[0]!;
|
||||
const found = await repository.findAchievementById(first.id);
|
||||
expect(found?.id).toBe(first.id);
|
||||
});
|
||||
|
||||
it('creates and queries user achievements and stats', async () => {
|
||||
const all = await repository.findAllAchievements();
|
||||
const achievement = all[0]!;
|
||||
const userId = 'user-1';
|
||||
|
||||
const ua: UserAchievement = {
|
||||
id: 'ua-1',
|
||||
userId,
|
||||
achievementId: achievement.id,
|
||||
isComplete: () => true,
|
||||
} as unknown as UserAchievement;
|
||||
|
||||
await repository.createUserAchievement(ua);
|
||||
|
||||
expect(await repository.hasUserEarnedAchievement(userId, achievement.id)).toBe(true);
|
||||
|
||||
const leaderboard = await repository.getAchievementLeaderboard(10);
|
||||
expect(leaderboard.length).toBe(1);
|
||||
expect(leaderboard[0]?.userId).toBe(userId);
|
||||
expect(leaderboard[0]?.count).toBe(1);
|
||||
|
||||
const stats = await repository.getUserAchievementStats(userId);
|
||||
expect(stats.total).toBe(1);
|
||||
expect(stats.points).toBeGreaterThan(0);
|
||||
expect(Object.values(stats.byCategory).reduce((a, b) => a + b, 0)).toBe(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { UserId } from '@core/identity';
|
||||
import { User } from '@core/identity/domain/entities/User';
|
||||
import { InMemoryUserRepository } from './InMemoryUserRepository';
|
||||
import { InMemoryAuthRepository } from './InMemoryAuthRepository';
|
||||
import { InMemoryPasswordHashingService } from '../../services/InMemoryPasswordHashingService';
|
||||
|
||||
describe('InMemoryAuthRepository', () => {
|
||||
let mockLogger: Logger;
|
||||
|
||||
beforeEach(() => {
|
||||
mockLogger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
});
|
||||
|
||||
it('creates and verifies a user password', async () => {
|
||||
const userRepo = new InMemoryUserRepository(mockLogger);
|
||||
const passwordService = new InMemoryPasswordHashingService();
|
||||
const authRepo = new InMemoryAuthRepository(userRepo, passwordService, mockLogger);
|
||||
|
||||
const user = User.create({
|
||||
id: UserId.fromString('user-1'),
|
||||
displayName: 'Test User',
|
||||
email: 'test@example.com',
|
||||
});
|
||||
|
||||
const created = await authRepo.create(user, 'password123');
|
||||
expect(created.getEmail()).toBe('test@example.com');
|
||||
|
||||
expect(await authRepo.userExists('test@example.com')).toBe(true);
|
||||
|
||||
const ok = await authRepo.verifyPassword('test@example.com', 'password123');
|
||||
expect(ok).not.toBeNull();
|
||||
expect(ok?.getId().value).toBe('user-1');
|
||||
|
||||
const bad = await authRepo.verifyPassword('test@example.com', 'wrong');
|
||||
expect(bad).toBeNull();
|
||||
});
|
||||
|
||||
it('save updates existing user', async () => {
|
||||
const userRepo = new InMemoryUserRepository(mockLogger);
|
||||
const passwordService = new InMemoryPasswordHashingService();
|
||||
const authRepo = new InMemoryAuthRepository(userRepo, passwordService, mockLogger);
|
||||
|
||||
const user = User.create({
|
||||
id: UserId.fromString('user-2'),
|
||||
displayName: 'User Two',
|
||||
email: 'two@example.com',
|
||||
});
|
||||
|
||||
await authRepo.create(user, 'pw');
|
||||
|
||||
const updated = User.create({
|
||||
id: UserId.fromString('user-2'),
|
||||
displayName: 'User Two Updated',
|
||||
email: 'two@example.com',
|
||||
});
|
||||
|
||||
await authRepo.save(updated);
|
||||
|
||||
const stored = await userRepo.findById('user-2');
|
||||
expect(stored?.displayName).toBe('User Two Updated');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import { UserId, type SponsorAccount } from '@core/identity';
|
||||
import { InMemorySponsorAccountRepository } from './InMemorySponsorAccountRepository';
|
||||
|
||||
describe('InMemorySponsorAccountRepository', () => {
|
||||
let repository: InMemorySponsorAccountRepository;
|
||||
let logger: Logger;
|
||||
|
||||
const makeAccount = (id: string, sponsorId: string, email: string): SponsorAccount => {
|
||||
return {
|
||||
getId: () => ({ value: id }),
|
||||
getSponsorId: () => sponsorId,
|
||||
getEmail: () => email,
|
||||
} as unknown as SponsorAccount;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
logger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
repository = new InMemorySponsorAccountRepository(logger);
|
||||
repository.clear();
|
||||
});
|
||||
|
||||
it('saves and finds sponsor accounts by id / sponsorId / email', async () => {
|
||||
const account = makeAccount('user-1', 'sponsor-1', 'Sponsor@Example.com');
|
||||
|
||||
await repository.save(account);
|
||||
|
||||
expect(await repository.findById(UserId.fromString('user-1'))).toBe(account);
|
||||
expect(await repository.findBySponsorId('sponsor-1')).toBe(account);
|
||||
expect(await repository.findByEmail('sponsor@example.com')).toBe(account);
|
||||
});
|
||||
|
||||
it('deletes sponsor accounts', async () => {
|
||||
const account = makeAccount('user-del', 'sponsor-del', 'del@example.com');
|
||||
await repository.save(account);
|
||||
|
||||
await repository.delete(UserId.fromString('user-del'));
|
||||
expect(await repository.findById(UserId.fromString('user-del'))).toBeNull();
|
||||
});
|
||||
|
||||
it('seeds via constructor', async () => {
|
||||
const seeded = makeAccount('user-seed', 'sponsor-seed', 'seed@example.com');
|
||||
repository = new InMemorySponsorAccountRepository(logger, [seeded]);
|
||||
|
||||
expect(await repository.findBySponsorId('sponsor-seed')).toBe(seeded);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,57 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import type { UserRating } from '@core/identity';
|
||||
import { InMemoryUserRatingRepository } from './InMemoryUserRatingRepository';
|
||||
|
||||
describe('InMemoryUserRatingRepository', () => {
|
||||
let repository: InMemoryUserRatingRepository;
|
||||
let mockLogger: Logger;
|
||||
|
||||
const rating = (userId: string, driverValue: number, trustValue: number, canSteward: boolean): UserRating => {
|
||||
return {
|
||||
userId,
|
||||
driver: { sampleSize: 10, value: driverValue },
|
||||
trust: { sampleSize: 5, value: trustValue },
|
||||
canBeSteward: () => canSteward,
|
||||
getDriverTier: () => (driverValue >= 2400 ? 'elite' : 'rookie'),
|
||||
} as unknown as UserRating;
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
mockLogger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
repository = new InMemoryUserRatingRepository(mockLogger);
|
||||
repository.clear();
|
||||
});
|
||||
|
||||
it('saves and finds ratings', async () => {
|
||||
await repository.save(rating('u1', 2300, 50, false));
|
||||
await repository.save(rating('u2', 2500, 70, true));
|
||||
|
||||
expect((await repository.findByUserId('u1'))?.userId).toBe('u1');
|
||||
expect((await repository.findByUserIds(['u1', 'u2'])).length).toBe(2);
|
||||
|
||||
const topDrivers = await repository.getTopDrivers(1);
|
||||
expect(topDrivers[0]?.userId).toBe('u2');
|
||||
|
||||
const topTrusted = await repository.getTopTrusted(1);
|
||||
expect(topTrusted[0]?.userId).toBe('u2');
|
||||
|
||||
const stewards = await repository.getEligibleStewards();
|
||||
expect(stewards.map(r => r.userId)).toEqual(['u2']);
|
||||
|
||||
const elite = await repository.findByDriverTier('elite');
|
||||
expect(elite.map(r => r.userId)).toEqual(['u2']);
|
||||
});
|
||||
|
||||
it('deletes ratings', async () => {
|
||||
await repository.save(rating('u3', 2200, 10, false));
|
||||
await repository.delete('u3');
|
||||
expect(await repository.findByUserId('u3')).toBeNull();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,69 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
import type { Logger } from '@core/shared/application';
|
||||
import type { StoredUser } from '@core/identity/domain/repositories/IUserRepository';
|
||||
import { InMemoryUserRepository } from './InMemoryUserRepository';
|
||||
|
||||
describe('InMemoryUserRepository', () => {
|
||||
let repository: InMemoryUserRepository;
|
||||
let mockLogger: Logger;
|
||||
|
||||
beforeEach(() => {
|
||||
mockLogger = {
|
||||
debug: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
error: vi.fn(),
|
||||
} as unknown as Logger;
|
||||
|
||||
const seeded: StoredUser[] = [
|
||||
{
|
||||
id: 'u1',
|
||||
email: 'seed@example.com',
|
||||
displayName: 'Seed',
|
||||
passwordHash: 'hash',
|
||||
salt: 'salt',
|
||||
createdAt: new Date('2025-01-01T00:00:00.000Z'),
|
||||
},
|
||||
];
|
||||
|
||||
repository = new InMemoryUserRepository(mockLogger, seeded);
|
||||
});
|
||||
|
||||
it('finds by email and id', async () => {
|
||||
expect((await repository.findByEmail('seed@example.com'))?.id).toBe('u1');
|
||||
expect((await repository.findById('u1'))?.email).toBe('seed@example.com');
|
||||
expect(await repository.findByEmail('missing@example.com')).toBeNull();
|
||||
});
|
||||
|
||||
it('creates and rejects duplicate emails', async () => {
|
||||
const user: StoredUser = {
|
||||
id: 'u2',
|
||||
email: 'new@example.com',
|
||||
displayName: 'New',
|
||||
passwordHash: 'hash2',
|
||||
salt: 'salt2',
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
await repository.create(user);
|
||||
expect(await repository.emailExists('new@example.com')).toBe(true);
|
||||
|
||||
await expect(repository.create({ ...user, id: 'u3' })).rejects.toThrow('Email already exists');
|
||||
});
|
||||
|
||||
it('updates and maintains email index', async () => {
|
||||
const existing = await repository.findById('u1');
|
||||
expect(existing).not.toBeNull();
|
||||
|
||||
const updated: StoredUser = {
|
||||
...(existing as StoredUser),
|
||||
email: 'changed@example.com',
|
||||
displayName: 'Changed',
|
||||
};
|
||||
|
||||
await repository.update(updated);
|
||||
|
||||
expect(await repository.findByEmail('seed@example.com')).toBeNull();
|
||||
expect((await repository.findByEmail('changed@example.com'))?.displayName).toBe('Changed');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user