diff --git a/src/libs/tests/unit/db/prisma-repository.base.spec.ts b/src/libs/tests/unit/db/prisma-repository.base.spec.ts index 49eca17..e3cc272 100644 --- a/src/libs/tests/unit/db/prisma-repository.base.spec.ts +++ b/src/libs/tests/unit/db/prisma-repository.base.spec.ts @@ -13,11 +13,11 @@ import { Test, TestingModule } from '@nestjs/testing'; import { Prisma } from '@prisma/client'; import { v4 } from 'uuid'; -export interface FakeProps { +interface FakeProps { name: string; } -export interface CreateFakeProps { +interface CreateFakeProps { name: string; } diff --git a/src/libs/tests/unit/ddd/aggregate-root.base.spec.ts b/src/libs/tests/unit/ddd/aggregate-root.base.spec.ts new file mode 100644 index 0000000..1b10ec6 --- /dev/null +++ b/src/libs/tests/unit/ddd/aggregate-root.base.spec.ts @@ -0,0 +1,76 @@ +import { + AggregateID, + AggregateRoot, + DomainEvent, + DomainEventProps, +} from '@libs/ddd'; +import { EventEmitter2 } from '@nestjs/event-emitter'; +import { v4 } from 'uuid'; + +interface FakeProps { + name: string; +} + +interface CreateFakeProps { + name: string; +} + +class FakeRecordCreatedDomainEvent extends DomainEvent { + readonly name: string; + + constructor(props: DomainEventProps) { + super(props); + this.name = props.name; + } +} + +class FakeEntity extends AggregateRoot { + protected readonly _id: AggregateID; + + static create = (create: CreateFakeProps): FakeEntity => { + const id = v4(); + const props: FakeProps = { ...create }; + const fake = new FakeEntity({ id, props }); + fake.addEvent( + new FakeRecordCreatedDomainEvent({ + aggregateId: id, + name: props.name, + }), + ); + return fake; + }; + + validate(): void { + // not implemented + } +} + +const mockLogger = { + debug: jest.fn(), + log: jest.fn(), + error: jest.fn(), + warn: jest.fn(), +}; + +describe('AggregateRoot Base', () => { + it('should define an aggregate root based object instance', () => { + const fakeInstance = FakeEntity.create({ + name: 'someFakeName', + }); + expect(fakeInstance).toBeDefined(); + expect(fakeInstance.domainEvents.length).toBe(1); + }); + + it('should publish domain events', async () => { + jest.spyOn(mockLogger, 'debug'); + const eventEmitter = new EventEmitter2(); + jest.spyOn(eventEmitter, 'emitAsync'); + const fakeInstance = FakeEntity.create({ + name: 'someFakeName', + }); + await fakeInstance.publishEvents(mockLogger, eventEmitter); + expect(mockLogger.debug).toHaveBeenCalledTimes(1); + expect(eventEmitter.emitAsync).toHaveBeenCalledTimes(1); + expect(fakeInstance.domainEvents.length).toBe(0); + }); +}); diff --git a/src/libs/tests/unit/ddd/command.base.spec.ts b/src/libs/tests/unit/ddd/command.base.spec.ts new file mode 100644 index 0000000..5ea8f42 --- /dev/null +++ b/src/libs/tests/unit/ddd/command.base.spec.ts @@ -0,0 +1,47 @@ +import { Command, CommandProps } from '@libs/ddd'; +import { ArgumentNotProvidedException } from '@libs/exceptions'; + +class FakeCommand extends Command { + readonly name: string; + + constructor(props: CommandProps) { + super(props); + this.name = props.name; + } +} + +class BadFakeCommand extends Command { + constructor(props: CommandProps) { + super(props); + } +} + +describe('Command Base', () => { + it('should define a command based object instance', () => { + const fakeCommand = new FakeCommand({ name: 'fakeName' }); + expect(fakeCommand).toBeDefined(); + expect(fakeCommand.id.length).toBe(36); + }); + + it('should define a command based object instance with a provided id', () => { + const fakeCommand = new FakeCommand({ id: 'some-id', name: 'fakeName' }); + expect(fakeCommand.id).toBe('some-id'); + }); + + it('should define a command based object instance with metadata', () => { + const fakeCommand = new FakeCommand({ + name: 'fakeName', + metadata: { + correlationId: 'some-correlation-id', + causationId: 'some-causation-id', + userId: 'some-user-id', + timestamp: new Date('2023-06-28T05:00:00Z').getTime(), + }, + }); + expect(fakeCommand.metadata.timestamp).toBe(1687928400000); + }); + + it('should throw an exception if props are empty', () => { + expect(() => new BadFakeCommand({})).toThrow(ArgumentNotProvidedException); + }); +});