Implement the UpdateAdCommand
This commit is contained in:
parent
34ad357f47
commit
3be2d73c60
|
@ -22,7 +22,7 @@
|
||||||
"test:unit:ci": "jest --testPathPattern 'tests/unit/' --coverage",
|
"test:unit:ci": "jest --testPathPattern 'tests/unit/' --coverage",
|
||||||
"test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose --runInBand",
|
"test:integration": "npm run migrate:test && dotenv -e .env.test -- jest --testPathPattern 'tests/integration/' --verbose --runInBand",
|
||||||
"test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/' --runInBand",
|
"test:integration:ci": "npm run migrate:test:ci && dotenv -e ci/.env.ci -- jest --testPathPattern 'tests/integration/' --runInBand",
|
||||||
"test:cov": "jest --testPathPattern 'tests/unit/' --coverage",
|
"test:watch": "jest --testPathPattern 'tests/unit/' --watch",
|
||||||
"test:e2e": "jest --config ./test/jest-e2e.json",
|
"test:e2e": "jest --config ./test/jest-e2e.json",
|
||||||
"migrate": "docker exec v3-matcher-api sh -c 'npx prisma migrate deploy'",
|
"migrate": "docker exec v3-matcher-api sh -c 'npx prisma migrate deploy'",
|
||||||
"migrate:dev": "docker exec v3-matcher-api sh -c 'npx prisma migrate dev'",
|
"migrate:dev": "docker exec v3-matcher-api sh -c 'npx prisma migrate dev'",
|
||||||
|
|
|
@ -10,6 +10,8 @@ export const GRPC_GEOROUTER_SERVICE_NAME = 'GeorouterService';
|
||||||
export const MATCHER_AD_CREATED_ROUTING_KEY = 'matcher-ad.created';
|
export const MATCHER_AD_CREATED_ROUTING_KEY = 'matcher-ad.created';
|
||||||
export const MATCHER_AD_CREATION_FAILED_ROUTING_KEY =
|
export const MATCHER_AD_CREATION_FAILED_ROUTING_KEY =
|
||||||
'matcher-ad.creation-failed';
|
'matcher-ad.creation-failed';
|
||||||
|
export const MATCHER_AD_UPDATED_ROUTING_KEY = 'matcher-ad.updated';
|
||||||
|
export const MATCHER_AD_UPDATE_FAILED_ROUTING_KEY = 'matcher-ad.update-failed';
|
||||||
|
|
||||||
// messaging input
|
// messaging input
|
||||||
export const AD_CREATED_MESSAGE_HANDLER = 'adCreated';
|
export const AD_CREATED_MESSAGE_HANDLER = 'adCreated';
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {
|
||||||
AdWriteExtraModel,
|
AdWriteExtraModel,
|
||||||
AdWriteModel,
|
AdWriteModel,
|
||||||
ScheduleItemModel,
|
ScheduleItemModel,
|
||||||
|
ScheduleWriteModel,
|
||||||
} from './infrastructure/ad.repository';
|
} from './infrastructure/ad.repository';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -38,9 +39,8 @@ export class AdMapper
|
||||||
private readonly directionEncoder: DirectionEncoderPort,
|
private readonly directionEncoder: DirectionEncoderPort,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
toPersistence = (entity: AdEntity): AdWriteModel => {
|
toPersistence = (entity: AdEntity, update?: boolean): AdWriteModel => {
|
||||||
const copy = entity.getProps();
|
const copy = entity.getProps();
|
||||||
const now = new Date();
|
|
||||||
const record: AdWriteModel = {
|
const record: AdWriteModel = {
|
||||||
uuid: copy.id,
|
uuid: copy.id,
|
||||||
driver: copy.driver,
|
driver: copy.driver,
|
||||||
|
@ -48,8 +48,27 @@ export class AdMapper
|
||||||
frequency: copy.frequency,
|
frequency: copy.frequency,
|
||||||
fromDate: new Date(copy.fromDate),
|
fromDate: new Date(copy.fromDate),
|
||||||
toDate: new Date(copy.toDate),
|
toDate: new Date(copy.toDate),
|
||||||
schedule: {
|
schedule: this.toScheduleItemWriteModel(copy.schedule, update),
|
||||||
create: copy.schedule.map((scheduleItem: ScheduleItemProps) => ({
|
seatsProposed: copy.seatsProposed,
|
||||||
|
seatsRequested: copy.seatsRequested,
|
||||||
|
strict: copy.strict,
|
||||||
|
driverDuration: copy.driverDuration,
|
||||||
|
driverDistance: copy.driverDistance,
|
||||||
|
passengerDuration: copy.passengerDuration,
|
||||||
|
passengerDistance: copy.passengerDistance,
|
||||||
|
fwdAzimuth: copy.fwdAzimuth,
|
||||||
|
backAzimuth: copy.backAzimuth,
|
||||||
|
};
|
||||||
|
return record;
|
||||||
|
};
|
||||||
|
|
||||||
|
toScheduleItemWriteModel = (
|
||||||
|
schedule: ScheduleItemProps[],
|
||||||
|
update?: boolean,
|
||||||
|
): ScheduleWriteModel => {
|
||||||
|
const now = new Date();
|
||||||
|
const record: ScheduleWriteModel = {
|
||||||
|
create: schedule.map((scheduleItem: ScheduleItemProps) => ({
|
||||||
uuid: v4(),
|
uuid: v4(),
|
||||||
day: scheduleItem.day,
|
day: scheduleItem.day,
|
||||||
time: new Date(
|
time: new Date(
|
||||||
|
@ -63,19 +82,12 @@ export class AdMapper
|
||||||
createdAt: now,
|
createdAt: now,
|
||||||
updatedAt: now,
|
updatedAt: now,
|
||||||
})),
|
})),
|
||||||
},
|
|
||||||
seatsProposed: copy.seatsProposed,
|
|
||||||
seatsRequested: copy.seatsRequested,
|
|
||||||
strict: copy.strict,
|
|
||||||
driverDuration: copy.driverDuration,
|
|
||||||
driverDistance: copy.driverDistance,
|
|
||||||
passengerDuration: copy.passengerDuration,
|
|
||||||
passengerDistance: copy.passengerDistance,
|
|
||||||
fwdAzimuth: copy.fwdAzimuth,
|
|
||||||
backAzimuth: copy.backAzimuth,
|
|
||||||
createdAt: copy.createdAt,
|
|
||||||
updatedAt: copy.updatedAt,
|
|
||||||
};
|
};
|
||||||
|
if (update) {
|
||||||
|
record.deleteMany = {
|
||||||
|
createdAt: { lt: now },
|
||||||
|
};
|
||||||
|
}
|
||||||
return record;
|
return record;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -31,6 +31,7 @@ import {
|
||||||
import { AdMapper } from './ad.mapper';
|
import { AdMapper } from './ad.mapper';
|
||||||
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
|
import { CreateAdService } from './core/application/commands/create-ad/create-ad.service';
|
||||||
import { DeleteAdService } from './core/application/commands/delete-ad/delete-ad.service';
|
import { DeleteAdService } from './core/application/commands/delete-ad/delete-ad.service';
|
||||||
|
import { UpdateAdService } from './core/application/commands/update-ad/update-ad.service';
|
||||||
import { PublishMessageWhenMatcherAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-matcher-ad-is-created.domain-event-handler';
|
import { PublishMessageWhenMatcherAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-matcher-ad-is-created.domain-event-handler';
|
||||||
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
|
import { MatchQueryHandler } from './core/application/queries/match/match.query-handler';
|
||||||
import { AdRepository } from './infrastructure/ad.repository';
|
import { AdRepository } from './infrastructure/ad.repository';
|
||||||
|
@ -104,7 +105,11 @@ const eventHandlers: Provider[] = [
|
||||||
PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
|
PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
|
||||||
];
|
];
|
||||||
|
|
||||||
const commandHandlers: Provider[] = [CreateAdService, DeleteAdService];
|
const commandHandlers: Provider[] = [
|
||||||
|
CreateAdService,
|
||||||
|
UpdateAdService,
|
||||||
|
DeleteAdService,
|
||||||
|
];
|
||||||
|
|
||||||
const queryHandlers: Provider[] = [MatchQueryHandler];
|
const queryHandlers: Provider[] = [MatchQueryHandler];
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@ import { Inject } from '@nestjs/common';
|
||||||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
|
||||||
import { MATCHER_AD_CREATION_FAILED_ROUTING_KEY } from '@src/app.constants';
|
import { MATCHER_AD_CREATION_FAILED_ROUTING_KEY } from '@src/app.constants';
|
||||||
import { GeorouterService } from '../../../domain/georouter.service';
|
import { GeorouterService } from '../../../domain/georouter.service';
|
||||||
import { MatcherAdCreationFailedIntegrationEvent } from '../../events/matcher-ad-creation-failed.integration-event';
|
import { MatcherAdCreationFailedIntegrationEvent } from '../../events/matcher-ad-failure.integration-event';
|
||||||
import { AdRepositoryPort } from '../../ports/ad.repository.port';
|
import { AdRepositoryPort } from '../../ports/ad.repository.port';
|
||||||
import { CreateAdCommand } from './create-ad.command';
|
import { CreateAdCommand } from './create-ad.command';
|
||||||
|
|
||||||
|
@ -35,6 +35,7 @@ export class CreateAdService implements ICommandHandler {
|
||||||
const ad = await adFactory.create(command);
|
const ad = await adFactory.create(command);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
//TODO it should not be this service's concern that Prisma does not support postgis types
|
||||||
await this.repository.insertExtra(ad, 'ad');
|
await this.repository.insertExtra(ad, 'ad');
|
||||||
return ad.id;
|
return ad.id;
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { CreateAdCommand } from '../create-ad/create-ad.command';
|
||||||
|
|
||||||
|
export class UpdateAdCommand extends CreateAdCommand {}
|
|
@ -0,0 +1,48 @@
|
||||||
|
import { MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||||
|
import {
|
||||||
|
AD_MESSAGE_PUBLISHER,
|
||||||
|
AD_REPOSITORY,
|
||||||
|
AD_ROUTE_PROVIDER,
|
||||||
|
} from '@modules/ad/ad.di-tokens';
|
||||||
|
import { AdFactory } from '@modules/ad/core/domain/ad.factory';
|
||||||
|
import { Inject } from '@nestjs/common';
|
||||||
|
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
|
||||||
|
import { MATCHER_AD_UPDATE_FAILED_ROUTING_KEY } from '@src/app.constants';
|
||||||
|
import { GeorouterService } from '../../../domain/georouter.service';
|
||||||
|
import { MatcherAdUpdateFailedIntegrationEvent } from '../../events/matcher-ad-failure.integration-event';
|
||||||
|
import { AdRepositoryPort } from '../../ports/ad.repository.port';
|
||||||
|
import { UpdateAdCommand } from './update-ad.command';
|
||||||
|
|
||||||
|
@CommandHandler(UpdateAdCommand)
|
||||||
|
export class UpdateAdService implements ICommandHandler {
|
||||||
|
constructor(
|
||||||
|
@Inject(AD_MESSAGE_PUBLISHER)
|
||||||
|
private readonly messagePublisher: MessagePublisherPort,
|
||||||
|
@Inject(AD_REPOSITORY)
|
||||||
|
private readonly repository: AdRepositoryPort,
|
||||||
|
@Inject(AD_ROUTE_PROVIDER)
|
||||||
|
private readonly routeProvider: GeorouterService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
async execute(command: UpdateAdCommand): Promise<void> {
|
||||||
|
try {
|
||||||
|
const adFactory = new AdFactory(this.routeProvider);
|
||||||
|
const ad = await adFactory.create(command);
|
||||||
|
return this.repository.update(ad.id, ad);
|
||||||
|
} catch (error: any) {
|
||||||
|
const integrationEvent = new MatcherAdUpdateFailedIntegrationEvent({
|
||||||
|
id: command.id,
|
||||||
|
metadata: {
|
||||||
|
correlationId: command.id,
|
||||||
|
timestamp: Date.now(),
|
||||||
|
},
|
||||||
|
cause: error.message,
|
||||||
|
});
|
||||||
|
this.messagePublisher.publish(
|
||||||
|
MATCHER_AD_UPDATE_FAILED_ROUTING_KEY,
|
||||||
|
JSON.stringify(integrationEvent),
|
||||||
|
);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,12 +0,0 @@
|
||||||
import { IntegrationEvent, IntegrationEventProps } from '@mobicoop/ddd-library';
|
|
||||||
|
|
||||||
export class MatcherAdCreationFailedIntegrationEvent extends IntegrationEvent {
|
|
||||||
readonly cause?: string;
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
props: IntegrationEventProps<MatcherAdCreationFailedIntegrationEvent>,
|
|
||||||
) {
|
|
||||||
super(props);
|
|
||||||
this.cause = props.cause;
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { IntegrationEvent, IntegrationEventProps } from '@mobicoop/ddd-library';
|
||||||
|
|
||||||
|
export class MatcherAdFailureIntegrationEvent extends IntegrationEvent {
|
||||||
|
readonly cause?: string;
|
||||||
|
|
||||||
|
constructor(
|
||||||
|
props: IntegrationEventProps<MatcherAdCreationFailedIntegrationEvent>,
|
||||||
|
) {
|
||||||
|
super(props);
|
||||||
|
this.cause = props.cause;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export class MatcherAdCreationFailedIntegrationEvent extends MatcherAdFailureIntegrationEvent {}
|
||||||
|
export class MatcherAdUpdateFailedIntegrationEvent extends MatcherAdFailureIntegrationEvent {}
|
|
@ -1,14 +1,14 @@
|
||||||
|
import { LoggerBase, MessagePublisherPort } from '@mobicoop/ddd-library';
|
||||||
|
import { ExtendedPrismaRepositoryBase } from '@mobicoop/ddd-library/dist/db/prisma-repository.base';
|
||||||
import { Inject, Injectable, Logger } from '@nestjs/common';
|
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||||||
import { EventEmitter2 } from '@nestjs/event-emitter';
|
import { EventEmitter2 } from '@nestjs/event-emitter';
|
||||||
import { AdRepositoryPort } from '../core/application/ports/ad.repository.port';
|
|
||||||
import { LoggerBase, MessagePublisherPort } from '@mobicoop/ddd-library';
|
|
||||||
import { PrismaService } from './prisma.service';
|
|
||||||
import { AD_MESSAGE_PUBLISHER } from '../ad.di-tokens';
|
|
||||||
import { AdEntity } from '../core/domain/ad.entity';
|
|
||||||
import { AdMapper } from '../ad.mapper';
|
|
||||||
import { ExtendedPrismaRepositoryBase } from '@mobicoop/ddd-library/dist/db/prisma-repository.base';
|
|
||||||
import { Frequency } from '../core/domain/ad.types';
|
|
||||||
import { SERVICE_NAME } from '@src/app.constants';
|
import { SERVICE_NAME } from '@src/app.constants';
|
||||||
|
import { AD_MESSAGE_PUBLISHER } from '../ad.di-tokens';
|
||||||
|
import { AdMapper } from '../ad.mapper';
|
||||||
|
import { AdRepositoryPort } from '../core/application/ports/ad.repository.port';
|
||||||
|
import { AdEntity } from '../core/domain/ad.entity';
|
||||||
|
import { Frequency } from '../core/domain/ad.types';
|
||||||
|
import { PrismaService } from './prisma.service';
|
||||||
|
|
||||||
export type AdModel = {
|
export type AdModel = {
|
||||||
uuid: string;
|
uuid: string;
|
||||||
|
@ -26,8 +26,6 @@ export type AdModel = {
|
||||||
passengerDistance?: number;
|
passengerDistance?: number;
|
||||||
fwdAzimuth: number;
|
fwdAzimuth: number;
|
||||||
backAzimuth: number;
|
backAzimuth: number;
|
||||||
createdAt: Date;
|
|
||||||
updatedAt: Date;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -36,15 +34,26 @@ export type AdModel = {
|
||||||
export type AdReadModel = AdModel & {
|
export type AdReadModel = AdModel & {
|
||||||
waypoints: string;
|
waypoints: string;
|
||||||
schedule: ScheduleItemModel[];
|
schedule: ScheduleItemModel[];
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The record ready to be sent to the persistence system
|
* The record ready to be sent to the persistence system
|
||||||
*/
|
*/
|
||||||
export type AdWriteModel = AdModel & {
|
export type AdWriteModel = AdModel & {
|
||||||
schedule: {
|
schedule: ScheduleWriteModel;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type ScheduleWriteModel = {
|
||||||
|
deleteMany?: PastCreatedFilter;
|
||||||
create: ScheduleItemModel[];
|
create: ScheduleItemModel[];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// used to delete records created in the past,
|
||||||
|
// because the order of `create` and `deleteMany` is not guaranteed
|
||||||
|
export type PastCreatedFilter = {
|
||||||
|
createdAt: { lt: Date };
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AdWriteExtraModel = {
|
export type AdWriteExtraModel = {
|
||||||
|
@ -70,11 +79,15 @@ export type UngroupedAdModel = AdModel &
|
||||||
scheduleItemCreatedAt: Date;
|
scheduleItemCreatedAt: Date;
|
||||||
scheduleItemUpdatedAt: Date;
|
scheduleItemUpdatedAt: Date;
|
||||||
waypoints: string;
|
waypoints: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type GroupedAdModel = AdModel & {
|
export type GroupedAdModel = AdModel & {
|
||||||
schedule: ScheduleItemModel[];
|
schedule: ScheduleItemModel[];
|
||||||
waypoints: string;
|
waypoints: string;
|
||||||
|
createdAt: Date;
|
||||||
|
updatedAt: Date;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -169,4 +182,12 @@ export class AdRepository
|
||||||
});
|
});
|
||||||
return adReadModels;
|
return adReadModels;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
async update(
|
||||||
|
id: string,
|
||||||
|
entity: AdEntity,
|
||||||
|
identifier?: string,
|
||||||
|
): Promise<void> {
|
||||||
|
this.updateExtra(id, entity, 'ad', identifier);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
import {
|
||||||
|
AD_MESSAGE_PUBLISHER,
|
||||||
|
AD_REPOSITORY,
|
||||||
|
AD_ROUTE_PROVIDER,
|
||||||
|
} from '@modules/ad/ad.di-tokens';
|
||||||
|
import { UpdateAdCommand } from '@modules/ad/core/application/commands/update-ad/update-ad.command';
|
||||||
|
import { UpdateAdService } from '@modules/ad/core/application/commands/update-ad/update-ad.service';
|
||||||
|
import { GeorouterService } from '@modules/ad/core/domain/georouter.service';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
import { createAdProps } from './ad.fixtures';
|
||||||
|
|
||||||
|
const mockAdRepository = {
|
||||||
|
update: jest.fn().mockImplementation((id) => {
|
||||||
|
if (id === '42') {
|
||||||
|
throw 'Bad id!';
|
||||||
|
}
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockRouteProvider: GeorouterService = {
|
||||||
|
getRoute: jest.fn().mockImplementation(() => ({
|
||||||
|
distance: 350101,
|
||||||
|
duration: 14422,
|
||||||
|
fwdAzimuth: 273,
|
||||||
|
backAzimuth: 93,
|
||||||
|
distanceAzimuth: 336544,
|
||||||
|
points: [
|
||||||
|
{
|
||||||
|
lon: 6.1765102,
|
||||||
|
lat: 48.689445,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 4.984578,
|
||||||
|
lat: 48.725687,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
lon: 2.3522,
|
||||||
|
lat: 48.8566,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockMessagePublisher = {
|
||||||
|
publish: jest.fn().mockImplementation(),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('update-ad.service', () => {
|
||||||
|
let updateAdService: UpdateAdService;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: AD_REPOSITORY,
|
||||||
|
useValue: mockAdRepository,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AD_ROUTE_PROVIDER,
|
||||||
|
useValue: mockRouteProvider,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: AD_MESSAGE_PUBLISHER,
|
||||||
|
useValue: mockMessagePublisher,
|
||||||
|
},
|
||||||
|
UpdateAdService,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
updateAdService = module.get<UpdateAdService>(UpdateAdService);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(updateAdService).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('execute', () => {
|
||||||
|
it('should call the repository update method', async () => {
|
||||||
|
const updateAdCommand = new UpdateAdCommand(createAdProps());
|
||||||
|
await updateAdService.execute(updateAdCommand);
|
||||||
|
expect(mockAdRepository.update).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should emit an event when an error occurs', async () => {
|
||||||
|
const commandProps = createAdProps();
|
||||||
|
commandProps.id = '42';
|
||||||
|
const updateAdCommand = new UpdateAdCommand(commandProps);
|
||||||
|
await expect(updateAdService.execute(updateAdCommand)).rejects.toBe(
|
||||||
|
'Bad id!',
|
||||||
|
);
|
||||||
|
expect(mockMessagePublisher.publish).toHaveBeenCalled();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Reference in New Issue