resolve todos in website
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { GetCurrentUserSessionUseCase } from './GetCurrentUserSessionUseCase';
|
||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||
import type { AuthSessionDTO } from '../dto/AuthSessionDTO';
|
||||
|
||||
describe('GetCurrentUserSessionUseCase', () => {
|
||||
let sessionPort: {
|
||||
getCurrentSession: Mock;
|
||||
createSession: Mock;
|
||||
clearSession: Mock;
|
||||
};
|
||||
|
||||
let useCase: GetCurrentUserSessionUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
sessionPort = {
|
||||
getCurrentSession: vi.fn(),
|
||||
createSession: vi.fn(),
|
||||
clearSession: vi.fn(),
|
||||
};
|
||||
|
||||
useCase = new GetCurrentUserSessionUseCase(sessionPort as unknown as IdentitySessionPort);
|
||||
});
|
||||
|
||||
it('returns the current auth session when one exists', async () => {
|
||||
const session: AuthSessionDTO = {
|
||||
user: {
|
||||
id: 'user-1',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
primaryDriverId: 'driver-1',
|
||||
},
|
||||
issuedAt: Date.now(),
|
||||
expiresAt: Date.now() + 1000,
|
||||
token: 'token-123',
|
||||
};
|
||||
|
||||
sessionPort.getCurrentSession.mockResolvedValue(session);
|
||||
|
||||
const result = await useCase.execute();
|
||||
|
||||
expect(sessionPort.getCurrentSession).toHaveBeenCalledTimes(1);
|
||||
expect(result).toEqual(session);
|
||||
});
|
||||
|
||||
it('returns null when there is no active session', async () => {
|
||||
sessionPort.getCurrentSession.mockResolvedValue(null);
|
||||
|
||||
const result = await useCase.execute();
|
||||
|
||||
expect(sessionPort.getCurrentSession).toHaveBeenCalledTimes(1);
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
48
core/identity/application/use-cases/GetUserUseCase.test.ts
Normal file
48
core/identity/application/use-cases/GetUserUseCase.test.ts
Normal file
@@ -0,0 +1,48 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { GetUserUseCase } from './GetUserUseCase';
|
||||
import { User } from '../../domain/entities/User';
|
||||
import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
|
||||
|
||||
describe('GetUserUseCase', () => {
|
||||
let userRepository: {
|
||||
findById: Mock;
|
||||
};
|
||||
|
||||
let useCase: GetUserUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
userRepository = {
|
||||
findById: vi.fn(),
|
||||
};
|
||||
|
||||
useCase = new GetUserUseCase(userRepository as unknown as IUserRepository);
|
||||
});
|
||||
|
||||
it('returns a User when the user exists', async () => {
|
||||
const storedUser: StoredUser = {
|
||||
id: 'user-1',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
passwordHash: 'hash',
|
||||
salt: 'salt',
|
||||
primaryDriverId: 'driver-1',
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
userRepository.findById.mockResolvedValue(storedUser);
|
||||
|
||||
const result = await useCase.execute('user-1');
|
||||
|
||||
expect(userRepository.findById).toHaveBeenCalledWith('user-1');
|
||||
expect(result).toBeInstanceOf(User);
|
||||
expect(result.getId().value).toBe('user-1');
|
||||
expect(result.getDisplayName()).toBe('Test User');
|
||||
});
|
||||
|
||||
it('throws when the user does not exist', async () => {
|
||||
userRepository.findById.mockResolvedValue(null);
|
||||
|
||||
await expect(useCase.execute('missing-user')).rejects.toThrow('User not found');
|
||||
expect(userRepository.findById).toHaveBeenCalledWith('missing-user');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,65 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { HandleAuthCallbackUseCase } from './HandleAuthCallbackUseCase';
|
||||
import type { IdentityProviderPort } from '../ports/IdentityProviderPort';
|
||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||
import type { AuthCallbackCommandDTO } from '../dto/AuthCallbackCommandDTO';
|
||||
import type { AuthenticatedUserDTO } from '../dto/AuthenticatedUserDTO';
|
||||
import type { AuthSessionDTO } from '../dto/AuthSessionDTO';
|
||||
|
||||
describe('HandleAuthCallbackUseCase', () => {
|
||||
let provider: {
|
||||
completeAuth: Mock;
|
||||
};
|
||||
let sessionPort: {
|
||||
createSession: Mock;
|
||||
getCurrentSession: Mock;
|
||||
clearSession: Mock;
|
||||
};
|
||||
let useCase: HandleAuthCallbackUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
provider = {
|
||||
completeAuth: vi.fn(),
|
||||
};
|
||||
sessionPort = {
|
||||
createSession: vi.fn(),
|
||||
getCurrentSession: vi.fn(),
|
||||
clearSession: vi.fn(),
|
||||
};
|
||||
|
||||
useCase = new HandleAuthCallbackUseCase(
|
||||
provider as unknown as IdentityProviderPort,
|
||||
sessionPort as unknown as IdentitySessionPort,
|
||||
);
|
||||
});
|
||||
|
||||
it('completes auth and creates a session', async () => {
|
||||
const command: AuthCallbackCommandDTO = {
|
||||
code: 'auth-code',
|
||||
state: 'state-123',
|
||||
redirectUri: 'https://app/callback',
|
||||
};
|
||||
|
||||
const user: AuthenticatedUserDTO = {
|
||||
id: 'user-1',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
};
|
||||
|
||||
const session: AuthSessionDTO = {
|
||||
user,
|
||||
issuedAt: Date.now(),
|
||||
expiresAt: Date.now() + 1000,
|
||||
token: 'session-token',
|
||||
};
|
||||
|
||||
provider.completeAuth.mockResolvedValue(user);
|
||||
sessionPort.createSession.mockResolvedValue(session);
|
||||
|
||||
const result = await useCase.execute(command);
|
||||
|
||||
expect(provider.completeAuth).toHaveBeenCalledWith(command);
|
||||
expect(sessionPort.createSession).toHaveBeenCalledWith(user);
|
||||
expect(result).toEqual(session);
|
||||
});
|
||||
});
|
||||
81
core/identity/application/use-cases/LoginUseCase.test.ts
Normal file
81
core/identity/application/use-cases/LoginUseCase.test.ts
Normal file
@@ -0,0 +1,81 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { LoginUseCase } from './LoginUseCase';
|
||||
import { EmailAddress } from '../../domain/value-objects/EmailAddress';
|
||||
import type { IAuthRepository } from '../../domain/repositories/IAuthRepository';
|
||||
import type { IPasswordHashingService } from '../../domain/services/PasswordHashingService';
|
||||
import { User } from '../../domain/entities/User';
|
||||
|
||||
describe('LoginUseCase', () => {
|
||||
let authRepo: {
|
||||
findByEmail: Mock;
|
||||
};
|
||||
let passwordService: {
|
||||
verify: Mock;
|
||||
};
|
||||
let useCase: LoginUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
authRepo = {
|
||||
findByEmail: vi.fn(),
|
||||
};
|
||||
passwordService = {
|
||||
verify: vi.fn(),
|
||||
};
|
||||
useCase = new LoginUseCase(
|
||||
authRepo as unknown as IAuthRepository,
|
||||
passwordService as unknown as IPasswordHashingService,
|
||||
);
|
||||
});
|
||||
|
||||
it('returns the user when credentials are valid', async () => {
|
||||
const email = 'test@example.com';
|
||||
const password = 'password123';
|
||||
const emailVO = EmailAddress.create(email);
|
||||
|
||||
const user = User.create({
|
||||
id: { value: 'user-1' } as any,
|
||||
displayName: 'Test User',
|
||||
email: emailVO.value,
|
||||
});
|
||||
|
||||
(user as any).getPasswordHash = () => ({ value: 'stored-hash' });
|
||||
|
||||
authRepo.findByEmail.mockResolvedValue(user);
|
||||
passwordService.verify.mockResolvedValue(true);
|
||||
|
||||
const result = await useCase.execute(email, password);
|
||||
|
||||
expect(authRepo.findByEmail).toHaveBeenCalledWith(emailVO);
|
||||
expect(passwordService.verify).toHaveBeenCalledWith(password, 'stored-hash');
|
||||
expect(result).toBe(user);
|
||||
});
|
||||
|
||||
it('throws when user is not found', async () => {
|
||||
const email = 'missing@example.com';
|
||||
|
||||
authRepo.findByEmail.mockResolvedValue(null);
|
||||
|
||||
await expect(useCase.execute(email, 'password')).rejects.toThrow('Invalid credentials');
|
||||
});
|
||||
|
||||
it('throws when password is invalid', async () => {
|
||||
const email = 'test@example.com';
|
||||
const password = 'wrong-password';
|
||||
const emailVO = EmailAddress.create(email);
|
||||
|
||||
const user = User.create({
|
||||
id: { value: 'user-1' } as any,
|
||||
displayName: 'Test User',
|
||||
email: emailVO.value,
|
||||
});
|
||||
|
||||
(user as any).getPasswordHash = () => ({ value: 'stored-hash' });
|
||||
|
||||
authRepo.findByEmail.mockResolvedValue(user);
|
||||
passwordService.verify.mockResolvedValue(false);
|
||||
|
||||
await expect(useCase.execute(email, password)).rejects.toThrow('Invalid credentials');
|
||||
expect(authRepo.findByEmail).toHaveBeenCalled();
|
||||
expect(passwordService.verify).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,108 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { LoginWithEmailUseCase, type LoginCommandDTO } from './LoginWithEmailUseCase';
|
||||
import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
|
||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||
import type { AuthSessionDTO } from '../dto/AuthSessionDTO';
|
||||
|
||||
describe('LoginWithEmailUseCase', () => {
|
||||
let userRepository: {
|
||||
findByEmail: Mock;
|
||||
};
|
||||
let sessionPort: {
|
||||
createSession: Mock;
|
||||
getCurrentSession: Mock;
|
||||
clearSession: Mock;
|
||||
};
|
||||
let useCase: LoginWithEmailUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
userRepository = {
|
||||
findByEmail: vi.fn(),
|
||||
};
|
||||
sessionPort = {
|
||||
createSession: vi.fn(),
|
||||
getCurrentSession: vi.fn(),
|
||||
clearSession: vi.fn(),
|
||||
};
|
||||
useCase = new LoginWithEmailUseCase(
|
||||
userRepository as unknown as IUserRepository,
|
||||
sessionPort as unknown as IdentitySessionPort,
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a session for valid credentials', async () => {
|
||||
const command: LoginCommandDTO = {
|
||||
email: 'Test@Example.com',
|
||||
password: 'password123',
|
||||
};
|
||||
|
||||
const storedUser: StoredUser = {
|
||||
id: 'user-1',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
passwordHash: 'hashed-password',
|
||||
salt: 'salt',
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
const session: AuthSessionDTO = {
|
||||
user: {
|
||||
id: storedUser.id,
|
||||
email: storedUser.email,
|
||||
displayName: storedUser.displayName,
|
||||
},
|
||||
issuedAt: Date.now(),
|
||||
expiresAt: Date.now() + 1000,
|
||||
token: 'token-123',
|
||||
};
|
||||
|
||||
userRepository.findByEmail.mockResolvedValue(storedUser);
|
||||
sessionPort.createSession.mockResolvedValue(session);
|
||||
|
||||
const result = await useCase.execute(command);
|
||||
|
||||
expect(userRepository.findByEmail).toHaveBeenCalledWith('test@example.com');
|
||||
expect(sessionPort.createSession).toHaveBeenCalledWith({
|
||||
id: storedUser.id,
|
||||
email: storedUser.email,
|
||||
displayName: storedUser.displayName,
|
||||
});
|
||||
expect(result).toEqual(session);
|
||||
});
|
||||
|
||||
it('throws when email or password is missing', async () => {
|
||||
await expect(useCase.execute({ email: '', password: 'x' })).rejects.toThrow('Email and password are required');
|
||||
await expect(useCase.execute({ email: 'a@example.com', password: '' })).rejects.toThrow('Email and password are required');
|
||||
});
|
||||
|
||||
it('throws when user does not exist', async () => {
|
||||
const command: LoginCommandDTO = {
|
||||
email: 'missing@example.com',
|
||||
password: 'password',
|
||||
};
|
||||
|
||||
userRepository.findByEmail.mockResolvedValue(null);
|
||||
|
||||
await expect(useCase.execute(command)).rejects.toThrow('Invalid email or password');
|
||||
});
|
||||
|
||||
it('throws when password is invalid', async () => {
|
||||
const command: LoginCommandDTO = {
|
||||
email: 'test@example.com',
|
||||
password: 'wrong',
|
||||
};
|
||||
|
||||
const storedUser: StoredUser = {
|
||||
id: 'user-1',
|
||||
email: 'test@example.com',
|
||||
displayName: 'Test User',
|
||||
passwordHash: 'different-hash',
|
||||
salt: 'salt',
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
userRepository.findByEmail.mockResolvedValue(storedUser);
|
||||
|
||||
await expect(useCase.execute(command)).rejects.toThrow('Invalid email or password');
|
||||
});
|
||||
});
|
||||
28
core/identity/application/use-cases/LogoutUseCase.test.ts
Normal file
28
core/identity/application/use-cases/LogoutUseCase.test.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { LogoutUseCase } from './LogoutUseCase';
|
||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||
|
||||
describe('LogoutUseCase', () => {
|
||||
let sessionPort: {
|
||||
clearSession: Mock;
|
||||
getCurrentSession: Mock;
|
||||
createSession: Mock;
|
||||
};
|
||||
let useCase: LogoutUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
sessionPort = {
|
||||
clearSession: vi.fn(),
|
||||
getCurrentSession: vi.fn(),
|
||||
createSession: vi.fn(),
|
||||
};
|
||||
|
||||
useCase = new LogoutUseCase(sessionPort as unknown as IdentitySessionPort);
|
||||
});
|
||||
|
||||
it('clears the current session', async () => {
|
||||
await useCase.execute();
|
||||
|
||||
expect(sessionPort.clearSession).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
71
core/identity/application/use-cases/SignupUseCase.test.ts
Normal file
71
core/identity/application/use-cases/SignupUseCase.test.ts
Normal file
@@ -0,0 +1,71 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { SignupUseCase } from './SignupUseCase';
|
||||
import { EmailAddress } from '../../domain/value-objects/EmailAddress';
|
||||
import { UserId } from '../../domain/value-objects/UserId';
|
||||
import { User } from '../../domain/entities/User';
|
||||
import type { IAuthRepository } from '../../domain/repositories/IAuthRepository';
|
||||
import type { IPasswordHashingService } from '../../domain/services/PasswordHashingService';
|
||||
|
||||
vi.mock('../../domain/value-objects/PasswordHash', () => ({
|
||||
PasswordHash: {
|
||||
fromHash: (hash: string) => ({ value: hash }),
|
||||
},
|
||||
}));
|
||||
|
||||
describe('SignupUseCase', () => {
|
||||
let authRepo: {
|
||||
findByEmail: Mock;
|
||||
save: Mock;
|
||||
};
|
||||
let passwordService: {
|
||||
hash: Mock;
|
||||
};
|
||||
let useCase: SignupUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
authRepo = {
|
||||
findByEmail: vi.fn(),
|
||||
save: vi.fn(),
|
||||
};
|
||||
passwordService = {
|
||||
hash: vi.fn(),
|
||||
};
|
||||
|
||||
useCase = new SignupUseCase(
|
||||
authRepo as unknown as IAuthRepository,
|
||||
passwordService as unknown as IPasswordHashingService,
|
||||
);
|
||||
});
|
||||
|
||||
it('creates and saves a new user when email is free', async () => {
|
||||
const email = 'new@example.com';
|
||||
const password = 'password123';
|
||||
const displayName = 'New User';
|
||||
|
||||
authRepo.findByEmail.mockResolvedValue(null);
|
||||
passwordService.hash.mockResolvedValue('hashed-password');
|
||||
|
||||
const result = await useCase.execute(email, password, displayName);
|
||||
|
||||
expect(authRepo.findByEmail).toHaveBeenCalledWith(EmailAddress.create(email));
|
||||
expect(passwordService.hash).toHaveBeenCalledWith(password);
|
||||
expect(authRepo.save).toHaveBeenCalled();
|
||||
|
||||
expect(result).toBeInstanceOf(User);
|
||||
expect(result.getDisplayName()).toBe(displayName);
|
||||
});
|
||||
|
||||
it('throws when user already exists', async () => {
|
||||
const email = 'existing@example.com';
|
||||
|
||||
const existingUser = User.create({
|
||||
id: UserId.create(),
|
||||
displayName: 'Existing User',
|
||||
email,
|
||||
});
|
||||
|
||||
authRepo.findByEmail.mockResolvedValue(existingUser);
|
||||
|
||||
await expect(useCase.execute(email, 'password', 'Existing User')).rejects.toThrow('User already exists');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,121 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { SignupWithEmailUseCase, type SignupCommandDTO } from './SignupWithEmailUseCase';
|
||||
import type { IUserRepository, StoredUser } from '../../domain/repositories/IUserRepository';
|
||||
import type { IdentitySessionPort } from '../ports/IdentitySessionPort';
|
||||
import type { AuthSessionDTO } from '../dto/AuthSessionDTO';
|
||||
|
||||
describe('SignupWithEmailUseCase', () => {
|
||||
let userRepository: {
|
||||
findByEmail: Mock;
|
||||
create: Mock;
|
||||
};
|
||||
let sessionPort: {
|
||||
createSession: Mock;
|
||||
getCurrentSession: Mock;
|
||||
clearSession: Mock;
|
||||
};
|
||||
let useCase: SignupWithEmailUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
userRepository = {
|
||||
findByEmail: vi.fn(),
|
||||
create: vi.fn(),
|
||||
};
|
||||
sessionPort = {
|
||||
createSession: vi.fn(),
|
||||
getCurrentSession: vi.fn(),
|
||||
clearSession: vi.fn(),
|
||||
};
|
||||
useCase = new SignupWithEmailUseCase(
|
||||
userRepository as unknown as IUserRepository,
|
||||
sessionPort as unknown as IdentitySessionPort,
|
||||
);
|
||||
});
|
||||
|
||||
it('creates a new user and session for valid input', async () => {
|
||||
const command: SignupCommandDTO = {
|
||||
email: 'new@example.com',
|
||||
password: 'password123',
|
||||
displayName: 'New User',
|
||||
};
|
||||
|
||||
userRepository.findByEmail.mockResolvedValue(null);
|
||||
|
||||
const session: AuthSessionDTO = {
|
||||
user: {
|
||||
id: 'user-1',
|
||||
email: command.email.toLowerCase(),
|
||||
displayName: command.displayName,
|
||||
},
|
||||
issuedAt: Date.now(),
|
||||
expiresAt: Date.now() + 1000,
|
||||
token: 'session-token',
|
||||
};
|
||||
|
||||
sessionPort.createSession.mockResolvedValue(session);
|
||||
|
||||
const result = await useCase.execute(command);
|
||||
|
||||
expect(userRepository.findByEmail).toHaveBeenCalledWith(command.email);
|
||||
expect(userRepository.create).toHaveBeenCalled();
|
||||
expect(sessionPort.createSession).toHaveBeenCalledWith({
|
||||
id: expect.any(String),
|
||||
email: command.email.toLowerCase(),
|
||||
displayName: command.displayName,
|
||||
});
|
||||
|
||||
expect(result.session).toEqual(session);
|
||||
expect(result.isNewUser).toBe(true);
|
||||
});
|
||||
|
||||
it('throws when email format is invalid', async () => {
|
||||
const command: SignupCommandDTO = {
|
||||
email: 'invalid-email',
|
||||
password: 'password123',
|
||||
displayName: 'User',
|
||||
};
|
||||
|
||||
await expect(useCase.execute(command)).rejects.toThrow('Invalid email format');
|
||||
});
|
||||
|
||||
it('throws when password is too short', async () => {
|
||||
const command: SignupCommandDTO = {
|
||||
email: 'valid@example.com',
|
||||
password: 'short',
|
||||
displayName: 'User',
|
||||
};
|
||||
|
||||
await expect(useCase.execute(command)).rejects.toThrow('Password must be at least 8 characters');
|
||||
});
|
||||
|
||||
it('throws when display name is too short', async () => {
|
||||
const command: SignupCommandDTO = {
|
||||
email: 'valid@example.com',
|
||||
password: 'password123',
|
||||
displayName: ' ',
|
||||
};
|
||||
|
||||
await expect(useCase.execute(command)).rejects.toThrow('Display name must be at least 2 characters');
|
||||
});
|
||||
|
||||
it('throws when email already exists', async () => {
|
||||
const command: SignupCommandDTO = {
|
||||
email: 'existing@example.com',
|
||||
password: 'password123',
|
||||
displayName: 'Existing User',
|
||||
};
|
||||
|
||||
const existingUser: StoredUser = {
|
||||
id: 'user-1',
|
||||
email: command.email,
|
||||
displayName: command.displayName,
|
||||
passwordHash: 'hash',
|
||||
salt: 'salt',
|
||||
createdAt: new Date(),
|
||||
};
|
||||
|
||||
userRepository.findByEmail.mockResolvedValue(existingUser);
|
||||
|
||||
await expect(useCase.execute(command)).rejects.toThrow('An account with this email already exists');
|
||||
});
|
||||
});
|
||||
35
core/identity/application/use-cases/StartAuthUseCase.test.ts
Normal file
35
core/identity/application/use-cases/StartAuthUseCase.test.ts
Normal file
@@ -0,0 +1,35 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { StartAuthUseCase } from './StartAuthUseCase';
|
||||
import type { IdentityProviderPort } from '../ports/IdentityProviderPort';
|
||||
import type { StartAuthCommandDTO } from '../dto/StartAuthCommandDTO';
|
||||
|
||||
describe('StartAuthUseCase', () => {
|
||||
let provider: {
|
||||
startAuth: Mock;
|
||||
};
|
||||
let useCase: StartAuthUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
provider = {
|
||||
startAuth: vi.fn(),
|
||||
};
|
||||
|
||||
useCase = new StartAuthUseCase(provider as unknown as IdentityProviderPort);
|
||||
});
|
||||
|
||||
it('delegates to the identity provider to start auth', async () => {
|
||||
const command: StartAuthCommandDTO = {
|
||||
redirectUri: 'https://app/callback',
|
||||
provider: 'demo',
|
||||
};
|
||||
|
||||
const expected = { redirectUrl: 'https://auth/redirect', state: 'state-123' };
|
||||
|
||||
provider.startAuth.mockResolvedValue(expected);
|
||||
|
||||
const result = await useCase.execute(command);
|
||||
|
||||
expect(provider.startAuth).toHaveBeenCalledWith(command);
|
||||
expect(result).toEqual(expected);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,53 @@
|
||||
import { describe, it, expect, vi, type Mock } from 'vitest';
|
||||
import { CreateAchievementUseCase, type IAchievementRepository } from './CreateAchievementUseCase';
|
||||
import { Achievement } from '@core/identity/domain/entities/Achievement';
|
||||
|
||||
describe('CreateAchievementUseCase', () => {
|
||||
let achievementRepository: {
|
||||
save: Mock;
|
||||
findById: Mock;
|
||||
};
|
||||
let useCase: CreateAchievementUseCase;
|
||||
|
||||
beforeEach(() => {
|
||||
achievementRepository = {
|
||||
save: vi.fn(),
|
||||
findById: vi.fn(),
|
||||
};
|
||||
|
||||
useCase = new CreateAchievementUseCase(achievementRepository as unknown as IAchievementRepository);
|
||||
});
|
||||
|
||||
it('creates an achievement and persists it', async () => {
|
||||
const props = {
|
||||
id: 'achv-1',
|
||||
name: 'First Win',
|
||||
description: 'Awarded for winning your first race',
|
||||
category: 'driver' as const,
|
||||
rarity: 'common' as const,
|
||||
iconUrl: 'https://example.com/icon.png',
|
||||
points: 50,
|
||||
requirements: [
|
||||
{
|
||||
type: 'wins',
|
||||
value: 1,
|
||||
operator: '>=',
|
||||
},
|
||||
],
|
||||
isSecret: false,
|
||||
};
|
||||
|
||||
achievementRepository.save.mockResolvedValue(undefined);
|
||||
|
||||
const result = await useCase.execute(props);
|
||||
|
||||
expect(result).toBeInstanceOf(Achievement);
|
||||
expect(result.id).toBe(props.id);
|
||||
expect(result.name).toBe(props.name);
|
||||
expect(result.description).toBe(props.description);
|
||||
expect(result.category).toBe(props.category);
|
||||
expect(result.points).toBe(props.points);
|
||||
expect(result.requirements).toHaveLength(1);
|
||||
expect(achievementRepository.save).toHaveBeenCalledWith(result);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user