tests for infractructure, domain entities and create authentication service

This commit is contained in:
sbriat 2023-07-06 17:38:57 +02:00
parent 470a93879e
commit b23f8f9ade
7 changed files with 296 additions and 3 deletions

View File

@ -1,11 +1,13 @@
import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library'; import { AggregateID, AggregateRoot } from '@mobicoop/ddd-library';
import { UsernameProps } from './username.types'; import { CreateUsernameProps, UsernameProps } from './username.types';
import { UsernameAddedDomainEvent } from './events/username-added.domain-event'; import { UsernameAddedDomainEvent } from './events/username-added.domain-event';
export class UsernameEntity extends AggregateRoot<UsernameProps> { export class UsernameEntity extends AggregateRoot<UsernameProps> {
protected readonly _id: AggregateID; protected readonly _id: AggregateID;
static create = async (create: UsernameProps): Promise<UsernameEntity> => { static create = async (
create: CreateUsernameProps,
): Promise<UsernameEntity> => {
const props: UsernameProps = { ...create }; const props: UsernameProps = { ...create };
const username = new UsernameEntity({ const username = new UsernameEntity({
id: props.name, id: props.name,

View File

@ -1,10 +1,15 @@
// All properties that a Username has
export interface UsernameProps { export interface UsernameProps {
name: string; name: string;
userId?: string; userId?: string;
type: Type; type: Type;
} }
export interface CreateUsernameProps {
name: string;
userId: string;
type: Type;
}
export enum Type { export enum Type {
EMAIL = 'EMAIL', EMAIL = 'EMAIL',
PHONE = 'PHONE', PHONE = 'PHONE',

View File

@ -0,0 +1,61 @@
import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity';
import { CreateAuthenticationProps } from '@modules/authentication/core/domain/authentication.types';
import { AuthenticationDeletedDomainEvent } from '@modules/authentication/core/domain/events/authentication-deleted.domain-event';
import { Type } from '@modules/authentication/core/domain/username.types';
const createAuthenticationProps: CreateAuthenticationProps = {
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
password: 'somePassword',
usernames: [
{
type: Type.EMAIL,
name: 'john.doe@email.com',
},
],
};
const createAuthenticationPropsWith2Usernames: CreateAuthenticationProps = {
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
password: 'somePassword',
usernames: [
{
type: Type.EMAIL,
name: 'john.doe@email.com',
},
{
type: Type.PHONE,
name: '+33611223344',
},
],
};
describe('Authentication entity create', () => {
it('should create a new authentication entity', async () => {
const authenticationEntity: AuthenticationEntity =
await AuthenticationEntity.create(createAuthenticationProps);
expect(authenticationEntity.id).toBe(
'165192d4-398a-4469-a16b-98c02cc6f531',
);
expect(authenticationEntity.domainEvents.length).toBe(1);
});
it('should create a new authentication entity with 2 usernames', async () => {
const authenticationEntity: AuthenticationEntity =
await AuthenticationEntity.create(
createAuthenticationPropsWith2Usernames,
);
expect(authenticationEntity.id).toBe(
'165192d4-398a-4469-a16b-98c02cc6f531',
);
expect(authenticationEntity.getProps().usernames.length).toBe(2);
expect(authenticationEntity.domainEvents.length).toBe(1);
});
it('should delete an authentication entity', async () => {
const authenticationEntity: AuthenticationEntity =
await AuthenticationEntity.create(createAuthenticationProps);
authenticationEntity.delete();
expect(authenticationEntity.domainEvents.length).toBe(2);
expect(authenticationEntity.domainEvents[1]).toBeInstanceOf(
AuthenticationDeletedDomainEvent,
);
});
});

View File

@ -0,0 +1,131 @@
import {
AggregateID,
ConflictException,
UniqueConstraintException,
} from '@mobicoop/ddd-library';
import { AUTHENTICATION_REPOSITORY } from '@modules/authentication/authentication.di-tokens';
import { CreateAuthenticationCommand } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.command';
import { CreateAuthenticationService } from '@modules/authentication/core/application/commands/create-authentication/create-authentication.service';
import { AuthenticationEntity } from '@modules/authentication/core/domain/authentication.entity';
import {
AuthenticationAlreadyExistsException,
UsernameAlreadyExistsException,
} from '@modules/authentication/core/domain/authentication.errors';
import { Type } from '@modules/authentication/core/domain/username.types';
import { CreateAuthenticationRequestDto } from '@modules/authentication/interface/grpc-controllers/dtos/create-authentication.request.dto';
import { Test, TestingModule } from '@nestjs/testing';
const createAuthenticationRequest: CreateAuthenticationRequestDto = {
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
password: 'somePassword',
usernames: [
{
name: 'john.doe@email.com',
type: Type.EMAIL,
},
],
};
const mockAuthenticationRepository = {
insert: jest
.fn()
.mockImplementationOnce(() => ({}))
.mockImplementationOnce(() => {
throw new ConflictException('already exists');
})
.mockImplementationOnce(() => {
throw new UniqueConstraintException('uuid');
})
.mockImplementationOnce(() => {
throw new UniqueConstraintException('username');
})
.mockImplementationOnce(() => {
throw new Error();
}),
};
describe('create-authentication.service', () => {
let createAuthenticationService: CreateAuthenticationService;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
providers: [
{
provide: AUTHENTICATION_REPOSITORY,
useValue: mockAuthenticationRepository,
},
CreateAuthenticationService,
],
}).compile();
createAuthenticationService = module.get<CreateAuthenticationService>(
CreateAuthenticationService,
);
});
it('should be defined', () => {
expect(createAuthenticationService).toBeDefined();
});
describe('execution', () => {
const createAdCommand = new CreateAuthenticationCommand(
createAuthenticationRequest,
);
it('should create a new authentication', async () => {
AuthenticationEntity.create = jest.fn().mockReturnValue({
id: '165192d4-398a-4469-a16b-98c02cc6f531',
getProps: jest.fn().mockImplementation(() => ({
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
})),
});
const result: AggregateID = await createAuthenticationService.execute(
createAdCommand,
);
expect(result).toBe('165192d4-398a-4469-a16b-98c02cc6f531');
});
it('should throw a dedicated exception if Authentication already exists', async () => {
AuthenticationEntity.create = jest.fn().mockReturnValue({
id: '165192d4-398a-4469-a16b-98c02cc6f531',
getProps: jest.fn().mockImplementation(() => ({
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
})),
});
await expect(
createAuthenticationService.execute(createAdCommand),
).rejects.toBeInstanceOf(AuthenticationAlreadyExistsException);
});
it('should throw a dedicated exception if uuid already exists', async () => {
AuthenticationEntity.create = jest.fn().mockReturnValue({
id: '165192d4-398a-4469-a16b-98c02cc6f531',
getProps: jest.fn().mockImplementation(() => ({
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
})),
});
await expect(
createAuthenticationService.execute(createAdCommand),
).rejects.toBeInstanceOf(AuthenticationAlreadyExistsException);
});
it('should throw a dedicated exception if username already exists', async () => {
AuthenticationEntity.create = jest.fn().mockReturnValue({
id: '165192d4-398a-4469-a16b-98c02cc6f531',
getProps: jest.fn().mockImplementation(() => ({
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
})),
});
await expect(
createAuthenticationService.execute(createAdCommand),
).rejects.toBeInstanceOf(UsernameAlreadyExistsException);
});
it('should throw an error if something bad happens', async () => {
AuthenticationEntity.create = jest.fn().mockReturnValue({
id: '165192d4-398a-4469-a16b-98c02cc6f531',
getProps: jest.fn().mockImplementation(() => ({
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
})),
});
await expect(
createAuthenticationService.execute(createAdCommand),
).rejects.toBeInstanceOf(Error);
});
});
});

View File

@ -0,0 +1,21 @@
import { UsernameEntity } from '@modules/authentication/core/domain/username.entity';
import {
CreateUsernameProps,
Type,
} from '@modules/authentication/core/domain/username.types';
const createUsernameProps: CreateUsernameProps = {
userId: '165192d4-398a-4469-a16b-98c02cc6f531',
type: Type.EMAIL,
name: 'john.doe@email.com',
};
describe('Username entity create', () => {
it('should create a new username entity', async () => {
const usernameEntity: UsernameEntity = await UsernameEntity.create(
createUsernameProps,
);
expect(usernameEntity.id).toBe('john.doe@email.com');
expect(usernameEntity.domainEvents.length).toBe(1);
});
});

View File

@ -0,0 +1,37 @@
import { AuthenticationMapper } from '@modules/authentication/authentication.mapper';
import { AuthenticationRepository } from '@modules/authentication/infrastructure/authentication.repository';
import { PrismaService } from '@modules/authentication/infrastructure/prisma.service';
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
import { Test, TestingModule } from '@nestjs/testing';
const mockMessagePublisher = {
publish: jest.fn().mockImplementation(),
};
describe('Authentication repository', () => {
let prismaService: PrismaService;
let authenticationMapper: AuthenticationMapper;
let eventEmitter: EventEmitter2;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [EventEmitterModule.forRoot()],
providers: [PrismaService, AuthenticationMapper],
}).compile();
prismaService = module.get<PrismaService>(PrismaService);
authenticationMapper =
module.get<AuthenticationMapper>(AuthenticationMapper);
eventEmitter = module.get<EventEmitter2>(EventEmitter2);
});
it('should be defined', () => {
expect(
new AuthenticationRepository(
prismaService,
authenticationMapper,
eventEmitter,
mockMessagePublisher,
),
).toBeDefined();
});
});

View File

@ -0,0 +1,36 @@
import { UsernameMapper } from '@modules/authentication/username.mapper';
import { PrismaService } from '@modules/authentication/infrastructure/prisma.service';
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
import { Test, TestingModule } from '@nestjs/testing';
import { UsernameRepository } from '@modules/authentication/infrastructure/username.repository';
const mockMessagePublisher = {
publish: jest.fn().mockImplementation(),
};
describe('Username repository', () => {
let prismaService: PrismaService;
let usernameMapper: UsernameMapper;
let eventEmitter: EventEmitter2;
beforeAll(async () => {
const module: TestingModule = await Test.createTestingModule({
imports: [EventEmitterModule.forRoot()],
providers: [PrismaService, UsernameMapper],
}).compile();
prismaService = module.get<PrismaService>(PrismaService);
usernameMapper = module.get<UsernameMapper>(UsernameMapper);
eventEmitter = module.get<EventEmitter2>(EventEmitter2);
});
it('should be defined', () => {
expect(
new UsernameRepository(
prismaService,
usernameMapper,
eventEmitter,
mockMessagePublisher,
),
).toBeDefined();
});
});