refactor adapters
This commit is contained in:
parent
c530bc55f5
commit
b55e122bef
|
@ -97,7 +97,7 @@
|
||||||
"main.ts"
|
"main.ts"
|
||||||
],
|
],
|
||||||
"rootDir": "src",
|
"rootDir": "src",
|
||||||
"testRegex": ".converter.*\\.spec\\.ts$",
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
"transform": {
|
"transform": {
|
||||||
"^.+\\.(t|j)s$": "ts-jest"
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
},
|
},
|
||||||
|
|
|
@ -2,4 +2,5 @@ export const AD_MESSAGE_PUBLISHER = Symbol('AD_MESSAGE_PUBLISHER');
|
||||||
export const PARAMS_PROVIDER = Symbol('PARAMS_PROVIDER');
|
export const PARAMS_PROVIDER = Symbol('PARAMS_PROVIDER');
|
||||||
export const TIMEZONE_FINDER = Symbol('TIMEZONE_FINDER');
|
export const TIMEZONE_FINDER = Symbol('TIMEZONE_FINDER');
|
||||||
export const TIME_CONVERTER = Symbol('TIME_CONVERTER');
|
export const TIME_CONVERTER = Symbol('TIME_CONVERTER');
|
||||||
|
export const DATETIME_TRANSFORMER = Symbol('DATETIME_TRANSFORMER');
|
||||||
export const AD_REPOSITORY = Symbol('AD_REPOSITORY');
|
export const AD_REPOSITORY = Symbol('AD_REPOSITORY');
|
||||||
|
|
|
@ -4,6 +4,7 @@ import { CqrsModule } from '@nestjs/cqrs';
|
||||||
import {
|
import {
|
||||||
AD_MESSAGE_PUBLISHER,
|
AD_MESSAGE_PUBLISHER,
|
||||||
AD_REPOSITORY,
|
AD_REPOSITORY,
|
||||||
|
DATETIME_TRANSFORMER,
|
||||||
PARAMS_PROVIDER,
|
PARAMS_PROVIDER,
|
||||||
TIMEZONE_FINDER,
|
TIMEZONE_FINDER,
|
||||||
TIME_CONVERTER,
|
TIME_CONVERTER,
|
||||||
|
@ -19,6 +20,7 @@ import { FindAdByIdQueryHandler } from './core/application/queries/find-ad-by-id
|
||||||
import { PublishMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler';
|
import { PublishMessageWhenAdIsCreatedDomainEventHandler } from './core/application/event-handlers/publish-message-when-ad-is-created.domain-event-handler';
|
||||||
import { PrismaService } from './infrastructure/prisma.service';
|
import { PrismaService } from './infrastructure/prisma.service';
|
||||||
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
import { MessageBrokerPublisher } from '@mobicoop/message-broker-module';
|
||||||
|
import { DateTimeTransformer } from './infrastructure/datetime-transformer';
|
||||||
|
|
||||||
const grpcControllers = [CreateAdGrpcController, FindAdByIdGrpcController];
|
const grpcControllers = [CreateAdGrpcController, FindAdByIdGrpcController];
|
||||||
|
|
||||||
|
@ -60,6 +62,10 @@ const adapters: Provider[] = [
|
||||||
provide: TIME_CONVERTER,
|
provide: TIME_CONVERTER,
|
||||||
useClass: TimeConverter,
|
useClass: TimeConverter,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: DATETIME_TRANSFORMER,
|
||||||
|
useClass: DateTimeTransformer,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
|
|
|
@ -3,9 +3,8 @@ import { CreateAdCommand } from './create-ad.command';
|
||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import {
|
import {
|
||||||
AD_REPOSITORY,
|
AD_REPOSITORY,
|
||||||
|
DATETIME_TRANSFORMER,
|
||||||
PARAMS_PROVIDER,
|
PARAMS_PROVIDER,
|
||||||
TIMEZONE_FINDER,
|
|
||||||
TIME_CONVERTER,
|
|
||||||
} from '@modules/ad/ad.di-tokens';
|
} from '@modules/ad/ad.di-tokens';
|
||||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||||
import { Waypoint } from '../../types/waypoint';
|
import { Waypoint } from '../../types/waypoint';
|
||||||
|
@ -15,9 +14,7 @@ import { DefaultParamsProviderPort } from '../../ports/default-params-provider.p
|
||||||
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
|
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
|
||||||
import { AggregateID, ConflictException } from '@mobicoop/ddd-library';
|
import { AggregateID, ConflictException } from '@mobicoop/ddd-library';
|
||||||
import { ScheduleItem } from '../../types/schedule-item';
|
import { ScheduleItem } from '../../types/schedule-item';
|
||||||
import { TimeConverterPort } from '../../ports/time-converter.port';
|
import { DateTimeTransformerPort } from '../../ports/datetime-transformer.port';
|
||||||
import { TimezoneFinderPort } from '../../ports/timezone-finder.port';
|
|
||||||
import { Frequency } from '@modules/ad/core/domain/ad.types';
|
|
||||||
|
|
||||||
@CommandHandler(CreateAdCommand)
|
@CommandHandler(CreateAdCommand)
|
||||||
export class CreateAdService implements ICommandHandler {
|
export class CreateAdService implements ICommandHandler {
|
||||||
|
@ -28,52 +25,65 @@ export class CreateAdService implements ICommandHandler {
|
||||||
private readonly repository: AdRepositoryPort,
|
private readonly repository: AdRepositoryPort,
|
||||||
@Inject(PARAMS_PROVIDER)
|
@Inject(PARAMS_PROVIDER)
|
||||||
private readonly defaultParamsProvider: DefaultParamsProviderPort,
|
private readonly defaultParamsProvider: DefaultParamsProviderPort,
|
||||||
@Inject(TIMEZONE_FINDER)
|
@Inject(DATETIME_TRANSFORMER)
|
||||||
private readonly timezoneFinder: TimezoneFinderPort,
|
private readonly datetimeTransformer: DateTimeTransformerPort,
|
||||||
@Inject(TIME_CONVERTER)
|
|
||||||
private readonly timeConverter: TimeConverterPort,
|
|
||||||
) {
|
) {
|
||||||
this._defaultParams = defaultParamsProvider.getParams();
|
this._defaultParams = defaultParamsProvider.getParams();
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute(command: CreateAdCommand): Promise<AggregateID> {
|
async execute(command: CreateAdCommand): Promise<AggregateID> {
|
||||||
const timezone = this.timezoneFinder.timezones(
|
|
||||||
command.waypoints[0].lon,
|
|
||||||
command.waypoints[0].lat,
|
|
||||||
this._defaultParams.DEFAULT_TIMEZONE,
|
|
||||||
)[0];
|
|
||||||
const ad = AdEntity.create(
|
const ad = AdEntity.create(
|
||||||
{
|
{
|
||||||
userId: command.userId,
|
userId: command.userId,
|
||||||
driver: command.driver,
|
driver: command.driver,
|
||||||
passenger: command.passenger,
|
passenger: command.passenger,
|
||||||
frequency: command.frequency,
|
frequency: command.frequency,
|
||||||
fromDate: this.getFromDate(
|
fromDate: this.datetimeTransformer.fromDate(
|
||||||
command.fromDate,
|
{
|
||||||
|
date: command.fromDate,
|
||||||
|
time: command.schedule[0].time,
|
||||||
|
coordinates: {
|
||||||
|
lon: command.waypoints[0].lon,
|
||||||
|
lat: command.waypoints[0].lat,
|
||||||
|
},
|
||||||
|
},
|
||||||
command.frequency,
|
command.frequency,
|
||||||
command.schedule[0].time,
|
|
||||||
timezone,
|
|
||||||
),
|
),
|
||||||
toDate: this.getToDate(
|
toDate: this.datetimeTransformer.toDate(
|
||||||
command.fromDate,
|
|
||||||
command.toDate,
|
command.toDate,
|
||||||
|
{
|
||||||
|
date: command.fromDate,
|
||||||
|
time: command.schedule[0].time,
|
||||||
|
coordinates: {
|
||||||
|
lon: command.waypoints[0].lon,
|
||||||
|
lat: command.waypoints[0].lat,
|
||||||
|
},
|
||||||
|
},
|
||||||
command.frequency,
|
command.frequency,
|
||||||
command.schedule[0].time,
|
|
||||||
timezone,
|
|
||||||
),
|
),
|
||||||
schedule: command.schedule.map((scheduleItem: ScheduleItem) => ({
|
schedule: command.schedule.map((scheduleItem: ScheduleItem) => ({
|
||||||
day: this.getDay(
|
day: this.datetimeTransformer.day(
|
||||||
scheduleItem.day,
|
scheduleItem.day,
|
||||||
command.fromDate,
|
{
|
||||||
|
date: command.fromDate,
|
||||||
|
time: scheduleItem.time,
|
||||||
|
coordinates: {
|
||||||
|
lon: command.waypoints[0].lon,
|
||||||
|
lat: command.waypoints[0].lat,
|
||||||
|
},
|
||||||
|
},
|
||||||
command.frequency,
|
command.frequency,
|
||||||
scheduleItem.time,
|
|
||||||
timezone,
|
|
||||||
),
|
),
|
||||||
time: this.getTime(
|
time: this.datetimeTransformer.time(
|
||||||
command.fromDate,
|
{
|
||||||
|
date: command.fromDate,
|
||||||
|
time: scheduleItem.time,
|
||||||
|
coordinates: {
|
||||||
|
lon: command.waypoints[0].lon,
|
||||||
|
lat: command.waypoints[0].lat,
|
||||||
|
},
|
||||||
|
},
|
||||||
command.frequency,
|
command.frequency,
|
||||||
scheduleItem.time,
|
|
||||||
timezone,
|
|
||||||
),
|
),
|
||||||
margin: scheduleItem.margin,
|
margin: scheduleItem.margin,
|
||||||
})),
|
})),
|
||||||
|
@ -116,71 +126,4 @@ export class CreateAdService implements ICommandHandler {
|
||||||
throw error;
|
throw error;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private getFromDate = (
|
|
||||||
fromDate: string,
|
|
||||||
frequency: Frequency,
|
|
||||||
time: string,
|
|
||||||
timezone: string,
|
|
||||||
): string => {
|
|
||||||
if (frequency === Frequency.RECURRENT) return fromDate;
|
|
||||||
return this.timeConverter
|
|
||||||
.localStringDateTimeToUtcDate(fromDate, time, timezone)
|
|
||||||
.toISOString();
|
|
||||||
};
|
|
||||||
|
|
||||||
private getToDate = (
|
|
||||||
fromDate: string,
|
|
||||||
toDate: string,
|
|
||||||
frequency: Frequency,
|
|
||||||
time: string,
|
|
||||||
timezone: string,
|
|
||||||
): string => {
|
|
||||||
if (frequency === Frequency.RECURRENT) return toDate;
|
|
||||||
return this.getFromDate(fromDate, frequency, time, timezone);
|
|
||||||
};
|
|
||||||
|
|
||||||
private getDay = (
|
|
||||||
day: number,
|
|
||||||
fromDate: string,
|
|
||||||
frequency: Frequency,
|
|
||||||
time: string,
|
|
||||||
timezone: string,
|
|
||||||
): number => {
|
|
||||||
if (frequency === Frequency.RECURRENT)
|
|
||||||
return this.getRecurrentDay(day, time, timezone);
|
|
||||||
return new Date(
|
|
||||||
this.getFromDate(fromDate, frequency, time, timezone),
|
|
||||||
).getDay();
|
|
||||||
};
|
|
||||||
|
|
||||||
private getTime = (
|
|
||||||
fromDate: string,
|
|
||||||
frequency: Frequency,
|
|
||||||
time: string,
|
|
||||||
timezone: string,
|
|
||||||
): string => {
|
|
||||||
if (frequency === Frequency.RECURRENT)
|
|
||||||
return this.timeConverter.localStringTimeToUtcStringTime(time, timezone);
|
|
||||||
return new Date(
|
|
||||||
this.getFromDate(fromDate, frequency, time, timezone),
|
|
||||||
).toTimeString();
|
|
||||||
};
|
|
||||||
|
|
||||||
private getRecurrentDay = (
|
|
||||||
day: number,
|
|
||||||
time: string,
|
|
||||||
timezone: string,
|
|
||||||
): number => {
|
|
||||||
// continuer ici
|
|
||||||
const baseDate = new Date('1970-01-01T00:00:00Z');
|
|
||||||
const hour = parseInt(time.split(':')[0]);
|
|
||||||
const utcHour = parseInt(
|
|
||||||
this.timeConverter
|
|
||||||
.localStringTimeToUtcStringTime(time, timezone)
|
|
||||||
.split(':')[0],
|
|
||||||
);
|
|
||||||
if (utcHour >= 11 && hour < 13) return day > 0 ? day - 1 : 6;
|
|
||||||
return day;
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
export interface DateTimeTransformerPort {
|
||||||
|
fromDate(geoFromDate: GeoDateTime, frequency: Frequency): string;
|
||||||
|
toDate(
|
||||||
|
toDate: string,
|
||||||
|
geoFromDate: GeoDateTime,
|
||||||
|
frequency: Frequency,
|
||||||
|
): string;
|
||||||
|
day(day: number, geoFromDate: GeoDateTime, frequency: Frequency): number;
|
||||||
|
time(geoFromDate: GeoDateTime, frequency: Frequency): string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export type GeoDateTime = {
|
||||||
|
date: string;
|
||||||
|
time: string;
|
||||||
|
coordinates: Coordinates;
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Coordinates = {
|
||||||
|
lon: number;
|
||||||
|
lat: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
export enum Frequency {
|
||||||
|
PUNCTUAL = 'PUNCTUAL',
|
||||||
|
RECURRENT = 'RECURRENT',
|
||||||
|
}
|
|
@ -6,5 +6,5 @@ export interface TimeConverterPort {
|
||||||
timezone: string,
|
timezone: string,
|
||||||
dst?: boolean,
|
dst?: boolean,
|
||||||
): Date;
|
): Date;
|
||||||
utcDatetimeToLocalTime(isoString: string, timezone: string): string;
|
utcUnixEpochDayFromTime(time: string, timezone: string): number;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
export interface TimezoneFinderPort {
|
export interface TimezoneFinderPort {
|
||||||
timezones(lon: number, lat: number, defaultTimezone?: string): string[];
|
timezones(lon: number, lat: number, defaultTimezone?: string): string[];
|
||||||
offset(timezone: string): number;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,7 @@ export class AdCreatedDomainEvent extends DomainEvent {
|
||||||
readonly frequency: string;
|
readonly frequency: string;
|
||||||
readonly fromDate: string;
|
readonly fromDate: string;
|
||||||
readonly toDate: string;
|
readonly toDate: string;
|
||||||
readonly schedule: ScheduleDay[];
|
readonly schedule: ScheduleItem[];
|
||||||
readonly seatsProposed: number;
|
readonly seatsProposed: number;
|
||||||
readonly seatsRequested: number;
|
readonly seatsRequested: number;
|
||||||
readonly strict: boolean;
|
readonly strict: boolean;
|
||||||
|
@ -29,7 +29,7 @@ export class AdCreatedDomainEvent extends DomainEvent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ScheduleDay {
|
export class ScheduleItem {
|
||||||
day: number;
|
day: number;
|
||||||
time: string;
|
time: string;
|
||||||
margin: number;
|
margin: number;
|
||||||
|
|
|
@ -6,13 +6,13 @@ import { ValueObject } from '@mobicoop/ddd-library';
|
||||||
* */
|
* */
|
||||||
|
|
||||||
export interface ScheduleItemProps {
|
export interface ScheduleItemProps {
|
||||||
day: number;
|
day?: number;
|
||||||
time: string;
|
time: string;
|
||||||
margin?: number;
|
margin?: number;
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ScheduleItem extends ValueObject<ScheduleItemProps> {
|
export class ScheduleItem extends ValueObject<ScheduleItemProps> {
|
||||||
get day(): number {
|
get day(): number | undefined {
|
||||||
return this.props.day;
|
return this.props.day;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,132 @@
|
||||||
|
import { Inject, Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
DateTimeTransformerPort,
|
||||||
|
Frequency,
|
||||||
|
GeoDateTime,
|
||||||
|
} from '../core/application/ports/datetime-transformer.port';
|
||||||
|
import { TimeConverterPort } from '../core/application/ports/time-converter.port';
|
||||||
|
import {
|
||||||
|
PARAMS_PROVIDER,
|
||||||
|
TIMEZONE_FINDER,
|
||||||
|
TIME_CONVERTER,
|
||||||
|
} from '../ad.di-tokens';
|
||||||
|
import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port';
|
||||||
|
import { DefaultParamsProviderPort } from '../core/application/ports/default-params-provider.port';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class DateTimeTransformer implements DateTimeTransformerPort {
|
||||||
|
private readonly _defaultTimezone: string;
|
||||||
|
constructor(
|
||||||
|
@Inject(PARAMS_PROVIDER)
|
||||||
|
private readonly defaultParamsProvider: DefaultParamsProviderPort,
|
||||||
|
@Inject(TIMEZONE_FINDER)
|
||||||
|
private readonly timezoneFinder: TimezoneFinderPort,
|
||||||
|
@Inject(TIME_CONVERTER) private readonly timeConverter: TimeConverterPort,
|
||||||
|
) {
|
||||||
|
this._defaultTimezone = defaultParamsProvider.getParams().DEFAULT_TIMEZONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Compute the fromDate : if an ad is punctual, the departure date
|
||||||
|
* is converted to UTC with the time and timezone
|
||||||
|
*/
|
||||||
|
fromDate = (geoFromDate: GeoDateTime, frequency: Frequency): string => {
|
||||||
|
if (frequency === Frequency.RECURRENT) return geoFromDate.date;
|
||||||
|
return this.timeConverter
|
||||||
|
.localStringDateTimeToUtcDate(
|
||||||
|
geoFromDate.date,
|
||||||
|
geoFromDate.time,
|
||||||
|
this.timezoneFinder.timezones(
|
||||||
|
geoFromDate.coordinates.lon,
|
||||||
|
geoFromDate.coordinates.lat,
|
||||||
|
this._defaultTimezone,
|
||||||
|
)[0],
|
||||||
|
)
|
||||||
|
.toISOString()
|
||||||
|
.split('T')[0];
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the toDate depending on frequency, time and timezone :
|
||||||
|
* if the ad is punctual, the toDate is equal to the fromDate
|
||||||
|
*/
|
||||||
|
toDate = (
|
||||||
|
toDate: string,
|
||||||
|
geoFromDate: GeoDateTime,
|
||||||
|
frequency: Frequency,
|
||||||
|
): string => {
|
||||||
|
if (frequency === Frequency.RECURRENT) return toDate;
|
||||||
|
return this.fromDate(geoFromDate, frequency);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the day for a schedule item :
|
||||||
|
* - if the ad is punctual, the day is infered from fromDate
|
||||||
|
* - if the ad is recurrent, the day is computed by converting the time to utc
|
||||||
|
*/
|
||||||
|
day = (
|
||||||
|
day: number,
|
||||||
|
geoFromDate: GeoDateTime,
|
||||||
|
frequency: Frequency,
|
||||||
|
): number => {
|
||||||
|
if (frequency === Frequency.RECURRENT)
|
||||||
|
return this.recurrentDay(
|
||||||
|
day,
|
||||||
|
geoFromDate.time,
|
||||||
|
this.timezoneFinder.timezones(
|
||||||
|
geoFromDate.coordinates.lon,
|
||||||
|
geoFromDate.coordinates.lat,
|
||||||
|
this._defaultTimezone,
|
||||||
|
)[0],
|
||||||
|
);
|
||||||
|
return new Date(this.fromDate(geoFromDate, frequency)).getDay();
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the utc time
|
||||||
|
*/
|
||||||
|
time = (geoFromDate: GeoDateTime, frequency: Frequency): string => {
|
||||||
|
if (frequency === Frequency.RECURRENT)
|
||||||
|
return this.timeConverter.localStringTimeToUtcStringTime(
|
||||||
|
geoFromDate.time,
|
||||||
|
this.timezoneFinder.timezones(
|
||||||
|
geoFromDate.coordinates.lon,
|
||||||
|
geoFromDate.coordinates.lat,
|
||||||
|
this._defaultTimezone,
|
||||||
|
)[0],
|
||||||
|
);
|
||||||
|
return this.timeConverter
|
||||||
|
.localStringDateTimeToUtcDate(
|
||||||
|
geoFromDate.date,
|
||||||
|
geoFromDate.time,
|
||||||
|
this.timezoneFinder.timezones(
|
||||||
|
geoFromDate.coordinates.lon,
|
||||||
|
geoFromDate.coordinates.lat,
|
||||||
|
this._defaultTimezone,
|
||||||
|
)[0],
|
||||||
|
)
|
||||||
|
.toISOString()
|
||||||
|
.split('T')[1]
|
||||||
|
.split(':', 2)
|
||||||
|
.join(':');
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the day for a schedule item for a recurrent ad
|
||||||
|
* The day may change when transforming from local timezone to utc
|
||||||
|
*/
|
||||||
|
private recurrentDay = (
|
||||||
|
day: number,
|
||||||
|
time: string,
|
||||||
|
timezone: string,
|
||||||
|
): number => {
|
||||||
|
const unixEpochDay = 4; // 1970-01-01 is a thursday !
|
||||||
|
const utcBaseDay = this.timeConverter.utcUnixEpochDayFromTime(
|
||||||
|
time,
|
||||||
|
timezone,
|
||||||
|
);
|
||||||
|
if (unixEpochDay == utcBaseDay) return day;
|
||||||
|
if (unixEpochDay > utcBaseDay) return day > 0 ? day - 1 : 6;
|
||||||
|
return day < 6 ? day + 1 : 0;
|
||||||
|
};
|
||||||
|
}
|
|
@ -4,12 +4,12 @@ import { TimeConverterPort } from '../core/application/ports/time-converter.port
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimeConverter implements TimeConverterPort {
|
export class TimeConverter implements TimeConverterPort {
|
||||||
private readonly BASE_DATE = '1970-01-01';
|
private readonly UNIX_EPOCH = '1970-01-01';
|
||||||
|
|
||||||
localStringTimeToUtcStringTime = (time: string, timezone: string): string => {
|
localStringTimeToUtcStringTime = (time: string, timezone: string): string => {
|
||||||
try {
|
try {
|
||||||
if (!time || !timezone) throw new Error();
|
if (!time || !timezone) throw new Error();
|
||||||
return new DateTime(`${this.BASE_DATE}T${time}`, TimeZone.zone(timezone))
|
return new DateTime(`${this.UNIX_EPOCH}T${time}`, TimeZone.zone(timezone))
|
||||||
.convert(TimeZone.zone('UTC'))
|
.convert(TimeZone.zone('UTC'))
|
||||||
.format('HH:mm');
|
.format('HH:mm');
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
@ -25,21 +25,29 @@ export class TimeConverter implements TimeConverterPort {
|
||||||
): Date => {
|
): Date => {
|
||||||
try {
|
try {
|
||||||
if (!time || !timezone) throw new Error();
|
if (!time || !timezone) throw new Error();
|
||||||
return new DateTime(`${date}T${time}`, TimeZone.zone(timezone, dst))
|
return new Date(
|
||||||
.convert(TimeZone.zone('UTC'))
|
new DateTime(
|
||||||
.toDate();
|
`${date}T${time}`,
|
||||||
|
TimeZone.zone(timezone, dst),
|
||||||
|
).toIsoString(),
|
||||||
|
);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
utcDatetimeToLocalTime = (isoString: string, timezone: string): string => {
|
utcUnixEpochDayFromTime = (time: string, timezone: string): number => {
|
||||||
try {
|
try {
|
||||||
return new DateTime(isoString)
|
if (!time || !timezone) throw new Error();
|
||||||
.convert(TimeZone.zone(timezone))
|
return new Date(
|
||||||
.toString()
|
new DateTime(
|
||||||
.split('T')[1]
|
`${this.UNIX_EPOCH}T${time}`,
|
||||||
.substring(0, 5);
|
TimeZone.zone(timezone, false),
|
||||||
|
)
|
||||||
|
.convert(TimeZone.zone('UTC'))
|
||||||
|
.toIsoString()
|
||||||
|
.split('T')[0],
|
||||||
|
).getDay();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
import { Injectable } from '@nestjs/common';
|
import { Injectable } from '@nestjs/common';
|
||||||
import { find } from 'geo-tz';
|
import { find } from 'geo-tz';
|
||||||
import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port';
|
import { TimezoneFinderPort } from '../core/application/ports/timezone-finder.port';
|
||||||
import { zone } from 'timezonecomplete';
|
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TimezoneFinder implements TimezoneFinderPort {
|
export class TimezoneFinder implements TimezoneFinderPort {
|
||||||
|
@ -14,7 +13,4 @@ export class TimezoneFinder implements TimezoneFinderPort {
|
||||||
if (defaultTimezone && foundTimezones.length == 0) return [defaultTimezone];
|
if (defaultTimezone && foundTimezones.length == 0) return [defaultTimezone];
|
||||||
return foundTimezones;
|
return foundTimezones;
|
||||||
};
|
};
|
||||||
|
|
||||||
offset = (timezone: string): number =>
|
|
||||||
zone(timezone).offsetForUtc(1970, 1, 1, 0, 0, 0);
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,10 +1,4 @@
|
||||||
import {
|
import { AD_MESSAGE_PUBLISHER, AD_REPOSITORY } from '@modules/ad/ad.di-tokens';
|
||||||
AD_MESSAGE_PUBLISHER,
|
|
||||||
AD_REPOSITORY,
|
|
||||||
PARAMS_PROVIDER,
|
|
||||||
TIMEZONE_FINDER,
|
|
||||||
TIME_CONVERTER,
|
|
||||||
} from '@modules/ad/ad.di-tokens';
|
|
||||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||||
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
import { AdEntity } from '@modules/ad/core/domain/ad.entity';
|
||||||
import {
|
import {
|
||||||
|
@ -13,10 +7,7 @@ import {
|
||||||
Frequency,
|
Frequency,
|
||||||
} from '@modules/ad/core/domain/ad.types';
|
} from '@modules/ad/core/domain/ad.types';
|
||||||
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
||||||
import { DefaultParamsProvider } from '@modules/ad/infrastructure/default-params-provider';
|
|
||||||
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
||||||
import { TimeConverter } from '@modules/ad/infrastructure/time-converter';
|
|
||||||
import { TimezoneFinder } from '@modules/ad/infrastructure/timezone-finder';
|
|
||||||
import { ConfigModule } from '@nestjs/config';
|
import { ConfigModule } from '@nestjs/config';
|
||||||
import { EventEmitterModule } from '@nestjs/event-emitter';
|
import { EventEmitterModule } from '@nestjs/event-emitter';
|
||||||
import { Test } from '@nestjs/testing';
|
import { Test } from '@nestjs/testing';
|
||||||
|
@ -133,22 +124,6 @@ describe('Ad Repository', () => {
|
||||||
providers: [
|
providers: [
|
||||||
PrismaService,
|
PrismaService,
|
||||||
AdMapper,
|
AdMapper,
|
||||||
{
|
|
||||||
provide: AD_REPOSITORY,
|
|
||||||
useClass: AdRepository,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: PARAMS_PROVIDER,
|
|
||||||
useClass: DefaultParamsProvider,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: TIMEZONE_FINDER,
|
|
||||||
useClass: TimezoneFinder,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: TIME_CONVERTER,
|
|
||||||
useClass: TimeConverter,
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
provide: AD_MESSAGE_PUBLISHER,
|
provide: AD_MESSAGE_PUBLISHER,
|
||||||
useValue: mockMessagePublisher,
|
useValue: mockMessagePublisher,
|
||||||
|
@ -193,12 +168,11 @@ describe('Ad Repository', () => {
|
||||||
frequency: Frequency.PUNCTUAL,
|
frequency: Frequency.PUNCTUAL,
|
||||||
fromDate: '2023-02-01',
|
fromDate: '2023-02-01',
|
||||||
toDate: '2023-02-01',
|
toDate: '2023-02-01',
|
||||||
schedule: {
|
schedule: [
|
||||||
wed: '12:05',
|
{
|
||||||
},
|
time: '12:05',
|
||||||
marginDurations: {
|
},
|
||||||
wed: 900,
|
],
|
||||||
},
|
|
||||||
seatsProposed: 3,
|
seatsProposed: 3,
|
||||||
seatsRequested: 1,
|
seatsRequested: 1,
|
||||||
strict: false,
|
strict: false,
|
||||||
|
@ -233,15 +207,7 @@ describe('Ad Repository', () => {
|
||||||
const defaultAdProps: DefaultAdProps = {
|
const defaultAdProps: DefaultAdProps = {
|
||||||
driver: false,
|
driver: false,
|
||||||
passenger: true,
|
passenger: true,
|
||||||
marginDurations: {
|
marginDuration: 900,
|
||||||
mon: 900,
|
|
||||||
tue: 900,
|
|
||||||
wed: 900,
|
|
||||||
thu: 900,
|
|
||||||
fri: 900,
|
|
||||||
sat: 900,
|
|
||||||
sun: 900,
|
|
||||||
},
|
|
||||||
seatsProposed: 3,
|
seatsProposed: 3,
|
||||||
seatsRequested: 1,
|
seatsRequested: 1,
|
||||||
strict: false,
|
strict: false,
|
||||||
|
|
|
@ -1,5 +1,9 @@
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
import { AD_REPOSITORY, PARAMS_PROVIDER } from '@modules/ad/ad.di-tokens';
|
import {
|
||||||
|
AD_REPOSITORY,
|
||||||
|
DATETIME_TRANSFORMER,
|
||||||
|
PARAMS_PROVIDER,
|
||||||
|
} from '@modules/ad/ad.di-tokens';
|
||||||
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
import { WaypointDto } from '@modules/ad/interface/grpc-controllers/dtos/waypoint.dto';
|
||||||
import { CreateAdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/create-ad.request.dto';
|
import { CreateAdRequestDto } from '@modules/ad/interface/grpc-controllers/dtos/create-ad.request.dto';
|
||||||
import { AggregateID } from '@mobicoop/ddd-library';
|
import { AggregateID } from '@mobicoop/ddd-library';
|
||||||
|
@ -10,6 +14,7 @@ import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/de
|
||||||
import { CreateAdService } from '@modules/ad/core/application/commands/create-ad/create-ad.service';
|
import { CreateAdService } from '@modules/ad/core/application/commands/create-ad/create-ad.service';
|
||||||
import { CreateAdCommand } from '@modules/ad/core/application/commands/create-ad/create-ad.command';
|
import { CreateAdCommand } from '@modules/ad/core/application/commands/create-ad/create-ad.command';
|
||||||
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
|
import { AdAlreadyExistsException } from '@modules/ad/core/domain/ad.errors';
|
||||||
|
import { DateTimeTransformerPort } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||||
|
|
||||||
const originWaypoint: WaypointDto = {
|
const originWaypoint: WaypointDto = {
|
||||||
position: 0,
|
position: 0,
|
||||||
|
@ -33,9 +38,11 @@ const punctualCreateAdRequest: CreateAdRequestDto = {
|
||||||
userId: '4eb6a6af-ecfd-41c3-9118-473a507014d4',
|
userId: '4eb6a6af-ecfd-41c3-9118-473a507014d4',
|
||||||
fromDate: '2023-12-21',
|
fromDate: '2023-12-21',
|
||||||
toDate: '2023-12-21',
|
toDate: '2023-12-21',
|
||||||
schedule: {
|
schedule: [
|
||||||
thu: '08:15',
|
{
|
||||||
},
|
time: '08:15',
|
||||||
|
},
|
||||||
|
],
|
||||||
driver: true,
|
driver: true,
|
||||||
passenger: true,
|
passenger: true,
|
||||||
seatsRequested: 1,
|
seatsRequested: 1,
|
||||||
|
@ -58,13 +65,7 @@ const mockAdRepository = {
|
||||||
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
||||||
getParams: () => {
|
getParams: () => {
|
||||||
return {
|
return {
|
||||||
MON_MARGIN: 900,
|
DEPARTURE_TIME_MARGIN: 900,
|
||||||
TUE_MARGIN: 900,
|
|
||||||
WED_MARGIN: 900,
|
|
||||||
THU_MARGIN: 900,
|
|
||||||
FRI_MARGIN: 900,
|
|
||||||
SAT_MARGIN: 900,
|
|
||||||
SUN_MARGIN: 900,
|
|
||||||
DRIVER: false,
|
DRIVER: false,
|
||||||
SEATS_PROPOSED: 3,
|
SEATS_PROPOSED: 3,
|
||||||
PASSENGER: true,
|
PASSENGER: true,
|
||||||
|
@ -75,6 +76,13 @@ const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const mockDateTimeTransformer: DateTimeTransformerPort = {
|
||||||
|
fromDate: jest.fn(),
|
||||||
|
toDate: jest.fn(),
|
||||||
|
day: jest.fn(),
|
||||||
|
time: jest.fn(),
|
||||||
|
};
|
||||||
|
|
||||||
describe('create-ad.service', () => {
|
describe('create-ad.service', () => {
|
||||||
let createAdService: CreateAdService;
|
let createAdService: CreateAdService;
|
||||||
|
|
||||||
|
@ -89,6 +97,10 @@ describe('create-ad.service', () => {
|
||||||
provide: PARAMS_PROVIDER,
|
provide: PARAMS_PROVIDER,
|
||||||
useValue: mockDefaultParamsProvider,
|
useValue: mockDefaultParamsProvider,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
provide: DATETIME_TRANSFORMER,
|
||||||
|
useValue: mockDateTimeTransformer,
|
||||||
|
},
|
||||||
CreateAdService,
|
CreateAdService,
|
||||||
],
|
],
|
||||||
}).compile();
|
}).compile();
|
||||||
|
@ -102,7 +114,7 @@ describe('create-ad.service', () => {
|
||||||
|
|
||||||
describe('execution', () => {
|
describe('execution', () => {
|
||||||
const createAdCommand = new CreateAdCommand(punctualCreateAdRequest);
|
const createAdCommand = new CreateAdCommand(punctualCreateAdRequest);
|
||||||
it('should create a new ad', async () => {
|
it('should create a new punctual ad', async () => {
|
||||||
AdEntity.create = jest.fn().mockReturnValue({
|
AdEntity.create = jest.fn().mockReturnValue({
|
||||||
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
|
id: '047a6ecf-23d4-4d3e-877c-3225d560a8da',
|
||||||
});
|
});
|
||||||
|
|
|
@ -7,7 +7,6 @@ import {
|
||||||
} from '@modules/ad/core/domain/ad.types';
|
} from '@modules/ad/core/domain/ad.types';
|
||||||
import { FindAdByIdQuery } from '@modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query';
|
import { FindAdByIdQuery } from '@modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query';
|
||||||
import { FindAdByIdQueryHandler } from '@modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query-handler';
|
import { FindAdByIdQueryHandler } from '@modules/ad/core/application/queries/find-ad-by-id/find-ad-by-id.query-handler';
|
||||||
import { MarginDurationsProps } from '@modules/ad/core/domain/value-objects/margin-durations.value-object';
|
|
||||||
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
import { WaypointProps } from '@modules/ad/core/domain/value-objects/waypoint.value-object';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
|
@ -37,15 +36,6 @@ const destinationWaypointProps: WaypointProps = {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
const marginDurationsProps: MarginDurationsProps = {
|
|
||||||
mon: 600,
|
|
||||||
tue: 600,
|
|
||||||
wed: 600,
|
|
||||||
thu: 600,
|
|
||||||
fri: 600,
|
|
||||||
sat: 600,
|
|
||||||
sun: 600,
|
|
||||||
};
|
|
||||||
const baseCreateAdProps = {
|
const baseCreateAdProps = {
|
||||||
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
userId: 'e8fe64b1-4c33-49e1-9f69-4db48b21df36',
|
||||||
seatsProposed: 3,
|
seatsProposed: 3,
|
||||||
|
@ -56,31 +46,24 @@ const baseCreateAdProps = {
|
||||||
const punctualCreateAdProps = {
|
const punctualCreateAdProps = {
|
||||||
fromDate: '2023-06-22',
|
fromDate: '2023-06-22',
|
||||||
toDate: '2023-06-22',
|
toDate: '2023-06-22',
|
||||||
schedule: {
|
schedule: [
|
||||||
wed: '08:30',
|
{
|
||||||
},
|
time: '08:30',
|
||||||
|
},
|
||||||
|
],
|
||||||
frequency: Frequency.PUNCTUAL,
|
frequency: Frequency.PUNCTUAL,
|
||||||
};
|
};
|
||||||
const punctualPassengerCreateAdProps: CreateAdProps = {
|
const punctualPassengerCreateAdProps: CreateAdProps = {
|
||||||
...baseCreateAdProps,
|
...baseCreateAdProps,
|
||||||
...punctualCreateAdProps,
|
...punctualCreateAdProps,
|
||||||
marginDurations: marginDurationsProps,
|
|
||||||
driver: false,
|
driver: false,
|
||||||
passenger: true,
|
passenger: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const defaultAdProps: DefaultAdProps = {
|
const defaultAdProps: DefaultAdProps = {
|
||||||
|
marginDuration: 900,
|
||||||
driver: false,
|
driver: false,
|
||||||
passenger: true,
|
passenger: true,
|
||||||
marginDurations: {
|
|
||||||
mon: 900,
|
|
||||||
tue: 900,
|
|
||||||
wed: 900,
|
|
||||||
thu: 900,
|
|
||||||
fri: 900,
|
|
||||||
sat: 900,
|
|
||||||
sun: 900,
|
|
||||||
},
|
|
||||||
seatsProposed: 3,
|
seatsProposed: 3,
|
||||||
seatsRequested: 1,
|
seatsRequested: 1,
|
||||||
strict: false,
|
strict: false,
|
||||||
|
|
|
@ -1,47 +0,0 @@
|
||||||
import { MarginDurations } from '@modules/ad/core/domain/value-objects/margin-durations.value-object';
|
|
||||||
|
|
||||||
describe('Margin durations value object', () => {
|
|
||||||
it('should create a margin durations value object', () => {
|
|
||||||
const marginDurationsVO = new MarginDurations({
|
|
||||||
mon: 600,
|
|
||||||
tue: 610,
|
|
||||||
wed: 620,
|
|
||||||
thu: 630,
|
|
||||||
fri: 640,
|
|
||||||
sat: 650,
|
|
||||||
sun: 660,
|
|
||||||
});
|
|
||||||
expect(marginDurationsVO.mon).toBe(600);
|
|
||||||
expect(marginDurationsVO.tue).toBe(610);
|
|
||||||
expect(marginDurationsVO.wed).toBe(620);
|
|
||||||
expect(marginDurationsVO.thu).toBe(630);
|
|
||||||
expect(marginDurationsVO.fri).toBe(640);
|
|
||||||
expect(marginDurationsVO.sat).toBe(650);
|
|
||||||
expect(marginDurationsVO.sun).toBe(660);
|
|
||||||
});
|
|
||||||
it('should update margin durations value object values', () => {
|
|
||||||
const marginDurationsVO = new MarginDurations({
|
|
||||||
mon: 600,
|
|
||||||
tue: 610,
|
|
||||||
wed: 620,
|
|
||||||
thu: 630,
|
|
||||||
fri: 640,
|
|
||||||
sat: 650,
|
|
||||||
sun: 660,
|
|
||||||
});
|
|
||||||
marginDurationsVO.mon = 700;
|
|
||||||
marginDurationsVO.tue = 710;
|
|
||||||
marginDurationsVO.wed = 720;
|
|
||||||
marginDurationsVO.thu = 730;
|
|
||||||
marginDurationsVO.fri = 740;
|
|
||||||
marginDurationsVO.sat = 750;
|
|
||||||
marginDurationsVO.sun = 760;
|
|
||||||
expect(marginDurationsVO.mon).toBe(700);
|
|
||||||
expect(marginDurationsVO.tue).toBe(710);
|
|
||||||
expect(marginDurationsVO.wed).toBe(720);
|
|
||||||
expect(marginDurationsVO.thu).toBe(730);
|
|
||||||
expect(marginDurationsVO.fri).toBe(740);
|
|
||||||
expect(marginDurationsVO.sat).toBe(750);
|
|
||||||
expect(marginDurationsVO.sun).toBe(760);
|
|
||||||
});
|
|
||||||
});
|
|
|
@ -39,20 +39,13 @@ describe('Publish message when ad is created domain event handler', () => {
|
||||||
frequency: Frequency.PUNCTUAL,
|
frequency: Frequency.PUNCTUAL,
|
||||||
fromDate: '2023-06-28',
|
fromDate: '2023-06-28',
|
||||||
toDate: '2023-06-28',
|
toDate: '2023-06-28',
|
||||||
monTime: undefined,
|
schedule: [
|
||||||
tueTime: undefined,
|
{
|
||||||
wedTime: '07:15',
|
day: 3,
|
||||||
thuTime: undefined,
|
time: '07:15',
|
||||||
friTime: undefined,
|
margin: 900,
|
||||||
satTime: undefined,
|
},
|
||||||
sunTime: undefined,
|
],
|
||||||
monMarginDuration: 900,
|
|
||||||
tueMarginDuration: 900,
|
|
||||||
wedMarginDuration: 900,
|
|
||||||
thuMarginDuration: 900,
|
|
||||||
friMarginDuration: 900,
|
|
||||||
satMarginDuration: 900,
|
|
||||||
sunMarginDuration: 900,
|
|
||||||
seatsProposed: 3,
|
seatsProposed: 3,
|
||||||
seatsRequested: 1,
|
seatsRequested: 1,
|
||||||
strict: false,
|
strict: false,
|
||||||
|
@ -88,7 +81,7 @@ describe('Publish message when ad is created domain event handler', () => {
|
||||||
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
|
expect(mockMessagePublisher.publish).toHaveBeenCalledTimes(1);
|
||||||
expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
|
expect(mockMessagePublisher.publish).toHaveBeenCalledWith(
|
||||||
'ad.created',
|
'ad.created',
|
||||||
'{"id":"some-domain-event-id","aggregateId":"some-aggregate-id","userId":"some-user-id","driver":false,"passenger":true,"frequency":"PUNCTUAL","fromDate":"2023-06-28","toDate":"2023-06-28","wedTime":"07:15","monMarginDuration":900,"tueMarginDuration":900,"wedMarginDuration":900,"thuMarginDuration":900,"friMarginDuration":900,"satMarginDuration":900,"sunMarginDuration":900,"seatsProposed":3,"seatsRequested":1,"strict":false,"waypoints":[{"position":0,"houseNumber":"5","street":"Avenue Foch","locality":"Nancy","postalCode":"54000","country":"France","lat":48.689445,"lon":6.1765102},{"position":1,"locality":"Paris","postalCode":"75000","country":"France","lat":48.8566,"lon":2.3522}],"metadata":{"timestamp":1687928400000,"correlationId":"some-correlation-id"}}',
|
'{"id":"some-domain-event-id","aggregateId":"some-aggregate-id","userId":"some-user-id","driver":false,"passenger":true,"frequency":"PUNCTUAL","fromDate":"2023-06-28","toDate":"2023-06-28","schedule":[{"day":3,"time":"07:15","margin":900}],"seatsProposed":3,"seatsRequested":1,"strict":false,"waypoints":[{"position":0,"houseNumber":"5","street":"Avenue Foch","locality":"Nancy","postalCode":"54000","country":"France","lat":48.689445,"lon":6.1765102},{"position":1,"locality":"Paris","postalCode":"75000","country":"France","lat":48.8566,"lon":2.3522}],"metadata":{"timestamp":1687928400000,"correlationId":"some-correlation-id"}}',
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -1,55 +1,9 @@
|
||||||
import {
|
|
||||||
PARAMS_PROVIDER,
|
|
||||||
TIMEZONE_FINDER,
|
|
||||||
TIME_CONVERTER,
|
|
||||||
} from '@modules/ad/ad.di-tokens';
|
|
||||||
import { AdMapper } from '@modules/ad/ad.mapper';
|
import { AdMapper } from '@modules/ad/ad.mapper';
|
||||||
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
|
|
||||||
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
|
|
||||||
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
|
|
||||||
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
import { AdRepository } from '@modules/ad/infrastructure/ad.repository';
|
||||||
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
import { PrismaService } from '@modules/ad/infrastructure/prisma.service';
|
||||||
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
|
import { EventEmitter2, EventEmitterModule } from '@nestjs/event-emitter';
|
||||||
import { Test, TestingModule } from '@nestjs/testing';
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
|
||||||
getParams: () => {
|
|
||||||
return {
|
|
||||||
MON_MARGIN: 900,
|
|
||||||
TUE_MARGIN: 900,
|
|
||||||
WED_MARGIN: 900,
|
|
||||||
THU_MARGIN: 900,
|
|
||||||
FRI_MARGIN: 900,
|
|
||||||
SAT_MARGIN: 900,
|
|
||||||
SUN_MARGIN: 900,
|
|
||||||
DRIVER: false,
|
|
||||||
SEATS_PROPOSED: 3,
|
|
||||||
PASSENGER: true,
|
|
||||||
SEATS_REQUESTED: 1,
|
|
||||||
STRICT: false,
|
|
||||||
DEFAULT_TIMEZONE: 'Europe/Paris',
|
|
||||||
};
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockTimezoneFinder: TimezoneFinderPort = {
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
timezones: jest.fn().mockImplementation((lon: number, lat: number) => {
|
|
||||||
if (lon < 60) return 'Europe/Paris';
|
|
||||||
return 'America/New_York';
|
|
||||||
}),
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockTimeConverter: TimeConverterPort = {
|
|
||||||
localDateTimeToUtc: jest
|
|
||||||
.fn()
|
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
||||||
.mockImplementation((datetime: Date, timezone: string, dst?: boolean) => {
|
|
||||||
return datetime;
|
|
||||||
}),
|
|
||||||
utcDatetimeToLocalTime: jest.fn(),
|
|
||||||
};
|
|
||||||
|
|
||||||
const mockMessagePublisher = {
|
const mockMessagePublisher = {
|
||||||
publish: jest.fn().mockImplementation(),
|
publish: jest.fn().mockImplementation(),
|
||||||
};
|
};
|
||||||
|
@ -62,22 +16,7 @@ describe('Ad repository', () => {
|
||||||
beforeAll(async () => {
|
beforeAll(async () => {
|
||||||
const module: TestingModule = await Test.createTestingModule({
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
imports: [EventEmitterModule.forRoot()],
|
imports: [EventEmitterModule.forRoot()],
|
||||||
providers: [
|
providers: [PrismaService, AdMapper],
|
||||||
PrismaService,
|
|
||||||
AdMapper,
|
|
||||||
{
|
|
||||||
provide: PARAMS_PROVIDER,
|
|
||||||
useValue: mockDefaultParamsProvider,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: TIMEZONE_FINDER,
|
|
||||||
useValue: mockTimezoneFinder,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
provide: TIME_CONVERTER,
|
|
||||||
useValue: mockTimeConverter,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
}).compile();
|
}).compile();
|
||||||
|
|
||||||
prismaService = module.get<PrismaService>(PrismaService);
|
prismaService = module.get<PrismaService>(PrismaService);
|
||||||
|
|
|
@ -0,0 +1,266 @@
|
||||||
|
import {
|
||||||
|
PARAMS_PROVIDER,
|
||||||
|
TIMEZONE_FINDER,
|
||||||
|
TIME_CONVERTER,
|
||||||
|
} from '@modules/ad/ad.di-tokens';
|
||||||
|
import { Frequency } from '@modules/ad/core/application/ports/datetime-transformer.port';
|
||||||
|
import { DefaultParamsProviderPort } from '@modules/ad/core/application/ports/default-params-provider.port';
|
||||||
|
import { TimeConverterPort } from '@modules/ad/core/application/ports/time-converter.port';
|
||||||
|
import { TimezoneFinderPort } from '@modules/ad/core/application/ports/timezone-finder.port';
|
||||||
|
import { DateTimeTransformer } from '@modules/ad/infrastructure/datetime-transformer';
|
||||||
|
import { Test, TestingModule } from '@nestjs/testing';
|
||||||
|
|
||||||
|
const mockDefaultParamsProvider: DefaultParamsProviderPort = {
|
||||||
|
getParams: () => {
|
||||||
|
return {
|
||||||
|
DEPARTURE_TIME_MARGIN: 900,
|
||||||
|
DRIVER: false,
|
||||||
|
SEATS_PROPOSED: 3,
|
||||||
|
PASSENGER: true,
|
||||||
|
SEATS_REQUESTED: 1,
|
||||||
|
STRICT: false,
|
||||||
|
DEFAULT_TIMEZONE: 'Europe/Paris',
|
||||||
|
};
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockTimezoneFinder: TimezoneFinderPort = {
|
||||||
|
timezones: jest.fn().mockImplementation(() => ['Europe/Paris']),
|
||||||
|
};
|
||||||
|
|
||||||
|
const mockTimeConverter: TimeConverterPort = {
|
||||||
|
localStringTimeToUtcStringTime: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementationOnce(() => '00:15'),
|
||||||
|
localStringDateTimeToUtcDate: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementationOnce(() => new Date('2023-07-30T06:15:00.000Z'))
|
||||||
|
.mockImplementationOnce(() => new Date('2023-07-20T08:15:00.000Z'))
|
||||||
|
.mockImplementationOnce(() => new Date('2023-07-19T23:15:00.000Z'))
|
||||||
|
.mockImplementationOnce(() => new Date('2023-07-19T23:15:00.000Z')),
|
||||||
|
utcUnixEpochDayFromTime: jest
|
||||||
|
.fn()
|
||||||
|
.mockImplementationOnce(() => 4)
|
||||||
|
.mockImplementationOnce(() => 3)
|
||||||
|
.mockImplementationOnce(() => 3)
|
||||||
|
.mockImplementationOnce(() => 5)
|
||||||
|
.mockImplementationOnce(() => 5),
|
||||||
|
};
|
||||||
|
|
||||||
|
describe('Datetime Transformer', () => {
|
||||||
|
let datetimeTransformer: DateTimeTransformer;
|
||||||
|
|
||||||
|
beforeAll(async () => {
|
||||||
|
const module: TestingModule = await Test.createTestingModule({
|
||||||
|
providers: [
|
||||||
|
{
|
||||||
|
provide: PARAMS_PROVIDER,
|
||||||
|
useValue: mockDefaultParamsProvider,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: TIMEZONE_FINDER,
|
||||||
|
useValue: mockTimezoneFinder,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
provide: TIME_CONVERTER,
|
||||||
|
useValue: mockTimeConverter,
|
||||||
|
},
|
||||||
|
DateTimeTransformer,
|
||||||
|
],
|
||||||
|
}).compile();
|
||||||
|
|
||||||
|
datetimeTransformer = module.get<DateTimeTransformer>(DateTimeTransformer);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should be defined', () => {
|
||||||
|
expect(datetimeTransformer).toBeDefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('fromDate', () => {
|
||||||
|
it('should return fromDate as is if frequency is recurrent', () => {
|
||||||
|
const transformedFromDate: string = datetimeTransformer.fromDate(
|
||||||
|
{
|
||||||
|
date: '2023-07-30',
|
||||||
|
time: '07:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.RECURRENT,
|
||||||
|
);
|
||||||
|
expect(transformedFromDate).toBe('2023-07-30');
|
||||||
|
});
|
||||||
|
it('should return transformed fromDate if frequency is punctual and coordinates are those of Nancy', () => {
|
||||||
|
const transformedFromDate: string = datetimeTransformer.fromDate(
|
||||||
|
{
|
||||||
|
date: '2023-07-30',
|
||||||
|
time: '07:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.PUNCTUAL,
|
||||||
|
);
|
||||||
|
expect(transformedFromDate).toBe('2023-07-30');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('toDate', () => {
|
||||||
|
it('should return toDate as is if frequency is recurrent', () => {
|
||||||
|
const transformedToDate: string = datetimeTransformer.toDate(
|
||||||
|
'2024-07-29',
|
||||||
|
{
|
||||||
|
date: '2023-07-20',
|
||||||
|
time: '10:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.RECURRENT,
|
||||||
|
);
|
||||||
|
expect(transformedToDate).toBe('2024-07-29');
|
||||||
|
});
|
||||||
|
it('should return transformed fromDate if frequency is punctual', () => {
|
||||||
|
const transformedToDate: string = datetimeTransformer.toDate(
|
||||||
|
'2024-07-30',
|
||||||
|
{
|
||||||
|
date: '2023-07-20',
|
||||||
|
time: '10:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.PUNCTUAL,
|
||||||
|
);
|
||||||
|
expect(transformedToDate).toBe('2023-07-20');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('day', () => {
|
||||||
|
it('should not change day if frequency is recurrent and converted UTC time is on the same day', () => {
|
||||||
|
const day: number = datetimeTransformer.day(
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
date: '2023-07-24',
|
||||||
|
time: '01:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.RECURRENT,
|
||||||
|
);
|
||||||
|
expect(day).toBe(1);
|
||||||
|
});
|
||||||
|
it('should change day if frequency is recurrent and converted UTC time is on the previous day', () => {
|
||||||
|
const day: number = datetimeTransformer.day(
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
date: '2023-07-24',
|
||||||
|
time: '00:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.RECURRENT,
|
||||||
|
);
|
||||||
|
expect(day).toBe(0);
|
||||||
|
});
|
||||||
|
it('should change day if frequency is recurrent and converted UTC time is on the previous day and given day is sunday', () => {
|
||||||
|
const day: number = datetimeTransformer.day(
|
||||||
|
0,
|
||||||
|
{
|
||||||
|
date: '2023-07-23',
|
||||||
|
time: '00:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.RECURRENT,
|
||||||
|
);
|
||||||
|
expect(day).toBe(6);
|
||||||
|
});
|
||||||
|
it('should change day if frequency is recurrent and converted UTC time is on the next day', () => {
|
||||||
|
const day: number = datetimeTransformer.day(
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
date: '2023-07-24',
|
||||||
|
time: '23:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 30.82,
|
||||||
|
lat: 49.37,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.RECURRENT,
|
||||||
|
);
|
||||||
|
expect(day).toBe(2);
|
||||||
|
});
|
||||||
|
it('should change day if frequency is recurrent and converted UTC time is on the next day and given day is saturday(6)', () => {
|
||||||
|
const day: number = datetimeTransformer.day(
|
||||||
|
6,
|
||||||
|
{
|
||||||
|
date: '2023-07-29',
|
||||||
|
time: '23:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 30.82,
|
||||||
|
lat: 49.37,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.RECURRENT,
|
||||||
|
);
|
||||||
|
expect(day).toBe(0);
|
||||||
|
});
|
||||||
|
it('should return utc fromDate day if frequency is punctual', () => {
|
||||||
|
const day: number = datetimeTransformer.day(
|
||||||
|
1,
|
||||||
|
{
|
||||||
|
date: '2023-07-20',
|
||||||
|
time: '00:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.PUNCTUAL,
|
||||||
|
);
|
||||||
|
expect(day).toBe(3);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('time', () => {
|
||||||
|
it('should transform given time to utc time if frequency is recurrent', () => {
|
||||||
|
const time: string = datetimeTransformer.time(
|
||||||
|
{
|
||||||
|
date: '2023-07-24',
|
||||||
|
time: '01:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.RECURRENT,
|
||||||
|
);
|
||||||
|
expect(time).toBe('00:15');
|
||||||
|
});
|
||||||
|
it('should return given time to utc time if frequency is punctual', () => {
|
||||||
|
const time: string = datetimeTransformer.time(
|
||||||
|
{
|
||||||
|
date: '2023-07-24',
|
||||||
|
time: '01:15',
|
||||||
|
coordinates: {
|
||||||
|
lon: 6.175,
|
||||||
|
lat: 48.685,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
Frequency.PUNCTUAL,
|
||||||
|
);
|
||||||
|
expect(time).toBe('23:15');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
|
@ -7,7 +7,7 @@ describe('Time Converter', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('localStringTimeToUtcStringTime', () => {
|
describe('localStringTimeToUtcStringTime', () => {
|
||||||
it('should convert a paris time to utc time with dst', () => {
|
it('should convert a paris time to utc time', () => {
|
||||||
const timeConverter: TimeConverter = new TimeConverter();
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
const parisTime = '08:00';
|
const parisTime = '08:00';
|
||||||
const utcDatetime = timeConverter.localStringTimeToUtcStringTime(
|
const utcDatetime = timeConverter.localStringTimeToUtcStringTime(
|
||||||
|
@ -54,33 +54,151 @@ describe('Time Converter', () => {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
// describe('utcDatetimeToLocalTime', () => {
|
describe('localStringDateTimeToUtcDate', () => {
|
||||||
// it('should convert an utc datetime isostring to a paris local time', () => {
|
it('should convert a summer paris date and time to a utc date', () => {
|
||||||
// const timeConverter: TimeConverter = new TimeConverter();
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
// const utcDatetimeIsostring = '2023-06-22T06:25:00.000Z';
|
const parisDate = '2023-06-22';
|
||||||
// const parisTime = timeConverter.utcDatetimeToLocalTime(
|
const parisTime = '12:00';
|
||||||
// utcDatetimeIsostring,
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
// 'Europe/Paris',
|
parisDate,
|
||||||
// );
|
parisTime,
|
||||||
// expect(parisTime).toBe('08:25');
|
'Europe/Paris',
|
||||||
// });
|
);
|
||||||
// it('should return undefined if isostring input is invalid', () => {
|
expect(utcDate.toISOString()).toBe('2023-06-22T10:00:00.000Z');
|
||||||
// const timeConverter: TimeConverter = new TimeConverter();
|
});
|
||||||
// const utcDatetimeIsostring = 'not_an_isostring';
|
it('should convert a winter paris date and time to a utc date', () => {
|
||||||
// const parisTime = timeConverter.utcDatetimeToLocalTime(
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
// utcDatetimeIsostring,
|
const parisDate = '2023-02-02';
|
||||||
// 'Europe/Paris',
|
const parisTime = '12:00';
|
||||||
// );
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
// expect(parisTime).toBeUndefined();
|
parisDate,
|
||||||
// });
|
parisTime,
|
||||||
// it('should return undefined if timezone input is invalid', () => {
|
'Europe/Paris',
|
||||||
// const timeConverter: TimeConverter = new TimeConverter();
|
);
|
||||||
// const utcDatetimeIsostring = '2023-06-22T06:25:00.000Z';
|
expect(utcDate.toISOString()).toBe('2023-02-02T11:00:00.000Z');
|
||||||
// const parisTime = timeConverter.utcDatetimeToLocalTime(
|
});
|
||||||
// utcDatetimeIsostring,
|
it('should convert a summer paris date and time to a utc date without dst', () => {
|
||||||
// 'Foo/Bar',
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
// );
|
const parisDate = '2023-06-22';
|
||||||
// expect(parisTime).toBeUndefined();
|
const parisTime = '12:00';
|
||||||
// });
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
// });
|
parisDate,
|
||||||
|
parisTime,
|
||||||
|
'Europe/Paris',
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
expect(utcDate.toISOString()).toBe('2023-06-22T11:00:00.000Z');
|
||||||
|
});
|
||||||
|
it('should convert a tonga date and time to a utc date', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
const tongaDate = '2023-02-02';
|
||||||
|
const tongaTime = '12:00';
|
||||||
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
|
tongaDate,
|
||||||
|
tongaTime,
|
||||||
|
'Pacific/Tongatapu',
|
||||||
|
);
|
||||||
|
expect(utcDate.toISOString()).toBe('2023-02-01T23:00:00.000Z');
|
||||||
|
});
|
||||||
|
it('should convert a papeete date and time to a utc date', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
const papeeteDate = '2023-02-02';
|
||||||
|
const papeeteTime = '15:00';
|
||||||
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
|
papeeteDate,
|
||||||
|
papeeteTime,
|
||||||
|
'Pacific/Tahiti',
|
||||||
|
);
|
||||||
|
expect(utcDate.toISOString()).toBe('2023-02-03T01:00:00.000Z');
|
||||||
|
});
|
||||||
|
it('should return undefined if time is invalid', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
const parisDate = '2023-06-22';
|
||||||
|
const parisTime = '28:00';
|
||||||
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
|
parisDate,
|
||||||
|
parisTime,
|
||||||
|
'Europe/Paris',
|
||||||
|
);
|
||||||
|
expect(utcDate).toBeUndefined();
|
||||||
|
});
|
||||||
|
it('should return undefined if time is undefined', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
const parisDate = '2023-06-22';
|
||||||
|
const parisTime = undefined;
|
||||||
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
|
parisDate,
|
||||||
|
parisTime,
|
||||||
|
'Europe/Paris',
|
||||||
|
);
|
||||||
|
expect(utcDate).toBeUndefined();
|
||||||
|
});
|
||||||
|
it('should return undefined if timezone is invalid', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
const parisDate = '2023-06-22';
|
||||||
|
const parisTime = '12:00';
|
||||||
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
|
parisDate,
|
||||||
|
parisTime,
|
||||||
|
'Foo/Bar',
|
||||||
|
);
|
||||||
|
expect(utcDate).toBeUndefined();
|
||||||
|
});
|
||||||
|
it('should return undefined if timezone is undefined', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
const parisDate = '2023-06-22';
|
||||||
|
const parisTime = '12:00';
|
||||||
|
const utcDate = timeConverter.localStringDateTimeToUtcDate(
|
||||||
|
parisDate,
|
||||||
|
parisTime,
|
||||||
|
undefined,
|
||||||
|
);
|
||||||
|
expect(utcDate).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('utcBaseDayFromTime', () => {
|
||||||
|
it('should get the utc day of paris at 12:00', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
expect(
|
||||||
|
timeConverter.utcUnixEpochDayFromTime('12:00', 'Europe/Paris'),
|
||||||
|
).toBe(4);
|
||||||
|
});
|
||||||
|
it('should get the utc day of paris at 00:00', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
expect(
|
||||||
|
timeConverter.utcUnixEpochDayFromTime('00:00', 'Europe/Paris'),
|
||||||
|
).toBe(3);
|
||||||
|
});
|
||||||
|
it('should get the utc day of papeete at 16:00', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
expect(
|
||||||
|
timeConverter.utcUnixEpochDayFromTime('16:00', 'Pacific/Tahiti'),
|
||||||
|
).toBe(5);
|
||||||
|
});
|
||||||
|
it('should return undefined if time is invalid', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
expect(
|
||||||
|
timeConverter.utcUnixEpochDayFromTime('28:00', 'Europe/Paris'),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
it('should return undefined if time is undefined', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
expect(
|
||||||
|
timeConverter.utcUnixEpochDayFromTime(undefined, 'Europe/Paris'),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
it('should return undefined if timezone is invalid', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
expect(
|
||||||
|
timeConverter.utcUnixEpochDayFromTime('12:00', 'Foo/Bar'),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
it('should return undefined if timezone is undefined', () => {
|
||||||
|
const timeConverter: TimeConverter = new TimeConverter();
|
||||||
|
expect(
|
||||||
|
timeConverter.utcUnixEpochDayFromTime('12:00', undefined),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
|
@ -31,9 +31,11 @@ const punctualCreateAdRequest: CreateAdRequestDto = {
|
||||||
userId: '4eb6a6af-ecfd-41c3-9118-473a507014d4',
|
userId: '4eb6a6af-ecfd-41c3-9118-473a507014d4',
|
||||||
fromDate: '2023-12-21',
|
fromDate: '2023-12-21',
|
||||||
toDate: '2023-12-21',
|
toDate: '2023-12-21',
|
||||||
schedule: {
|
schedule: [
|
||||||
thu: '08:15',
|
{
|
||||||
},
|
time: '08:15',
|
||||||
|
},
|
||||||
|
],
|
||||||
driver: false,
|
driver: false,
|
||||||
passenger: true,
|
passenger: true,
|
||||||
seatsRequested: 1,
|
seatsRequested: 1,
|
||||||
|
|
|
@ -1,30 +0,0 @@
|
||||||
import { ScheduleItemDto } from '@modules/ad/interface/grpc-controllers/dtos/schedule.dto';
|
|
||||||
import { IsSchedule } from '@modules/ad/interface/grpc-controllers/dtos/validators/decorators/is-schedule.decorator';
|
|
||||||
import { Validator } from 'class-validator';
|
|
||||||
|
|
||||||
describe('schedule decorator', () => {
|
|
||||||
class MyClass {
|
|
||||||
@IsSchedule()
|
|
||||||
schedule: ScheduleItemDto;
|
|
||||||
}
|
|
||||||
it('should return a property decorator has a function', () => {
|
|
||||||
const isSchedule = IsSchedule();
|
|
||||||
expect(typeof isSchedule).toBe('function');
|
|
||||||
});
|
|
||||||
it('should validate a valid schedule', async () => {
|
|
||||||
const myClassInstance = new MyClass();
|
|
||||||
myClassInstance.schedule = {
|
|
||||||
mon: '07:15',
|
|
||||||
};
|
|
||||||
const validator = new Validator();
|
|
||||||
const validation = await validator.validate(myClassInstance);
|
|
||||||
expect(validation.length).toBe(0);
|
|
||||||
});
|
|
||||||
it('should not validate a invalid schedule', async () => {
|
|
||||||
const myClassInstance = new MyClass();
|
|
||||||
myClassInstance.schedule = {};
|
|
||||||
const validator = new Validator();
|
|
||||||
const validation = await validator.validate(myClassInstance);
|
|
||||||
expect(validation.length).toBe(1);
|
|
||||||
});
|
|
||||||
});
|
|
Loading…
Reference in New Issue