send messages when matcher ad is created, or when matcher ad creation has failed
This commit is contained in:
		
							parent
							
								
									73f660bf6d
								
							
						
					
					
						commit
						80fac59c43
					
				| 
						 | 
				
			
			@ -14,13 +14,9 @@ export const AD_UPDATED_QUEUE = 'matcher-ad-updated';
 | 
			
		|||
export const AD_DELETED_MESSAGE_HANDLER = 'adDeleted';
 | 
			
		||||
export const AD_DELETED_ROUTING_KEY = 'ad.deleted';
 | 
			
		||||
export const AD_DELETED_QUEUE = 'matcher-ad-deleted';
 | 
			
		||||
 | 
			
		||||
// configuration
 | 
			
		||||
export const SERVICE_CONFIGURATION_SET_QUEUE = 'matcher-configuration-set';
 | 
			
		||||
export const SERVICE_CONFIGURATION_DELETE_QUEUE =
 | 
			
		||||
  'matcher-configuration-delete';
 | 
			
		||||
export const SERVICE_CONFIGURATION_PROPAGATE_QUEUE =
 | 
			
		||||
  'matcher-configuration-propagate';
 | 
			
		||||
export const MATCHER_AD_CREATED_ROUTING_KEY = 'matcher.ad.created';
 | 
			
		||||
export const MATCHER_AD_CREATION_FAILED_ROUTING_KEY =
 | 
			
		||||
  'matcher.ad.creation.failed';
 | 
			
		||||
 | 
			
		||||
// health
 | 
			
		||||
export const GRPC_HEALTH_PACKAGE_NAME = 'health';
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -43,6 +43,7 @@ import {
 | 
			
		|||
  RedisModuleOptions,
 | 
			
		||||
} from '@songkeys/nestjs-redis';
 | 
			
		||||
import { ConfigurationRepository } from '@mobicoop/configuration-module';
 | 
			
		||||
import { PublishMessageWhenMatcherAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-matcher-ad-is-created.domain-event-handler';
 | 
			
		||||
 | 
			
		||||
const imports = [
 | 
			
		||||
  CqrsModule,
 | 
			
		||||
| 
						 | 
				
			
			@ -80,6 +81,10 @@ const grpcControllers = [MatchGrpcController];
 | 
			
		|||
 | 
			
		||||
const messageHandlers = [AdCreatedMessageHandler];
 | 
			
		||||
 | 
			
		||||
const eventHandlers: Provider[] = [
 | 
			
		||||
  PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
 | 
			
		||||
];
 | 
			
		||||
 | 
			
		||||
const commandHandlers: Provider[] = [CreateAdService];
 | 
			
		||||
 | 
			
		||||
const queryHandlers: Provider[] = [MatchQueryHandler];
 | 
			
		||||
| 
						 | 
				
			
			@ -150,6 +155,7 @@ const adapters: Provider[] = [
 | 
			
		|||
  controllers: [...grpcControllers],
 | 
			
		||||
  providers: [
 | 
			
		||||
    ...messageHandlers,
 | 
			
		||||
    ...eventHandlers,
 | 
			
		||||
    ...commandHandlers,
 | 
			
		||||
    ...queryHandlers,
 | 
			
		||||
    ...mappers,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,10 +1,18 @@
 | 
			
		|||
import { CommandHandler, ICommandHandler } from '@nestjs/cqrs';
 | 
			
		||||
import { CreateAdCommand } from './create-ad.command';
 | 
			
		||||
import { Inject } from '@nestjs/common';
 | 
			
		||||
import { AD_REPOSITORY, AD_ROUTE_PROVIDER } from '@modules/ad/ad.di-tokens';
 | 
			
		||||
import {
 | 
			
		||||
  AD_MESSAGE_PUBLISHER,
 | 
			
		||||
  AD_REPOSITORY,
 | 
			
		||||
  AD_ROUTE_PROVIDER,
 | 
			
		||||
} from '@modules/ad/ad.di-tokens';
 | 
			
		||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
 | 
			
		||||
import { AdRepositoryPort } from '../../ports/ad.repository.port';
 | 
			
		||||
import { AggregateID, ConflictException } from '@mobicoop/ddd-library';
 | 
			
		||||
import {
 | 
			
		||||
  AggregateID,
 | 
			
		||||
  ConflictException,
 | 
			
		||||
  MessagePublisherPort,
 | 
			
		||||
} from '@mobicoop/ddd-library';
 | 
			
		||||
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
 | 
			
		||||
import { RouteProviderPort } from '../../ports/route-provider.port';
 | 
			
		||||
import { Role } from '@modules/ad/core/domain/ad.types';
 | 
			
		||||
| 
						 | 
				
			
			@ -17,10 +25,14 @@ import {
 | 
			
		|||
import { Waypoint } from '../../types/waypoint.type';
 | 
			
		||||
import { Point as PointValueObject } from '@modules/ad/core/domain/value-objects/point.value-object';
 | 
			
		||||
import { Point } from '@modules/geography/core/domain/route.types';
 | 
			
		||||
import { MatcherAdCreationFailedIntegrationEvent } from '../../events/matcher-ad-creation-failed.integration-event';
 | 
			
		||||
import { MATCHER_AD_CREATION_FAILED_ROUTING_KEY } from '@src/app.constants';
 | 
			
		||||
 | 
			
		||||
@CommandHandler(CreateAdCommand)
 | 
			
		||||
export class CreateAdService implements ICommandHandler {
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(AD_MESSAGE_PUBLISHER)
 | 
			
		||||
    private readonly messagePublisher: MessagePublisherPort,
 | 
			
		||||
    @Inject(AD_REPOSITORY)
 | 
			
		||||
    private readonly repository: AdRepositoryPort,
 | 
			
		||||
    @Inject(AD_ROUTE_PROVIDER)
 | 
			
		||||
| 
						 | 
				
			
			@ -44,6 +56,15 @@ export class CreateAdService implements ICommandHandler {
 | 
			
		|||
    );
 | 
			
		||||
 | 
			
		||||
    let typedRoutes: TypedRoute[];
 | 
			
		||||
    let driverDistance: number | undefined;
 | 
			
		||||
    let driverDuration: number | undefined;
 | 
			
		||||
    let passengerDistance: number | undefined;
 | 
			
		||||
    let passengerDuration: number | undefined;
 | 
			
		||||
    let points: PointValueObject[] | undefined;
 | 
			
		||||
    let fwdAzimuth: number | undefined;
 | 
			
		||||
    let backAzimuth: number | undefined;
 | 
			
		||||
 | 
			
		||||
    try {
 | 
			
		||||
      try {
 | 
			
		||||
        typedRoutes = await Promise.all(
 | 
			
		||||
          pathCreator.getBasePaths().map(async (path: Path) => ({
 | 
			
		||||
| 
						 | 
				
			
			@ -55,13 +76,6 @@ export class CreateAdService implements ICommandHandler {
 | 
			
		|||
        throw new Error('Unable to find a route for given waypoints');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
    let driverDistance: number | undefined;
 | 
			
		||||
    let driverDuration: number | undefined;
 | 
			
		||||
    let passengerDistance: number | undefined;
 | 
			
		||||
    let passengerDuration: number | undefined;
 | 
			
		||||
    let points: PointValueObject[] | undefined;
 | 
			
		||||
    let fwdAzimuth: number | undefined;
 | 
			
		||||
    let backAzimuth: number | undefined;
 | 
			
		||||
      try {
 | 
			
		||||
        typedRoutes.forEach((typedRoute: TypedRoute) => {
 | 
			
		||||
          if ([PathType.DRIVER, PathType.GENERIC].includes(typedRoute.type)) {
 | 
			
		||||
| 
						 | 
				
			
			@ -77,7 +91,9 @@ export class CreateAdService implements ICommandHandler {
 | 
			
		|||
            fwdAzimuth = typedRoute.route.fwdAzimuth;
 | 
			
		||||
            backAzimuth = typedRoute.route.backAzimuth;
 | 
			
		||||
          }
 | 
			
		||||
        if ([PathType.PASSENGER, PathType.GENERIC].includes(typedRoute.type)) {
 | 
			
		||||
          if (
 | 
			
		||||
            [PathType.PASSENGER, PathType.GENERIC].includes(typedRoute.type)
 | 
			
		||||
          ) {
 | 
			
		||||
            passengerDistance = typedRoute.route.distance;
 | 
			
		||||
            passengerDuration = typedRoute.route.duration;
 | 
			
		||||
            if (!points)
 | 
			
		||||
| 
						 | 
				
			
			@ -95,6 +111,7 @@ export class CreateAdService implements ICommandHandler {
 | 
			
		|||
      } catch (error: any) {
 | 
			
		||||
        throw new Error('Invalid route');
 | 
			
		||||
      }
 | 
			
		||||
 | 
			
		||||
      const ad = AdEntity.create({
 | 
			
		||||
        id: command.id,
 | 
			
		||||
        driver: command.driver,
 | 
			
		||||
| 
						 | 
				
			
			@ -115,6 +132,7 @@ export class CreateAdService implements ICommandHandler {
 | 
			
		|||
        fwdAzimuth: fwdAzimuth as number,
 | 
			
		||||
        backAzimuth: backAzimuth as number,
 | 
			
		||||
      });
 | 
			
		||||
 | 
			
		||||
      try {
 | 
			
		||||
        await this.repository.insertExtra(ad, 'ad');
 | 
			
		||||
        return ad.id;
 | 
			
		||||
| 
						 | 
				
			
			@ -124,5 +142,21 @@ export class CreateAdService implements ICommandHandler {
 | 
			
		|||
        }
 | 
			
		||||
        throw error;
 | 
			
		||||
      }
 | 
			
		||||
    } catch (error: any) {
 | 
			
		||||
      const matcherAdCreationFailedIntegrationEvent =
 | 
			
		||||
        new MatcherAdCreationFailedIntegrationEvent({
 | 
			
		||||
          id: command.id,
 | 
			
		||||
          metadata: {
 | 
			
		||||
            correlationId: command.id,
 | 
			
		||||
            timestamp: Date.now(),
 | 
			
		||||
          },
 | 
			
		||||
          cause: error.message,
 | 
			
		||||
        });
 | 
			
		||||
      this.messagePublisher.publish(
 | 
			
		||||
        MATCHER_AD_CREATION_FAILED_ROUTING_KEY,
 | 
			
		||||
        JSON.stringify(matcherAdCreationFailedIntegrationEvent),
 | 
			
		||||
      );
 | 
			
		||||
      throw error;
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
import { Inject, Injectable } from '@nestjs/common';
 | 
			
		||||
import { OnEvent } from '@nestjs/event-emitter';
 | 
			
		||||
import { MatcherAdCreatedDomainEvent } from '../../domain/events/matcher-ad-created.domain-event';
 | 
			
		||||
import { MessagePublisherPort } from '@mobicoop/ddd-library';
 | 
			
		||||
import { AD_MESSAGE_PUBLISHER } from '@modules/ad/ad.di-tokens';
 | 
			
		||||
import { MATCHER_AD_CREATED_ROUTING_KEY } from '@src/app.constants';
 | 
			
		||||
import { MatcherAdCreatedIntegrationEvent } from '../events/matcher-ad-created.integration-event';
 | 
			
		||||
 | 
			
		||||
@Injectable()
 | 
			
		||||
export class PublishMessageWhenMatcherAdIsCreatedDomainEventHandler {
 | 
			
		||||
  constructor(
 | 
			
		||||
    @Inject(AD_MESSAGE_PUBLISHER)
 | 
			
		||||
    private readonly messagePublisher: MessagePublisherPort,
 | 
			
		||||
  ) {}
 | 
			
		||||
 | 
			
		||||
  @OnEvent(MatcherAdCreatedDomainEvent.name, { async: true, promisify: true })
 | 
			
		||||
  async handle(event: MatcherAdCreatedDomainEvent): Promise<any> {
 | 
			
		||||
    const matcherAdCreatedIntegrationEvent =
 | 
			
		||||
      new MatcherAdCreatedIntegrationEvent({
 | 
			
		||||
        id: event.aggregateId,
 | 
			
		||||
        driverDuration: event.driverDuration,
 | 
			
		||||
        driverDistance: event.driverDistance,
 | 
			
		||||
        passengerDuration: event.passengerDuration,
 | 
			
		||||
        passengerDistance: event.passengerDistance,
 | 
			
		||||
        fwdAzimuth: event.fwdAzimuth,
 | 
			
		||||
        backAzimuth: event.backAzimuth,
 | 
			
		||||
        metadata: event.metadata,
 | 
			
		||||
      });
 | 
			
		||||
    this.messagePublisher.publish(
 | 
			
		||||
      MATCHER_AD_CREATED_ROUTING_KEY,
 | 
			
		||||
      JSON.stringify(matcherAdCreatedIntegrationEvent),
 | 
			
		||||
    );
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
import { IntegrationEvent, IntegrationEventProps } from '@mobicoop/ddd-library';
 | 
			
		||||
 | 
			
		||||
export class MatcherAdCreatedIntegrationEvent extends IntegrationEvent {
 | 
			
		||||
  readonly driverDuration?: number;
 | 
			
		||||
  readonly driverDistance?: number;
 | 
			
		||||
  readonly passengerDuration?: number;
 | 
			
		||||
  readonly passengerDistance?: number;
 | 
			
		||||
  readonly fwdAzimuth: number;
 | 
			
		||||
  readonly backAzimuth: number;
 | 
			
		||||
 | 
			
		||||
  constructor(props: IntegrationEventProps<MatcherAdCreatedIntegrationEvent>) {
 | 
			
		||||
    super(props);
 | 
			
		||||
    this.driverDuration = props.driverDuration;
 | 
			
		||||
    this.driverDistance = props.driverDistance;
 | 
			
		||||
    this.passengerDuration = props.passengerDuration;
 | 
			
		||||
    this.passengerDistance = props.passengerDistance;
 | 
			
		||||
    this.fwdAzimuth = props.fwdAzimuth;
 | 
			
		||||
    this.backAzimuth = props.backAzimuth;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
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;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -21,7 +21,6 @@ export abstract class Algorithm {
 | 
			
		|||
    for (const processor of this.processors) {
 | 
			
		||||
      this.candidates = await processor.execute(this.candidates);
 | 
			
		||||
    }
 | 
			
		||||
    // console.log(JSON.stringify(this.candidates, null, 2));
 | 
			
		||||
    return this.candidates.map((candidate: CandidateEntity) =>
 | 
			
		||||
      MatchEntity.create({
 | 
			
		||||
        adId: candidate.id,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,4 +1,4 @@
 | 
			
		|||
import { Frequency, Role } from '@modules/ad/core/domain/ad.types';
 | 
			
		||||
import { AdStatus, Frequency, Role } from '@modules/ad/core/domain/ad.types';
 | 
			
		||||
import { Selector } from '../algorithm.abstract';
 | 
			
		||||
import { Waypoint } from '../../../types/waypoint.type';
 | 
			
		||||
import { Point } from '../../../types/point.type';
 | 
			
		||||
| 
						 | 
				
			
			@ -133,6 +133,7 @@ export class PassengerOrientedSelector extends Selector {
 | 
			
		|||
 | 
			
		||||
  private _createWhere = (role: Role): string =>
 | 
			
		||||
    [
 | 
			
		||||
      this._whereStatus(),
 | 
			
		||||
      this._whereRole(role),
 | 
			
		||||
      this._whereStrict(),
 | 
			
		||||
      this._whereDate(),
 | 
			
		||||
| 
						 | 
				
			
			@ -144,6 +145,8 @@ export class PassengerOrientedSelector extends Selector {
 | 
			
		|||
      .filter((where: string) => where != '')
 | 
			
		||||
      .join(' AND ');
 | 
			
		||||
 | 
			
		||||
  private _whereStatus = (): string => `status='${AdStatus.VALID}'`;
 | 
			
		||||
 | 
			
		||||
  private _whereRole = (role: Role): string =>
 | 
			
		||||
    role == Role.PASSENGER ? 'driver=True' : 'passenger=True';
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -174,7 +177,7 @@ export class PassengerOrientedSelector extends Selector {
 | 
			
		|||
  private _whereSchedule = (role: Role): string => {
 | 
			
		||||
    const schedule: string[] = [];
 | 
			
		||||
    // we need full dates to compare times, because margins can lead to compare on previous or next day
 | 
			
		||||
    // -first we establish a base calendar (up to a week)
 | 
			
		||||
    // - first we establish a base calendar (up to a week)
 | 
			
		||||
    const scheduleDates: Date[] = this._datesBetweenBoundaries(
 | 
			
		||||
      this.query.fromDate,
 | 
			
		||||
      this.query.toDate,
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,12 +1,29 @@
 | 
			
		|||
import { AggregateRoot, AggregateID } from '@mobicoop/ddd-library';
 | 
			
		||||
import { AdProps, CreateAdProps } from './ad.types';
 | 
			
		||||
import { MatcherAdCreatedDomainEvent } from './events/matcher-ad-created.domain-event';
 | 
			
		||||
 | 
			
		||||
export class AdEntity extends AggregateRoot<AdProps> {
 | 
			
		||||
  protected readonly _id: AggregateID;
 | 
			
		||||
 | 
			
		||||
  static create = (create: CreateAdProps): AdEntity => {
 | 
			
		||||
    const props: AdProps = { ...create };
 | 
			
		||||
    return new AdEntity({ id: create.id, props });
 | 
			
		||||
    const ad = new AdEntity({ id: create.id, props });
 | 
			
		||||
    ad.addEvent(
 | 
			
		||||
      new MatcherAdCreatedDomainEvent({
 | 
			
		||||
        metadata: {
 | 
			
		||||
          correlationId: create.id,
 | 
			
		||||
          timestamp: Date.now(),
 | 
			
		||||
        },
 | 
			
		||||
        aggregateId: create.id,
 | 
			
		||||
        driverDistance: create.driverDistance,
 | 
			
		||||
        driverDuration: create.driverDuration,
 | 
			
		||||
        passengerDistance: create.passengerDistance,
 | 
			
		||||
        passengerDuration: create.passengerDuration,
 | 
			
		||||
        fwdAzimuth: create.fwdAzimuth,
 | 
			
		||||
        backAzimuth: create.backAzimuth,
 | 
			
		||||
      }),
 | 
			
		||||
    );
 | 
			
		||||
    return ad;
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  validate(): void {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -53,3 +53,10 @@ export enum Role {
 | 
			
		|||
  DRIVER = 'DRIVER',
 | 
			
		||||
  PASSENGER = 'PASSENGER',
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export enum AdStatus {
 | 
			
		||||
  PENDING = 'PENDING',
 | 
			
		||||
  VALID = 'VALID',
 | 
			
		||||
  INVALID = 'INVALID',
 | 
			
		||||
  SUSPENDED = 'SUSPENDED',
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,20 @@
 | 
			
		|||
import { DomainEvent, DomainEventProps } from '@mobicoop/ddd-library';
 | 
			
		||||
 | 
			
		||||
export class MatcherAdCreatedDomainEvent extends DomainEvent {
 | 
			
		||||
  readonly driverDuration?: number;
 | 
			
		||||
  readonly driverDistance?: number;
 | 
			
		||||
  readonly passengerDuration?: number;
 | 
			
		||||
  readonly passengerDistance?: number;
 | 
			
		||||
  readonly fwdAzimuth: number;
 | 
			
		||||
  readonly backAzimuth: number;
 | 
			
		||||
 | 
			
		||||
  constructor(props: DomainEventProps<MatcherAdCreatedDomainEvent>) {
 | 
			
		||||
    super(props);
 | 
			
		||||
    this.driverDuration = props.driverDuration;
 | 
			
		||||
    this.driverDistance = props.driverDistance;
 | 
			
		||||
    this.passengerDuration = props.passengerDuration;
 | 
			
		||||
    this.passengerDistance = props.passengerDistance;
 | 
			
		||||
    this.fwdAzimuth = props.fwdAzimuth;
 | 
			
		||||
    this.backAzimuth = props.backAzimuth;
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -13,7 +13,6 @@ export class AdCreatedMessageHandler {
 | 
			
		|||
    name: AD_CREATED_MESSAGE_HANDLER,
 | 
			
		||||
  })
 | 
			
		||||
  public async adCreated(message: string) {
 | 
			
		||||
    try {
 | 
			
		||||
    const createdAd: Ad = JSON.parse(message);
 | 
			
		||||
    await this.commandBus.execute(
 | 
			
		||||
      new CreateAdCommand({
 | 
			
		||||
| 
						 | 
				
			
			@ -30,8 +29,5 @@ export class AdCreatedMessageHandler {
 | 
			
		|||
        waypoints: createdAd.waypoints,
 | 
			
		||||
      }),
 | 
			
		||||
    );
 | 
			
		||||
    } catch (e: any) {
 | 
			
		||||
      console.log(e);
 | 
			
		||||
    }
 | 
			
		||||
  }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,9 @@
 | 
			
		|||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { AD_REPOSITORY, AD_ROUTE_PROVIDER } from '@modules/ad/ad.di-tokens';
 | 
			
		||||
import {
 | 
			
		||||
  AD_MESSAGE_PUBLISHER,
 | 
			
		||||
  AD_REPOSITORY,
 | 
			
		||||
  AD_ROUTE_PROVIDER,
 | 
			
		||||
} from '@modules/ad/ad.di-tokens';
 | 
			
		||||
import { AggregateID } from '@mobicoop/ddd-library';
 | 
			
		||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
 | 
			
		||||
import { ConflictException } from '@mobicoop/ddd-library';
 | 
			
		||||
| 
						 | 
				
			
			@ -96,6 +100,10 @@ const mockRouteProvider: RouteProviderPort = {
 | 
			
		|||
  getDetailed: jest.fn(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
const mockMessagePublisher = {
 | 
			
		||||
  publish: jest.fn().mockImplementation(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('create-ad.service', () => {
 | 
			
		||||
  let createAdService: CreateAdService;
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -110,6 +118,10 @@ describe('create-ad.service', () => {
 | 
			
		|||
          provide: AD_ROUTE_PROVIDER,
 | 
			
		||||
          useValue: mockRouteProvider,
 | 
			
		||||
        },
 | 
			
		||||
        {
 | 
			
		||||
          provide: AD_MESSAGE_PUBLISHER,
 | 
			
		||||
          useValue: mockMessagePublisher,
 | 
			
		||||
        },
 | 
			
		||||
        CreateAdService,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,57 @@
 | 
			
		|||
import { Test, TestingModule } from '@nestjs/testing';
 | 
			
		||||
import { AD_MESSAGE_PUBLISHER } from '@modules/ad/ad.di-tokens';
 | 
			
		||||
import { MATCHER_AD_CREATED_ROUTING_KEY } from '@src/app.constants';
 | 
			
		||||
import { PublishMessageWhenMatcherAdIsCreatedDomainEventHandler } from '@modules/ad/core/application/event-handlers/publish-message-when-matcher-ad-is-created.domain-event-handler';
 | 
			
		||||
import { MatcherAdCreatedDomainEvent } from '@modules/ad/core/domain/events/matcher-ad-created.domain-event';
 | 
			
		||||
 | 
			
		||||
const mockMessagePublisher = {
 | 
			
		||||
  publish: jest.fn().mockImplementation(),
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
describe('Publish message when matcher ad is created domain event handler', () => {
 | 
			
		||||
  let publishMessageWhenMatcherAdIsCreatedDomainEventHandler: PublishMessageWhenMatcherAdIsCreatedDomainEventHandler;
 | 
			
		||||
 | 
			
		||||
  beforeAll(async () => {
 | 
			
		||||
    const module: TestingModule = await Test.createTestingModule({
 | 
			
		||||
      providers: [
 | 
			
		||||
        {
 | 
			
		||||
          provide: AD_MESSAGE_PUBLISHER,
 | 
			
		||||
          useValue: mockMessagePublisher,
 | 
			
		||||
        },
 | 
			
		||||
        PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
 | 
			
		||||
      ],
 | 
			
		||||
    }).compile();
 | 
			
		||||
 | 
			
		||||
    publishMessageWhenMatcherAdIsCreatedDomainEventHandler =
 | 
			
		||||
      module.get<PublishMessageWhenMatcherAdIsCreatedDomainEventHandler>(
 | 
			
		||||
        PublishMessageWhenMatcherAdIsCreatedDomainEventHandler,
 | 
			
		||||
      );
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  it('should publish a message', () => {
 | 
			
		||||
    jest.spyOn(mockMessagePublisher, 'publish');
 | 
			
		||||
    const matcherAdCreatedDomainEvent: MatcherAdCreatedDomainEvent = {
 | 
			
		||||
      id: 'some-domain-event-id',
 | 
			
		||||
      aggregateId: 'some-aggregate-id',
 | 
			
		||||
      metadata: {
 | 
			
		||||
        timestamp: new Date('2023-06-28T05:00:00Z').getTime(),
 | 
			
		||||
        correlationId: 'some-correlation-id',
 | 
			
		||||
      },
 | 
			
		||||
      driverDistance: 65845,
 | 
			
		||||
      driverDuration: 3254,
 | 
			
		||||
      fwdAzimuth: 90,
 | 
			
		||||
      backAzimuth: 270,
 | 
			
		||||
    };
 | 
			
		||||
    publishMessageWhenMatcherAdIsCreatedDomainEventHandler.handle(
 | 
			
		||||
      matcherAdCreatedDomainEvent,
 | 
			
		||||
    );
 | 
			
		||||
    expect(
 | 
			
		||||
      publishMessageWhenMatcherAdIsCreatedDomainEventHandler,
 | 
			
		||||
    ).toBeDefined();
 | 
			
		||||
    expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
 | 
			
		||||
    expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
 | 
			
		||||
      MATCHER_AD_CREATED_ROUTING_KEY,
 | 
			
		||||
      '{"id":"some-aggregate-id","metadata":{"correlationId":"some-correlation-id","timestamp":1687928400000},"driverDuration":3254,"driverDistance":65845,"fwdAzimuth":90,"backAzimuth":270}',
 | 
			
		||||
    );
 | 
			
		||||
  });
 | 
			
		||||
});
 | 
			
		||||
		Loading…
	
		Reference in New Issue