From b732e343e6ea285f5c1a9d16b8aa4106cb424a57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-L=C3=A9o=20Bourbonnais?= Date: Tue, 6 Feb 2024 15:23:09 -0500 Subject: [PATCH 1/2] simplify base objects and add unserialize - --- .../src/services/baseObjects/BaseAddress.ts | 5 + .../src/services/baseObjects/BaseHousehold.ts | 62 +--------- .../src/services/baseObjects/BaseInterview.ts | 72 +++++------- .../src/services/baseObjects/BaseJourney.ts | 70 ++++------- .../services/baseObjects/BaseOrganization.ts | 74 +----------- .../src/services/baseObjects/BasePerson.ts | 88 +------------- .../src/services/baseObjects/BasePlace.ts | 14 ++- .../src/services/baseObjects/BaseSegment.ts | 17 +-- .../src/services/baseObjects/BaseTrip.ts | 43 +------ .../src/services/baseObjects/BaseTripChain.ts | 21 +--- .../src/services/baseObjects/BaseVehicle.ts | 5 + .../services/baseObjects/BaseVisitedPlace.ts | 45 +++++-- .../src/services/baseObjects/Sample.ts | 11 +- .../src/services/baseObjects/Survey.ts | 35 +++--- .../src/services/baseObjects/Tripable.ts | 18 --- .../src/services/baseObjects/Vehicleable.ts | 14 --- .../baseObjects/__tests__/BaseAddress.test.ts | 6 + .../__tests__/BaseHousehold.test.ts | 56 +-------- .../__tests__/BaseInterview.test.ts | 82 +++++++------ .../baseObjects/__tests__/BaseJourney.test.ts | 50 +------- .../__tests__/BaseOrganization.test.ts | 41 +------ .../baseObjects/__tests__/BasePerson.test.ts | 56 ++------- .../baseObjects/__tests__/BasePlace.test.ts | 110 ++++++++++-------- .../baseObjects/__tests__/BaseSegment.test.ts | 29 +---- .../baseObjects/__tests__/BaseTrip.test.ts | 42 ++----- .../__tests__/BaseTripChain.test.ts | 24 ++-- .../baseObjects/__tests__/BaseVehicle.test.ts | 6 + .../__tests__/BaseVisitedPlace.test.ts | 22 ++-- .../baseObjects/__tests__/Sample.test.ts | 5 +- .../baseObjects/__tests__/Survey.test.ts | 7 +- .../src/services/baseObjects/_template.ts | 45 ------- .../evolution-common/src/utils/DateUtils.ts | 24 ++++ 32 files changed, 355 insertions(+), 844 deletions(-) delete mode 100644 packages/evolution-common/src/services/baseObjects/Tripable.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/Vehicleable.ts delete mode 100644 packages/evolution-common/src/services/baseObjects/_template.ts create mode 100644 packages/evolution-common/src/utils/DateUtils.ts diff --git a/packages/evolution-common/src/services/baseObjects/BaseAddress.ts b/packages/evolution-common/src/services/baseObjects/BaseAddress.ts index 839ec6a0..9ca7fa71 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseAddress.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseAddress.ts @@ -76,4 +76,9 @@ export class BaseAddress extends Uuidable { this.addressId = params.addressId; this.internalId = params.internalId; } + + // params must be sanitized and must be valid: + static unserialize(params: BaseAddressAttributes): BaseAddress { + return new BaseAddress(params); + } } diff --git a/packages/evolution-common/src/services/baseObjects/BaseHousehold.ts b/packages/evolution-common/src/services/baseObjects/BaseHousehold.ts index dfe44583..0e4ad75d 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseHousehold.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseHousehold.ts @@ -6,20 +6,13 @@ */ import { OptionalValidity, IValidatable } from './Validatable'; -import { BasePerson } from './BasePerson'; -import { BasePlace } from './BasePlace'; import { Weightable, Weight, validateWeights } from './Weight'; import * as HAttr from './attributeTypes/HouseholdAttributes'; import { Uuidable } from './Uuidable'; -import { Vehicleable } from './Vehicleable'; -import { BaseVehicle } from './BaseVehicle'; type BaseHouseholdAttributes = { _uuid?: string; - baseMembers?: BasePerson[]; - baseHome?: BasePlace; - size?: number; carNumber?: number; twoWheelNumber?: number; @@ -32,8 +25,7 @@ type BaseHouseholdAttributes = { // must be anonymized: contactPhoneNumber?: string; // TODO: normalize and/or validate phone numbers contactEmail?: string; -} & Weightable & - Vehicleable; +} & Weightable; type ExtendedHouseholdAttributes = BaseHouseholdAttributes & { [key: string]: any }; @@ -41,11 +33,6 @@ class BaseHousehold extends Uuidable implements IValidatable { _isValid: OptionalValidity; _weights?: Weight[]; - baseMembers?: BasePerson[]; - baseHome?: BasePlace; - - baseVehicles?: BaseVehicle[]; - /** * Here it would be better to just calculate from household members, * but in most surveys, we first ask the respondent for the household @@ -84,11 +71,6 @@ class BaseHousehold extends Uuidable implements IValidatable { this._isValid = undefined; this._weights = params._weights; - this.baseMembers = params.baseMembers || []; - this.baseHome = params.baseHome; - - this.baseVehicles = params.baseVehicles || []; - this.size = params.size; this.carNumber = params.carNumber; this.twoWheelNumber = params.twoWheelNumber; @@ -101,6 +83,11 @@ class BaseHousehold extends Uuidable implements IValidatable { this.contactEmail = params.contactEmail; } + // params must be sanitized and must be valid: + static unserialize(params: BaseHouseholdAttributes): BaseHousehold { + return new BaseHousehold(params); + } + validate(): OptionalValidity { // TODO: implement: this._isValid = true; @@ -148,43 +135,6 @@ class BaseHousehold extends Uuidable implements IValidatable { errors.push(...weightsErrors); } - // Validate baseMembers: - if (dirtyParams.baseMembers !== undefined && !Array.isArray(dirtyParams.baseMembers)) { - errors.push(new Error('BaseHousehold validateParams: baseMembers should be an array')); - } else if (dirtyParams.baseMembers !== undefined && dirtyParams.baseMembers.length > 0) { - for (let i = 0, countI = dirtyParams.baseMembers.length; i < countI; i++) { - if (!dirtyParams.baseMembers[i] || !(dirtyParams.baseMembers[i] instanceof BasePerson)) { - errors.push( - new Error( - `BaseHousehold validateParams: baseMembers index ${i} is not an instance of BasePerson` - ) - ); - } - } - } - - // Validate baseVehicles: - if (dirtyParams.baseVehicles !== undefined && !Array.isArray(dirtyParams.baseVehicles)) { - errors.push(new Error('BaseHousehold validateParams: baseVehicles should be an array')); - } else if (dirtyParams.baseVehicles !== undefined && dirtyParams.baseVehicles.length > 0) { - for (let i = 0, countI = dirtyParams.baseVehicles.length; i < countI; i++) { - if (!dirtyParams.baseVehicles[i] || !(dirtyParams.baseVehicles[i] instanceof BaseVehicle)) { - errors.push( - new Error( - `BaseHousehold validateParams: baseVehicles index ${i} is not an instance of BaseVehicle` - ) - ); - } - } - } - - // Validate baseHome: - if (dirtyParams.baseHome !== undefined) { - if (!(dirtyParams.baseHome instanceof BasePlace)) { - errors.push(new Error('BaseHousehold validateParams: baseHome is not an instance of BasePlace')); - } - } - // Validate size: if (dirtyParams.size !== undefined && (!Number.isInteger(dirtyParams.size) || dirtyParams.size < 0)) { errors.push(new Error('BaseHousehold validateParams: size should be a positive integer')); diff --git a/packages/evolution-common/src/services/baseObjects/BaseInterview.ts b/packages/evolution-common/src/services/baseObjects/BaseInterview.ts index 41e14d7a..10c947af 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseInterview.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseInterview.ts @@ -5,13 +5,12 @@ * License text available at https://opensource.org/licenses/MIT */ -import { parsePhoneNumber } from 'libphonenumber-js'; import { OptionalValidity, IValidatable } from './Validatable'; import { Surveyable, SurveyableAttributes } from './Surveyable'; +import { Survey, SurveyAttributes } from './Survey'; +import { Sample, SampleAttributes } from './Sample'; import { Uuidable } from './Uuidable'; -import { BasePerson } from './BasePerson'; -import { BaseHousehold } from './BaseHousehold'; -import { BaseOrganization } from './BaseOrganization'; +import { parseDate } from '../../utils/DateUtils'; export const devices = ['tablet', 'mobile', 'desktop', 'other', 'unknown'] as const; @@ -21,34 +20,26 @@ export type BaseInterviewAttributes = { _uuid?: string; accessCode?: string; - assignedDate?: Date; // date, the assigned date for the survey (trips date most of the time) + assignedDate?: Date | string; // date, the assigned date for the survey (trips date most of the time) contactPhoneNumber?: string; // phone number contactEmail?: string; // email - _startedAt?: Date; - _updatedAt?: Date; - _completedAt?: Date; - - baseHousehold?: BaseHousehold; // for household-based surveys - basePerson?: BasePerson; // for person-based with no household data surveys - baseOrganization?: BaseOrganization; // for organization-based surveys + _startedAt?: Date | string; + _updatedAt?: Date | string; + _completedAt?: Date | string; _language?: string; // two-letter ISO 639-1 code _source?: string; // source for the interview (web, phone, social, etc.) _isCompleted?: boolean; _device?: Device; -} & SurveyableAttributes; +}; export type ExtendedInterviewAttributes = BaseInterviewAttributes & { [key: string]: any }; export class BaseInterview extends Surveyable implements IValidatable { _isValid: OptionalValidity; - baseHousehold?: BaseHousehold; - basePerson?: BasePerson; - baseOrganization?: BaseOrganization; - accessCode?: string; assignedDate?: Date; // date, the assigned date for the survey (trips date most of the time) contactPhoneNumber?: string; // phone number @@ -71,21 +62,17 @@ export class BaseInterview extends Surveyable implements IValidatable { 'accessCode' ]; - constructor(params: BaseInterviewAttributes | ExtendedInterviewAttributes) { + constructor(params: (BaseInterviewAttributes | ExtendedInterviewAttributes) & SurveyableAttributes) { super(params.survey, params.sample, params.sampleBatchNumber, params._uuid); - this.baseHousehold = params.baseHousehold; - this.basePerson = params.basePerson; - this.baseOrganization = params.baseOrganization; - this.accessCode = params.accessCode; - this.assignedDate = params.assignedDate; + this.assignedDate = parseDate(params.assignedDate); this.contactPhoneNumber = params.contactPhoneNumber; this.contactEmail = params.contactEmail; - this._startedAt = params._startedAt; - this._updatedAt = params._updatedAt; - this._completedAt = params._completedAt; + this._startedAt = parseDate(params._startedAt); + this._updatedAt = parseDate(params._updatedAt); + this._completedAt = parseDate(params._completedAt); this._language = params._language; this._source = params._source; @@ -94,6 +81,13 @@ export class BaseInterview extends Surveyable implements IValidatable { this._device = params._device; } + // params must be sanitized and must be valid: + static unserialize(params: BaseInterviewAttributes & { survey: SurveyAttributes; sample: SampleAttributes, sampleBatchNumber?: number }): BaseInterview { + const survey = Survey.unserialize(params.survey); + const sample = Sample.unserialize(params.sample); + return new BaseInterview({ ...params, survey, sample, sampleBatchNumber: params.sampleBatchNumber }); + } + validate(): OptionalValidity { // TODO: implement: this._isValid = true; @@ -112,7 +106,7 @@ export class BaseInterview extends Surveyable implements IValidatable { */ static create(dirtyParams: { [key: string]: any }): BaseInterview | Error[] { const errors = BaseInterview.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseInterview(dirtyParams as ExtendedInterviewAttributes); + return errors.length > 0 ? errors : new BaseInterview(dirtyParams as (ExtendedInterviewAttributes & SurveyableAttributes)); } /** @@ -126,6 +120,11 @@ export class BaseInterview extends Surveyable implements IValidatable { static validateParams(dirtyParams: { [key: string]: any }): Error[] { const errors: Error[] = []; + dirtyParams.assignedDate = parseDate(dirtyParams.assignedDate); + dirtyParams._startedAt = parseDate(dirtyParams._startedAt); + dirtyParams._updatedAt = parseDate(dirtyParams._updatedAt); + dirtyParams._completedAt = parseDate(dirtyParams._completedAt); + // Validate params object: if (!dirtyParams || typeof dirtyParams !== 'object') { errors.push(new Error('BaseInterview validateParams: params is undefined or invalid')); @@ -193,25 +192,6 @@ export class BaseInterview extends Surveyable implements IValidatable { errors.push(new Error('BaseInterview validateParams: invalid _updatedAt')); } - // Validate baseHousehold (if provided) - if (dirtyParams.baseHousehold !== undefined && !(dirtyParams.baseHousehold instanceof BaseHousehold)) { - errors.push( - new Error('BaseInterview validateParams: baseHousehold should be an instance of BaseHousehold') - ); - } - - // Validate basePerson (if provided) - if (dirtyParams.basePerson !== undefined && !(dirtyParams.basePerson instanceof BasePerson)) { - errors.push(new Error('BaseInterview validateParams: basePerson should be an instance of BasePerson')); - } - - // Validate baseOrganization (if provided) - if (dirtyParams.baseOrganization !== undefined && !(dirtyParams.baseOrganization instanceof BaseOrganization)) { - errors.push( - new Error('BaseInterview validateParams: baseOrganization should be an instance of BaseOrganization') - ); - } - // Validate contactPhoneNumber (if provided): // Precise phone number validation must be done in audits, because incorrect phone number should not prevent the interview from being created. if (dirtyParams.contactPhoneNumber !== undefined && typeof dirtyParams.contactPhoneNumber !== 'string') { diff --git a/packages/evolution-common/src/services/baseObjects/BaseJourney.ts b/packages/evolution-common/src/services/baseObjects/BaseJourney.ts index 36f0b8ee..4794555d 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseJourney.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseJourney.ts @@ -15,21 +15,17 @@ import { OptionalValidity, IValidatable } from './Validatable'; import { Weightable, Weight, validateWeights } from './Weight'; import { Uuidable } from './Uuidable'; -import { BaseVisitedPlace } from './BaseVisitedPlace'; -import { BaseTripChain } from './BaseTripChain'; -import { BaseTrip } from './BaseTrip'; +import { parseDate } from '../../utils/DateUtils'; type BaseJourneyAttributes = { _uuid?: string; - startDate: Date; - startTime: number; - endDate: Date; - endTime: number; + startDate?: Date | string; + startTime?: number; + endDate?: Date | string; + endTime?: number; name?: string; - baseVisitedPlaces?: BaseVisitedPlace[]; - baseTrips?: BaseTrip[]; - baseTripChains?: BaseTripChain[]; + } & Weightable; type ExtendedJourneyAttributes = BaseJourneyAttributes & { [key: string]: any }; @@ -41,14 +37,11 @@ class BaseJourney extends Uuidable implements IBaseJourneyAttributes, IValidatab _isValid: OptionalValidity; _weights?: Weight[]; - startDate: Date; - startTime: number; - endDate: Date; - endTime: number; + startDate?: Date; + startTime?: number; + endDate?: Date; + endTime?: number; name?: string; - baseVisitedPlaces?: BaseVisitedPlace[]; - baseTrips?: BaseTrip[]; - baseTripChains?: BaseTripChain[]; _confidentialAttributes: string[] = [ // these attributes should be hidden when exporting @@ -60,14 +53,16 @@ class BaseJourney extends Uuidable implements IBaseJourneyAttributes, IValidatab this._isValid = undefined; this._weights = params._weights; - this.startDate = params.startDate; + this.startDate = parseDate(params.startDate); this.startTime = params.startTime; - this.endDate = params.endDate; + this.endDate = parseDate(params.endDate); this.endTime = params.endTime; this.name = params.name; - this.baseVisitedPlaces = params.baseVisitedPlaces; - this.baseTripChains = params.baseTripChains; - this.baseTrips = params.baseTrips; + } + + // params must be sanitized and must be valid: + static unserialize(params: BaseJourneyAttributes): BaseJourney { + return new BaseJourney(params); } validate(): OptionalValidity { @@ -99,6 +94,9 @@ class BaseJourney extends Uuidable implements IBaseJourneyAttributes, IValidatab static validateParams(dirtyParams: { [key: string]: any }): Error[] { const errors: Error[] = []; + dirtyParams.startDate = parseDate(dirtyParams.startDate); + dirtyParams.endDate = parseDate(dirtyParams.endDate); + // Validate params object: if (!dirtyParams || typeof dirtyParams !== 'object') { errors.push(new Error('BaseJourney validateParams: params is undefined or invalid')); @@ -155,34 +153,6 @@ class BaseJourney extends Uuidable implements IBaseJourneyAttributes, IValidatab errors.push(new Error('BaseJourney validateParams: name should be a string')); } - // Validate baseVisitedPlaces (if provided) - if ( - dirtyParams.baseVisitedPlaces !== undefined && - (!Array.isArray(dirtyParams.baseVisitedPlaces) || - !dirtyParams.baseVisitedPlaces.every((vp) => vp instanceof BaseVisitedPlace)) - ) { - errors.push( - new Error('BaseJourney validateParams: baseVisitedPlaces should be an array of BaseVisitedPlace') - ); - } - - // Validate baseTrips (if provided) - if ( - dirtyParams.baseTrips !== undefined && - (!Array.isArray(dirtyParams.baseTrips) || !dirtyParams.baseTrips.every((trip) => trip instanceof BaseTrip)) - ) { - errors.push(new Error('BaseJourney validateParams: baseTrips should be an array of BaseTrip')); - } - - // Validate baseTripChains (if provided) - if ( - dirtyParams.baseTripChains !== undefined && - (!Array.isArray(dirtyParams.baseTripChains) || - !dirtyParams.baseTripChains.every((tc) => tc instanceof BaseTripChain)) - ) { - errors.push(new Error('BaseJourney validateParams: baseTripChains should be an array of BaseTripChain')); - } - return errors; } } diff --git a/packages/evolution-common/src/services/baseObjects/BaseOrganization.ts b/packages/evolution-common/src/services/baseObjects/BaseOrganization.ts index eb81459e..ca6277a5 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseOrganization.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseOrganization.ts @@ -11,13 +11,10 @@ */ import { OptionalValidity, IValidatable } from './Validatable'; -import { BasePerson } from './BasePerson'; import { BasePlace } from './BasePlace'; import { Weightable, Weight, validateWeights } from './Weight'; import * as OAttr from './attributeTypes/OrganizationAttributes'; import { Uuidable } from './Uuidable'; -import { Vehicleable } from './Vehicleable'; -import { BaseVehicle } from './BaseVehicle'; type BaseOrganizationAttributes = { _uuid?: string; @@ -29,16 +26,9 @@ type BaseOrganizationAttributes = { vehicleNumber?: number; pluginHybridVehicleNumber?: number; electricVehicleNumber?: number; - - basePersons?: BasePerson[]; // employees, clients or any person related to the organization - baseLocations?: BasePlace[]; // all the baseLocations related to the organization - baseHeadquarter?: BasePlace; // baseHeadquarter or the single location for this organization - - contactPerson?: BasePerson; contactPhoneNumber?: string; contactEmail?: string; -} & Weightable & - Vehicleable; +} & Weightable; type ExtendedOrganizationAttributes = BaseOrganizationAttributes & { [key: string]: any }; @@ -53,14 +43,6 @@ class BaseOrganization extends Uuidable implements IValidatable { vehicleNumber?: number; pluginHybridVehicleNumber?: number; electricVehicleNumber?: number; - - basePersons: BasePerson[]; - baseLocations?: BasePlace[]; - baseHeadquarter?: BasePlace; - - baseVehicles?: BaseVehicle[]; - - contactPerson?: BasePerson; contactPhoneNumber?: string; contactEmail?: string; @@ -84,18 +66,15 @@ class BaseOrganization extends Uuidable implements IValidatable { this.vehicleNumber = params.vehicleNumber; this.pluginHybridVehicleNumber = params.pluginHybridVehicleNumber; this.electricVehicleNumber = params.electricVehicleNumber; - - this.basePersons = params.basePersons || []; - this.baseLocations = params.baseLocations || []; - this.baseHeadquarter = params.baseHeadquarter; - - this.baseVehicles = params.baseVehicles || []; - - this.contactPerson = params.contactPerson; this.contactPhoneNumber = params.contactPhoneNumber; this.contactEmail = params.contactEmail; } + // params must be sanitized and must be valid: + static unserialize(params: BaseOrganizationAttributes): BaseOrganization { + return new BaseOrganization(params); + } + validate(): OptionalValidity { // TODO: implement: this._isValid = true; @@ -191,47 +170,6 @@ class BaseOrganization extends Uuidable implements IValidatable { ); } - // Validate basePersons (if provided) - if ( - dirtyParams.basePersons !== undefined && - (!Array.isArray(dirtyParams.basePersons) || - !dirtyParams.basePersons.every((person) => person instanceof BasePerson)) - ) { - errors.push(new Error('BaseOrganization validateParams: basePersons should be an array of BasePerson')); - } - - // Validate baseLocations (if provided) - if ( - dirtyParams.baseLocations !== undefined && - (!Array.isArray(dirtyParams.baseLocations) || - !dirtyParams.baseLocations.every((location) => location instanceof BasePlace)) - ) { - errors.push(new Error('BaseOrganization validateParams: baseLocations should be an array of BasePlace')); - } - - // Validate baseHeadquarter (if provided) - if (dirtyParams.baseHeadquarter !== undefined && !(dirtyParams.baseHeadquarter instanceof BasePlace)) { - errors.push( - new Error('BaseOrganization validateParams: baseHeadquarter should be an instance of BasePlace') - ); - } - - // Validate baseVehicles (if provided) - if ( - dirtyParams.baseVehicles !== undefined && - (!Array.isArray(dirtyParams.baseVehicles) || - !dirtyParams.baseVehicles.every((vehicle) => vehicle instanceof BaseVehicle)) - ) { - errors.push(new Error('BaseOrganization validateParams: baseVehicles should be an array of BaseVehicle')); - } - - // Validate contactPerson (if provided) - if (dirtyParams.contactPerson !== undefined && !(dirtyParams.contactPerson instanceof BasePerson)) { - errors.push( - new Error('BaseOrganization validateParams: contactPerson should be an instance of BasePerson') - ); - } - // Validate contactPhoneNumber (if provided) if (dirtyParams.contactPhoneNumber !== undefined && typeof dirtyParams.contactPhoneNumber !== 'string') { errors.push(new Error('BaseOrganization validateParams: contactPhoneNumber should be a string')); diff --git a/packages/evolution-common/src/services/baseObjects/BasePerson.ts b/packages/evolution-common/src/services/baseObjects/BasePerson.ts index baa303f7..3b3f6ad4 100644 --- a/packages/evolution-common/src/services/baseObjects/BasePerson.ts +++ b/packages/evolution-common/src/services/baseObjects/BasePerson.ts @@ -7,14 +7,8 @@ import { OptionalValidity, IValidatable } from './Validatable'; import { Weightable, Weight, validateWeights } from './Weight'; -import { Tripable } from './Tripable'; import { Uuidable } from './Uuidable'; -import { BasePlace } from './BasePlace'; -import { BaseVisitedPlace } from './BaseVisitedPlace'; -import { BaseTrip } from './BaseTrip'; -import { BaseVehicle } from './BaseVehicle'; import * as PAttr from './attributeTypes/PersonAttributes'; -import { Vehicleable } from './Vehicleable'; type BasePersonAttributes = { _uuid?: string; @@ -37,22 +31,16 @@ type BasePersonAttributes = { isJobTelecommuteCompatible?: PAttr.IsJobTelecommuteCompatible; educationalAttainment?: PAttr.EducationalAttainment; - baseWorkPlaces?: BasePlace[]; - baseSchoolPlaces?: BasePlace[]; - baseHome?: BasePlace; - // must be anonymized: nickname?: string; contactPhoneNumber?: string; contactEmail?: string; -} & Weightable & - Tripable & - Vehicleable; +} & Weightable; type ExtendedPersonAttributes = BasePersonAttributes & { [key: string]: any }; // eslint-disable-next-line @typescript-eslint/no-empty-interface -interface IBasePersonAttributes extends BasePersonAttributes {} +interface IBasePersonAttributes extends BasePersonAttributes { } class BasePerson extends Uuidable implements IBasePersonAttributes, IValidatable { _isValid: OptionalValidity; @@ -76,15 +64,6 @@ class BasePerson extends Uuidable implements IBasePersonAttributes, IValidatable isJobTelecommuteCompatible?: PAttr.IsJobTelecommuteCompatible; educationalAttainment?: PAttr.EducationalAttainment; - baseWorkPlaces?: BasePlace[]; - baseSchoolPlaces?: BasePlace[]; - baseHome?: BasePlace; - - baseVisitedPlaces?: BaseVisitedPlace[]; - baseTrips?: BaseTrip[]; - - baseVehicles?: BaseVehicle[]; - // must be anonymized: nickname?: string; contactPhoneNumber?: string; @@ -125,15 +104,11 @@ class BasePerson extends Uuidable implements IBasePersonAttributes, IValidatable this.contactPhoneNumber = params.contactPhoneNumber; this.contactEmail = params.contactEmail; - this.baseWorkPlaces = params.baseWorkPlaces; - this.baseSchoolPlaces = params.baseSchoolPlaces; - - this.baseVisitedPlaces = params.baseVisitedPlaces || []; - this.baseTrips = params.baseTrips || []; - - this.baseVehicles = params.baseVehicles || []; + } - this.baseHome = params.baseHome; + // params must be sanitized and must be valid: + static unserialize(params: BasePersonAttributes): BasePerson { + return new BasePerson(params); } /** @@ -274,57 +249,6 @@ class BasePerson extends Uuidable implements IBasePersonAttributes, IValidatable errors.push(new Error('BasePerson validateParams: educationalAttainment is not a valid value')); } - // Validate baseWorkPlaces (if provided) - if ( - dirtyParams.baseWorkPlaces !== undefined && - (!Array.isArray(dirtyParams.baseWorkPlaces) || - !dirtyParams.baseWorkPlaces.every((place) => place instanceof BasePlace)) - ) { - errors.push(new Error('BasePerson validateParams: baseWorkPlaces should be an array of BasePlace')); - } - - // Validate baseSchoolPlaces (if provided) - if ( - dirtyParams.baseSchoolPlaces !== undefined && - (!Array.isArray(dirtyParams.baseSchoolPlaces) || - !dirtyParams.baseSchoolPlaces.every((place) => place instanceof BasePlace)) - ) { - errors.push(new Error('BasePerson validateParams: baseSchoolPlaces should be an array of BasePlace')); - } - - // Validate baseHome (if provided) - if (dirtyParams.baseHome !== undefined && !(dirtyParams.baseHome instanceof BasePlace)) { - errors.push(new Error('BasePerson validateParams: baseHome should be an instance of BasePlace')); - } - - // Validate baseVisitedPlaces (if provided) - if ( - dirtyParams.baseVisitedPlaces !== undefined && - (!Array.isArray(dirtyParams.baseVisitedPlaces) || - !dirtyParams.baseVisitedPlaces.every((place) => place instanceof BaseVisitedPlace)) - ) { - errors.push( - new Error('BasePerson validateParams: baseVisitedPlaces should be an array of BaseVisitedPlace') - ); - } - - // Validate baseTrips (if provided) - if ( - dirtyParams.baseTrips !== undefined && - (!Array.isArray(dirtyParams.baseTrips) || !dirtyParams.baseTrips.every((trip) => trip instanceof BaseTrip)) - ) { - errors.push(new Error('BasePerson validateParams: baseTrips should be an array of BaseTrip')); - } - - // Validate baseVehicles (if provided) - if ( - dirtyParams.baseVehicles !== undefined && - (!Array.isArray(dirtyParams.baseVehicles) || - !dirtyParams.baseVehicles.every((vehicle) => vehicle instanceof BaseVehicle)) - ) { - errors.push(new Error('BasePerson validateParams: baseVehicles should be an array of BaseVehicle')); - } - // Validate nickname (if provided) if (dirtyParams.nickname !== undefined && typeof dirtyParams.nickname !== 'string') { errors.push(new Error('BasePerson validateParams: nickname should be a string')); diff --git a/packages/evolution-common/src/services/baseObjects/BasePlace.ts b/packages/evolution-common/src/services/baseObjects/BasePlace.ts index 35e37d8c..748f0b69 100644 --- a/packages/evolution-common/src/services/baseObjects/BasePlace.ts +++ b/packages/evolution-common/src/services/baseObjects/BasePlace.ts @@ -14,7 +14,7 @@ import { isFeature, isPoint } from 'geojson-validation'; import { Uuidable } from './Uuidable'; import { GeocodingPrecisionCategory, LastAction } from './attributeTypes/PlaceAttributes'; -import { BaseAddress } from './BaseAddress'; +import { BaseAddress, BaseAddressAttributes } from './BaseAddress'; import { OptionalValidity, IValidatable } from './Validatable'; import { Device } from './BaseInterview'; @@ -26,7 +26,6 @@ export type BasePlaceAttributes = { geography?: GeoJSON.Feature | undefined; name?: string; shortname?: string; - address?: BaseAddress; osmId?: string; landRoleId?: string; postalId?: string; @@ -65,7 +64,7 @@ export class BasePlace extends Uuidable implements IValidatable { // these attributes should be hidden when exporting ]; - constructor(params: BasePlaceAttributes | ExtendedPlaceAttributes) { + constructor(params: (BasePlaceAttributes | ExtendedPlaceAttributes) & { address?: BaseAddress }) { super(params._uuid); this._isValid = undefined; @@ -87,6 +86,13 @@ export class BasePlace extends Uuidable implements IValidatable { this.zoom = params.zoom; } + // params must be sanitized and must be valid: + static unserialize(params: BasePlaceAttributes & { address?: BaseAddressAttributes }): BasePlace { + const address = params.address ? BaseAddress.unserialize(params.address) : undefined; + const geography = params.geography ? params.geography as GeoJSON.Feature : undefined; + return new BasePlace({ ...params, address, geography }); + } + validate(): OptionalValidity { // TODO: implement: this._isValid = true; @@ -105,7 +111,7 @@ export class BasePlace extends Uuidable implements IValidatable { */ static create(dirtyParams: { [key: string]: any }): BasePlace | Error[] { const errors = BasePlace.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BasePlace(dirtyParams as ExtendedPlaceAttributes); + return errors.length > 0 ? errors : new BasePlace(dirtyParams as (ExtendedPlaceAttributes & { address: BaseAddress })); } /** diff --git a/packages/evolution-common/src/services/baseObjects/BaseSegment.ts b/packages/evolution-common/src/services/baseObjects/BaseSegment.ts index 22a0a3d8..7e629afd 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseSegment.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseSegment.ts @@ -9,7 +9,7 @@ * A segment is the part of a trip using a single mode of transport */ -import { BaseVehicle } from './BaseVehicle'; +import { BaseVehicle, BaseVehicleAttributes, ExtendedVehicleAttributes } from './BaseVehicle'; import { Uuidable } from './Uuidable'; import { OptionalValidity, IValidatable } from './Validatable'; import * as SAttr from './attributeTypes/SegmentAttributes'; @@ -17,8 +17,6 @@ import * as SAttr from './attributeTypes/SegmentAttributes'; export type BaseSegmentAttributes = { _uuid?: string; - baseVehicle?: BaseVehicle; - modeCategory?: SAttr.ModeCategory; // TODO: remove this an include the mode category in the mode itself mode?: SAttr.Mode; }; @@ -28,8 +26,6 @@ export type ExtendedSegmentAttributes = BaseSegmentAttributes & { [key: string]: export class BaseSegment extends Uuidable implements IValidatable { _isValid: OptionalValidity; - baseVehicle?: BaseVehicle; - modeCategory?: SAttr.ModeCategory; mode?: SAttr.Mode; @@ -44,7 +40,11 @@ export class BaseSegment extends Uuidable implements IValidatable { this.modeCategory = params.modeCategory; this.mode = params.mode; - this.baseVehicle = params.baseVehicle; + } + + // params must be sanitized and must be valid: + static unserialize(params: BaseSegmentAttributes): BaseSegment { + return new BaseSegment(params); } validate(): OptionalValidity { @@ -99,11 +99,6 @@ export class BaseSegment extends Uuidable implements IValidatable { errors.push(new Error('BaseSegment validateParams: mode should be a string')); } - // Validate baseVehicle (if provided) - if (dirtyParams.baseVehicle !== undefined && !(dirtyParams.baseVehicle instanceof BaseVehicle)) { - errors.push(new Error('BaseSegment validateParams: baseVehicle should be an instance of BaseVehicle')); - } - return errors; } } diff --git a/packages/evolution-common/src/services/baseObjects/BaseTrip.ts b/packages/evolution-common/src/services/baseObjects/BaseTrip.ts index e61e38be..cffb09f4 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseTrip.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseTrip.ts @@ -11,17 +11,11 @@ import { Uuidable } from './Uuidable'; import { OptionalValidity, IValidatable } from './Validatable'; -import { BaseSegment } from './BaseSegment'; -import { BaseVisitedPlace } from './BaseVisitedPlace'; import { Weightable, Weight, validateWeights } from './Weight'; export type BaseTripAttributes = { _uuid?: string; - baseOrigin?: BaseVisitedPlace; - baseDestination?: BaseVisitedPlace; - baseSegments?: BaseSegment[]; - /** * Departure and arrival time must be calculated from visited places, * so shortcuts must be converted to places and each visited place @@ -35,10 +29,6 @@ export class BaseTrip extends Uuidable implements IValidatable { _isValid: OptionalValidity; _weights?: Weight[]; - baseOrigin?: BaseVisitedPlace; - baseDestination?: BaseVisitedPlace; - baseSegments?: BaseSegment[]; - _confidentialAttributes: string[] = [ // these attributes should be hidden when exporting ]; @@ -48,10 +38,11 @@ export class BaseTrip extends Uuidable implements IValidatable { this._isValid = undefined; this._weights = params._weights; + } - this.baseOrigin = params.baseOrigin; - this.baseDestination = params.baseDestination; - this.baseSegments = params.baseSegments || []; + // params must be sanitized and must be valid: + static unserialize(params: BaseTripAttributes): BaseTrip { + return new BaseTrip(params); } validate(): OptionalValidity { @@ -101,32 +92,6 @@ export class BaseTrip extends Uuidable implements IValidatable { errors.push(...weightsErrors); } - // Validate baseOrigin (if provided) - if (dirtyParams.baseOrigin !== undefined && !(dirtyParams.baseOrigin instanceof BaseVisitedPlace)) { - errors.push(new Error('BaseTrip validateParams: baseOrigin should be an object')); - } - - // Validate baseDestination (if provided) - if (dirtyParams.baseDestination !== undefined && !(dirtyParams.baseDestination instanceof BaseVisitedPlace)) { - errors.push(new Error('BaseTrip validateParams: baseDestination should be an object')); - } - - // Validate baseSegments (if provided) - if (dirtyParams.baseSegments !== undefined && !Array.isArray(dirtyParams.baseSegments)) { - errors.push(new Error('BaseTrip validateParams: baseSegments should be an array')); - } else if (Array.isArray(dirtyParams.baseSegments)) { - // Validate each segment in baseSegments - dirtyParams.baseSegments.forEach((segment: any, index: number) => { - if (!(segment instanceof BaseSegment)) { - errors.push( - new Error( - `BaseTrip validateParams: baseSegments at index ${index} should be an instance of BaseSegment` - ) - ); - } - }); - } - return errors; } } diff --git a/packages/evolution-common/src/services/baseObjects/BaseTripChain.ts b/packages/evolution-common/src/services/baseObjects/BaseTripChain.ts index 1d1c91b9..193e7d6e 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseTripChain.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseTripChain.ts @@ -66,7 +66,11 @@ export class BaseTripChain extends Uuidable implements IValidatable { this.mainActivityCategory = params.mainActivityCategory; this.mainActivity = params.mainActivity; - this.baseTrips = params.baseTrips || []; + } + + // params must be sanitized and must be valid: + static unserialize(params: BaseTripChainAttributes): BaseTripChain { + return new BaseTripChain(params); } validate(): OptionalValidity { @@ -141,21 +145,6 @@ export class BaseTripChain extends Uuidable implements IValidatable { errors.push(new Error('BaseTripChain validateParams: mainActivity should be a string')); } - // Validate baseTrips (if provided) - if (dirtyParams.baseTrips !== undefined && !Array.isArray(dirtyParams.baseTrips)) { - errors.push(new Error('BaseTripChain validateParams: baseTrips should be an array')); - } else if (Array.isArray(dirtyParams.baseTrips)) { - dirtyParams.baseTrips.forEach((trip: any, index: number) => { - if (!(trip instanceof BaseTrip)) { - errors.push( - new Error( - `BaseTripChain validateParams: baseTrips at index ${index} should be an instance of BaseTrip` - ) - ); - } - }); - } - return errors; } } diff --git a/packages/evolution-common/src/services/baseObjects/BaseVehicle.ts b/packages/evolution-common/src/services/baseObjects/BaseVehicle.ts index 946455b5..7537c596 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseVehicle.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseVehicle.ts @@ -58,6 +58,11 @@ export class BaseVehicle extends Uuidable implements IValidatable { this.capacityStanding = params.capacityStanding; } + // params must be sanitized and must be valid: + static unserialize(params: BaseVehicleAttributes): BaseVehicle { + return new BaseVehicle(params); + } + validate(): OptionalValidity { // TODO: implement: this._isValid = true; diff --git a/packages/evolution-common/src/services/baseObjects/BaseVisitedPlace.ts b/packages/evolution-common/src/services/baseObjects/BaseVisitedPlace.ts index 39333f5c..db594dec 100644 --- a/packages/evolution-common/src/services/baseObjects/BaseVisitedPlace.ts +++ b/packages/evolution-common/src/services/baseObjects/BaseVisitedPlace.ts @@ -12,17 +12,17 @@ import { Uuidable } from './Uuidable'; import { OptionalValidity, IValidatable } from './Validatable'; -import { BasePlace } from './BasePlace'; +import { BasePlace, BasePlaceAttributes } from './BasePlace'; +import { BaseAddressAttributes } from './BaseAddress'; import { Weightable, Weight, validateWeights } from './Weight'; import * as VPAttr from './attributeTypes/VisitedPlaceAttributes'; +import { parseDate } from '../../utils/DateUtils'; export type BaseVisitedPlaceAttributes = { _uuid?: string; - basePlace?: BasePlace; - - arrivalDate?: Date; - departureDate?: Date; + arrivalDate?: Date | string; + departureDate?: Date | string; arrivalTime?: number; departureTime?: number; activityCategory?: VPAttr.ActivityCategory; @@ -54,21 +54,26 @@ export class BaseVisitedPlace extends Uuidable implements IValidatable { // these attributes should be hidden when exporting ]; - constructor(params: BaseVisitedPlaceAttributes | ExtendedVisitedPlaceAttributes) { + constructor(params: (BaseVisitedPlaceAttributes | ExtendedVisitedPlaceAttributes) & { basePlace: BasePlace }) { super(params._uuid); this._isValid = undefined; this._weights = params._weights; this.basePlace = params.basePlace; - this.arrivalDate = params.arrivalDate; - this.departureDate = params.departureDate; + this.arrivalDate = parseDate(params.arrivalDate); + this.departureDate = parseDate(params.departureDate); this.arrivalTime = params.arrivalTime; this.departureTime = params.departureTime; this.activityCategory = params.activityCategory; this.activity = params.activity; } + // params must be sanitized and must be valid: + static unserialize(params: BaseVisitedPlaceAttributes & { basePlace: BasePlaceAttributes & { address?: BaseAddressAttributes } }): BaseVisitedPlace { + return new BaseVisitedPlace({ ...params, basePlace: BasePlace.unserialize(params.basePlace) }); + } + validate(): OptionalValidity { // TODO: implement: this._isValid = true; @@ -86,8 +91,25 @@ export class BaseVisitedPlace extends Uuidable implements IValidatable { * @returns BaseVisitedPlace | Error[] */ static create(dirtyParams: { [key: string]: any }): BaseVisitedPlace | Error[] { - const errors = BaseVisitedPlace.validateParams(dirtyParams); - return errors.length > 0 ? errors : new BaseVisitedPlace(dirtyParams as ExtendedVisitedPlaceAttributes); + + const basePlaceParams = { + ...dirtyParams, + geography: dirtyParams.geography, + geocodingQueryString: dirtyParams.geography?.properties?.geocodingQueryString, + lastAction: dirtyParams.geography?.properties?.lastAction, + deviceUsed: dirtyParams.geography?.properties?.platform, + zoom: dirtyParams.geography?.properties?.zoom + }; + + // validate params for both baseVisitedPlace and basePlace: + const errors = [...BasePlace.validateParams(basePlaceParams), ...BaseVisitedPlace.validateParams(dirtyParams)]; + if (errors.length > 0) { + return errors; + } else { + const basePlace = BasePlace.create(basePlaceParams) as BasePlace; + const baseVisitedPlace = new BaseVisitedPlace({basePlace, ...dirtyParams} as ExtendedVisitedPlaceAttributes & { basePlace: BasePlace }); + return baseVisitedPlace; + } } /** @@ -98,6 +120,9 @@ export class BaseVisitedPlace extends Uuidable implements IValidatable { static validateParams(dirtyParams: { [key: string]: any }): Error[] { const errors: Error[] = []; + dirtyParams.arrivalDate = parseDate(dirtyParams.arrivalDate); + dirtyParams.departureDate = parseDate(dirtyParams.departureDate); + // Validate params object: if (!dirtyParams || typeof dirtyParams !== 'object') { errors.push(new Error('BaseVisitedPlace validateParams: params is undefined or invalid')); diff --git a/packages/evolution-common/src/services/baseObjects/Sample.ts b/packages/evolution-common/src/services/baseObjects/Sample.ts index bf4c592a..8b94488a 100644 --- a/packages/evolution-common/src/services/baseObjects/Sample.ts +++ b/packages/evolution-common/src/services/baseObjects/Sample.ts @@ -13,12 +13,12 @@ import { Uuidable } from './Uuidable'; export type SampleAttributes = { _uuid?: string; - name: string; + name?: string; shortname: string; description?: string; }; export class Sample extends Uuidable { - name: string; + name?: string; shortname: string; description?: string; @@ -29,6 +29,11 @@ export class Sample extends Uuidable { this.description = params.description; } + // params must be sanitized and must be valid: + static unserialize(params: SampleAttributes): Sample { + return new Sample(params); + } + /** * Validates attribute types for Sample. * @param dirtyParams The parameters to validate. @@ -52,8 +57,6 @@ export class Sample extends Uuidable { // Validate attributes if (dirtyParams.name !== undefined && typeof dirtyParams.name !== 'string') { errors.push(new Error('Sample validateParams: name should be a string')); - } else if (dirtyParams.name === undefined) { - errors.push(new Error('Sample validateParams: name is required')); } if (dirtyParams.shortname !== undefined && typeof dirtyParams.shortname !== 'string') { errors.push(new Error('Sample validateParams: shortname should be a string')); diff --git a/packages/evolution-common/src/services/baseObjects/Survey.ts b/packages/evolution-common/src/services/baseObjects/Survey.ts index d9e2c517..d9015ef6 100644 --- a/packages/evolution-common/src/services/baseObjects/Survey.ts +++ b/packages/evolution-common/src/services/baseObjects/Survey.ts @@ -6,6 +6,7 @@ */ import { Uuidable } from './Uuidable'; +import { parseDate } from '../../utils/DateUtils'; /** * A specific survey for which a questionnaire is built. Can have single or multiple sample(s). @@ -14,21 +15,21 @@ import { Uuidable } from './Uuidable'; type SurveyAttributes = { _uuid?: string; - name: string; + name?: string; shortname: string; description?: string; - startDate: Date; - endDate: Date; + startDate?: Date | string; + endDate?: Date | string; }; type ExtendedSurveyAttributes = SurveyAttributes & { [key: string]: any }; class Survey extends Uuidable { - name: string; + name?: string; shortname: string; description?: string; - startDate: Date; - endDate: Date; + startDate?: Date; + endDate?: Date; constructor(params: SurveyAttributes | ExtendedSurveyAttributes) { super(params._uuid); @@ -36,8 +37,13 @@ class Survey extends Uuidable { this.name = params.name; this.shortname = params.shortname; this.description = params.description; - this.startDate = params.startDate; - this.endDate = params.endDate; + this.startDate = parseDate(params.startDate); + this.endDate = parseDate(params.endDate); + } + + // params must be sanitized and must be valid: + static unserialize(params: SurveyAttributes): Survey { + return new Survey(params); } /** @@ -60,11 +66,12 @@ class Survey extends Uuidable { errors.push(...uuidErrors); } + dirtyParams.startDate = parseDate(dirtyParams.startDate); + dirtyParams.endDate = parseDate(dirtyParams.endDate); + // validate attributes: if (dirtyParams.name !== undefined && typeof dirtyParams.name !== 'string') { errors.push(new Error('Survey validateParams: name should be a string')); - } else if (dirtyParams.name === undefined) { - errors.push(new Error('Survey validateParams: name is required')); } if (dirtyParams.shortname !== undefined && typeof dirtyParams.shortname !== 'string') { errors.push(new Error('Survey validateParams: shortname should be a string')); @@ -76,19 +83,15 @@ class Survey extends Uuidable { } if ( dirtyParams.startDate !== undefined && - (!(dirtyParams.startDate instanceof Date) || isNaN(dirtyParams.startDate.getDate())) + (!(dirtyParams.startDate instanceof Date) || isNaN(dirtyParams.startDate.getTime())) ) { errors.push(new Error('Survey validateParams: invalid startDate')); - } else if (dirtyParams.startDate === undefined) { - errors.push(new Error('Survey validateParams: startDate is required')); } if ( dirtyParams.endDate !== undefined && - (!(dirtyParams.endDate instanceof Date) || isNaN(dirtyParams.endDate.getDate())) + (!(dirtyParams.endDate instanceof Date) || isNaN(dirtyParams.endDate.getTime())) ) { errors.push(new Error('Survey validateParams: invalid endDate')); - } else if (dirtyParams.endDate === undefined) { - errors.push(new Error('Survey validateParams: endDate is required')); } return errors; diff --git a/packages/evolution-common/src/services/baseObjects/Tripable.ts b/packages/evolution-common/src/services/baseObjects/Tripable.ts deleted file mode 100644 index e443e00d..00000000 --- a/packages/evolution-common/src/services/baseObjects/Tripable.ts +++ /dev/null @@ -1,18 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -/** - * Tripable is a composition class for objects that can have visited places and/or trips associated with them - */ - -import { BaseVisitedPlace } from './BaseVisitedPlace'; -import { BaseTrip } from './BaseTrip'; - -export type Tripable = { - baseVisitedPlaces?: BaseVisitedPlace[]; - baseTrips?: BaseTrip[]; -}; diff --git a/packages/evolution-common/src/services/baseObjects/Vehicleable.ts b/packages/evolution-common/src/services/baseObjects/Vehicleable.ts deleted file mode 100644 index 8e6cb21c..00000000 --- a/packages/evolution-common/src/services/baseObjects/Vehicleable.ts +++ /dev/null @@ -1,14 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -// We must be able to ask questions about a household vehicles or other list of vehicles (company, person, etc.). - -import { BaseVehicle } from './BaseVehicle'; - -export type Vehicleable = { - baseVehicles?: BaseVehicle[]; -}; diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseAddress.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseAddress.test.ts index 0b97b65d..34264ab4 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseAddress.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseAddress.test.ts @@ -58,5 +58,11 @@ describe('BaseAddress Class Tests', () => { expect(addressInstance.country).toEqual(addressParams.country); }); + it('should unserialize object', () => { + const instance = BaseAddress.unserialize(addressParams); + expect(instance).toBeInstanceOf(BaseAddress); + expect(instance.region).toEqual(addressParams.region); + expect(instance.country).toEqual(addressParams.country); + }); }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseHousehold.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseHousehold.test.ts index 76693868..a2655773 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseHousehold.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseHousehold.test.ts @@ -7,23 +7,9 @@ import { v4 as uuidV4 } from 'uuid'; import { BaseHousehold, BaseHouseholdAttributes, ExtendedHouseholdAttributes } from '../BaseHousehold'; -import { BasePerson } from '../BasePerson'; -import { BasePlace } from '../BasePlace'; import * as HAttr from '../attributeTypes/HouseholdAttributes'; import { Weight } from '../Weight'; import { WeightMethod } from '../WeightMethod'; -import { BaseVehicle } from '../BaseVehicle'; - -const baseMembers = [new BasePerson({}), new BasePerson({})]; -const homeGeography = { - type: 'Feature' as const, - geometry: { - type: 'Point' as const, - coordinates: [45.5, -75.5], - }, - properties: {} -}; -const baseHome = new BasePlace({ geography: homeGeography }); const weightMethodAttributes = { _uuid: uuidV4(), @@ -34,15 +20,12 @@ const weightMethodAttributes = { const baseHouseholdAttributes: BaseHouseholdAttributes = { _uuid: uuidV4(), - baseMembers: [new BasePerson({ _uuid: uuidV4() })], - baseHome: new BasePlace({ _uuid: uuidV4() }), size: 3, carNumber: 2, category: 'bar' as HAttr.HouseholdCategory, contactPhoneNumber: '123-456-7890', contactEmail: 'test@example.com', _weights: [{ weight: 5.5, method: new WeightMethod(weightMethodAttributes) }, { weight: 6.3, method: new WeightMethod(weightMethodAttributes) }], - baseVehicles: [new BaseVehicle({ _uuid: uuidV4() })], }; const extendedHouseholdAttributes: ExtendedHouseholdAttributes = { @@ -55,8 +38,6 @@ const baseHouseholdAttributes2: BaseHouseholdAttributes = { size: 2, carNumber: 1, twoWheelNumber: 1, - baseMembers: baseMembers, - baseHome: baseHome, category: 'foo' as HAttr.HouseholdCategory, wouldLikeToParticipateToOtherSurveys: true, homeCarParkings: ['bar', 'foo'] as HAttr.HomePrivateCarParkingType[], @@ -113,18 +94,6 @@ describe('BaseHousehold Class Tests', () => { describe('BaseHousehold Class Additional Tests', () => { - it('should allow increasing the number of baseMembers after declaring the initial size', () => { - const householdInstance = new BaseHousehold(baseHouseholdAttributes2); - const newMember = new BasePerson({}); - householdInstance.baseMembers?.push(newMember); - expect(householdInstance.baseMembers?.length).toBeGreaterThan(baseHouseholdAttributes2.size as number); - }); - - it('should correctly set baseHome property if provided', () => { - const householdInstance = new BaseHousehold(baseHouseholdAttributes2); - expect(householdInstance.baseHome).toEqual(baseHouseholdAttributes2.baseHome); - }); - it('should correctly set category property if provided', () => { const householdInstance = new BaseHousehold(baseHouseholdAttributes2); expect(householdInstance.category).toEqual(baseHouseholdAttributes2.category); @@ -157,8 +126,6 @@ describe('BaseHousehold Class Additional Tests', () => { it('should return an empty array for valid params', () => { const validParams = { _uuid: uuidV4(), - baseMembers: [new BasePerson({}), new BasePerson({})], - baseHome: new BasePlace({}), size: 2, carNumber: 1, twoWheelNumber: 0, @@ -179,8 +146,6 @@ describe('BaseHousehold Class Additional Tests', () => { it('should return an array of errors for invalid params', () => { const invalidParams = { _uuid: 12345, // Invalid UUID - baseMembers: 'not-an-array', // Should be an array of BasePerson objects - baseHome: 'not-a-BasePlace', // Should be a BasePlace object size: -1, // Should be a non-negative integer carNumber: 'invalid', // Should be a non-negative integer twoWheelNumber: 2.5, // Should be a non-negative integer @@ -204,8 +169,6 @@ describe('BaseHousehold Class Additional Tests', () => { new Error('Weightable validateWeights: method at index 0 must be an instance of WeightMethod'), new Error('Weightable validateWeights: weight at index 1 must be a positive number'), new Error('Weightable validateWeights: weight at index 2 must be a positive number'), - new Error('BaseHousehold validateParams: baseMembers should be an array'), - new Error('BaseHousehold validateParams: baseHome is not an instance of BasePlace'), new Error('BaseHousehold validateParams: size should be a positive integer'), new Error('BaseHousehold validateParams: carNumber should be a positive integer'), new Error('BaseHousehold validateParams: twoWheelNumber should be a positive integer'), @@ -225,20 +188,11 @@ describe('BaseHousehold Class Additional Tests', () => { expect(errors).toEqual([]); }); - it('should return an array of errors for missing required params', () => { - const invalidMembersVehiclesAndHomeParams = { - baseMembers: [123,'aa'], - baseVehicles: ['foo','bar'], - baseHome: new Date('2000-01-01'), - }; - const errors = BaseHousehold.validateParams(invalidMembersVehiclesAndHomeParams); - expect(errors).toEqual([ - new Error('BaseHousehold validateParams: baseMembers index 0 is not an instance of BasePerson'), - new Error('BaseHousehold validateParams: baseMembers index 1 is not an instance of BasePerson'), - new Error('BaseHousehold validateParams: baseVehicles index 0 is not an instance of BaseVehicle'), - new Error('BaseHousehold validateParams: baseVehicles index 1 is not an instance of BaseVehicle'), - new Error('BaseHousehold validateParams: baseHome is not an instance of BasePlace'), - ]); + it('should unserialize object', () => { + const instance = BaseHousehold.unserialize(baseHouseholdAttributes); + expect(instance).toBeInstanceOf(BaseHousehold); + expect(instance.size).toEqual(baseHouseholdAttributes.size); + expect(instance.carNumber).toEqual(baseHouseholdAttributes.carNumber); }); }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseInterview.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseInterview.test.ts index af2f35e5..9b65706b 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseInterview.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseInterview.test.ts @@ -7,51 +7,49 @@ import { v4 as uuidV4 } from 'uuid'; import { BaseInterview, BaseInterviewAttributes } from '../BaseInterview'; -import { BaseHousehold, BaseHouseholdAttributes } from '../BaseHousehold'; -import { BasePerson, BasePersonAttributes } from '../BasePerson'; -import { BaseOrganization, BaseOrganizationAttributes } from '../BaseOrganization'; -import { Survey } from '../Survey'; -import { Sample } from '../Sample'; +import { Survey, SurveyAttributes } from '../Survey'; +import { Sample, SampleAttributes } from '../Sample'; import { SurveyableAttributes } from '../Surveyable'; const validUUID = uuidV4(); describe('BaseInterview', () => { - const surveyableAttributes: SurveyableAttributes = { - survey: new Survey({ name: 'Survey name', shortname: 'survey_shortname', startDate: new Date('2023-10-01'), endDate: new Date('2023-10-31') }), - sample: new Sample({ name: 'Sample name', shortname: 'sample_shortname' }), - sampleBatchNumber: 123, - }; - const baseHouseholdAttributes: BaseHouseholdAttributes = { - _uuid: validUUID + const surveyAttributes: SurveyAttributes = { + name: 'Survey name', + shortname: 'survey_shortname', + startDate: '2023-10-01', + endDate: '2023-10-31' }; - const basePersonAttributes: BasePersonAttributes = { - _uuid: validUUID + const sampleAttributes: SampleAttributes = { + name: 'Sample name', + shortname: 'sample_shortname' }; - const baseOrganizationAttributes: BaseOrganizationAttributes = { - _uuid: validUUID + const surveyableAttributes: SurveyableAttributes = { + survey: new Survey(surveyAttributes), + sample: new Sample(sampleAttributes), + sampleBatchNumber: 123, }; - const baseInterviewAttributes: BaseInterviewAttributes = { - ...surveyableAttributes, + const interviewAttributes: BaseInterviewAttributes = { _uuid: validUUID, accessCode: '0000-0000', - baseHousehold: new BaseHousehold(baseHouseholdAttributes), - basePerson: new BasePerson(basePersonAttributes), - baseOrganization: new BaseOrganization(baseOrganizationAttributes), _startedAt: new Date('2023-10-05 02:34:55'), _updatedAt: new Date('2023-10-06 07:00:23'), - _completedAt: new Date('2023-10-07 09:12:00'), - assignedDate: new Date('2023-10-03'), + _completedAt: '2023-10-07 09:12:00', + assignedDate: '2023-10-03', contactPhoneNumber: '+1 514-999-9999', contactEmail: 'test@test.test', _language: 'en', _source: 'web', _isCompleted: true, - _device: 'mobile', + _device: 'mobile' + }; + const baseInterviewAttributes: BaseInterviewAttributes & SurveyableAttributes = { + ...surveyableAttributes, + ...interviewAttributes }; it('should create a new BaseInterview instance', () => { @@ -59,9 +57,6 @@ describe('BaseInterview', () => { expect(interview).toBeInstanceOf(BaseInterview); expect(interview._uuid).toEqual(validUUID); expect(interview.accessCode).toEqual('0000-0000'); - expect(interview.baseHousehold).toBeInstanceOf(BaseHousehold); - expect(interview.basePerson).toBeInstanceOf(BasePerson); - expect(interview.baseOrganization).toBeInstanceOf(BaseOrganization); expect(interview._startedAt).toEqual(new Date('2023-10-05 02:34:55')); expect(interview._updatedAt).toEqual(new Date('2023-10-06 07:00:23')); expect(interview._completedAt).toEqual(new Date('2023-10-07 09:12:00')); @@ -76,7 +71,7 @@ describe('BaseInterview', () => { }); it('should create a new BaseInterview instance with minimal attributes', () => { - const minimalAttributes: BaseInterviewAttributes = { + const minimalAttributes: BaseInterviewAttributes & SurveyableAttributes = { _uuid: validUUID, survey: surveyableAttributes.survey, sample: surveyableAttributes.sample, @@ -86,9 +81,6 @@ describe('BaseInterview', () => { expect(interview).toBeInstanceOf(BaseInterview); expect(interview._uuid).toEqual(validUUID); expect(interview.accessCode).toBeUndefined(); - expect(interview.baseHousehold).toBeUndefined(); - expect(interview.basePerson).toBeUndefined(); - expect(interview.baseOrganization).toBeUndefined(); expect(interview._startedAt).toBeUndefined(); expect(interview._updatedAt).toBeUndefined(); expect(interview._completedAt).toBeUndefined(); @@ -118,9 +110,6 @@ describe('BaseInterview', () => { _updatedAt: new Date(), _completedAt: new Date(), assignedDate: new Date(), - baseHousehold: new BaseHousehold({ _uuid: uuidV4() }), - basePerson: new BasePerson({ _uuid: uuidV4() }), - baseOrganization: new BaseOrganization({ _uuid: uuidV4() }), constactEmail: 'test@test.test', contactPhoneNumber: '514-999-9999 #999', _language: 'fr', @@ -136,12 +125,9 @@ describe('BaseInterview', () => { _uuid: 'invalid-uuid', accessCode: {}, _startedAt: 'invalid-date', - _updatedAt: 122, - assignedDate: -22.2, + _updatedAt: 'invalid-date', + assignedDate: {}, _completedAt: {}, - baseHousehold: 'invalid-household', - basePerson: 'invalid-person', - baseOrganization: 'invalid-organization', contactEmail: new Date(), contactPhoneNumber: Infinity, _language: 'aaa', @@ -162,13 +148,25 @@ describe('BaseInterview', () => { new Error('BaseInterview validateParams: invalid _startedAt'), new Error('BaseInterview validateParams: invalid _completedAt'), new Error('BaseInterview validateParams: invalid _updatedAt'), - new Error('BaseInterview validateParams: baseHousehold should be an instance of BaseHousehold'), - new Error('BaseInterview validateParams: basePerson should be an instance of BasePerson'), - new Error('BaseInterview validateParams: baseOrganization should be an instance of BaseOrganization'), new Error('BaseInterview validateParams: contactPhoneNumber should be a string'), new Error('BaseInterview validateParams: contactEmail should be a string'), new Error('BaseInterview validateParams: _device is invalid'), ]); }); + it('should unserialize object', () => { + const instance = BaseInterview.unserialize({ + ...interviewAttributes, + sample: sampleAttributes, + survey: surveyAttributes, + sampleBatchNumber: 123 + }); + expect(instance).toBeInstanceOf(BaseInterview); + expect(instance.accessCode).toEqual(baseInterviewAttributes.accessCode); + expect(instance.survey).toBeInstanceOf(Survey); + expect(instance.survey.name).toEqual(surveyAttributes.name); + expect(instance.sample).toBeInstanceOf(Sample); + expect(instance.sample.name).toEqual(sampleAttributes.name); + }); + }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseJourney.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseJourney.test.ts index b19440e4..657499d5 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseJourney.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseJourney.test.ts @@ -19,31 +19,6 @@ import * as TCAttr from '../attributeTypes/TripChainAttributes'; const validUUID = uuidV4(); describe('BaseJourney', () => { - const baseVisitedPlaceAttributes: BaseVisitedPlaceAttributes = { - _uuid: validUUID, - basePlace: new BasePlace({}), - arrivalDate: new Date('2023-10-05'), - departureDate: new Date('2023-10-06'), - arrivalTime: 36000, // 10 hours in seconds - departureTime: 72000, // 20 hours in seconds - activityCategory: 'work' as VPAttr.ActivityCategory, - activity: 'workUsual' as VPAttr.Activity, - }; - - const baseTripAttributes: BaseTripAttributes = { - _uuid: validUUID, - baseOrigin: new BaseVisitedPlace(baseVisitedPlaceAttributes), - baseDestination: new BaseVisitedPlace(baseVisitedPlaceAttributes), - baseSegments: [], - }; - - const baseTripChainAttributes: BaseTripChainAttributes = { - _uuid: validUUID, - baseTrips: [new BaseTrip(baseTripAttributes)], - isMultiloop: false, - isConstrained: true, - category: 'simple' as TCAttr.TripChainCategory, - }; const weightMethodAttributes = { _uuid: uuidV4(), @@ -59,9 +34,6 @@ describe('BaseJourney', () => { startTime: 36000, // 10 hours in seconds endTime: 72000, // 20 hours in seconds name: 'Journey name', - baseVisitedPlaces: [new BaseVisitedPlace(baseVisitedPlaceAttributes)], - baseTrips: [new BaseTrip(baseTripAttributes), new BaseTrip(baseTripAttributes)], - baseTripChains: [new BaseTripChain(baseTripChainAttributes)], _weights: [{ weight: 2.333, method: new WeightMethod(weightMethodAttributes) }], foo: 'bar' }; @@ -70,17 +42,11 @@ describe('BaseJourney', () => { const journey = new BaseJourney(baseJourneyAttributes); expect(journey).toBeInstanceOf(BaseJourney); expect(journey._uuid).toEqual(validUUID); - expect(journey.baseTripChains?.length).toEqual(1); expect(journey.name).toEqual('Journey name'); expect(journey.startDate).toEqual(new Date('2023-10-05')); expect(journey.endDate).toEqual(new Date('2023-10-06')); expect(journey.startTime).toEqual(36000); expect(journey.endTime).toEqual(72000); - expect(journey.baseTripChains?.[0]).toBeInstanceOf(BaseTripChain); - expect(journey.baseVisitedPlaces?.length).toEqual(1); - expect(journey.baseVisitedPlaces?.[0]).toBeInstanceOf(BaseVisitedPlace); - expect(journey.baseTrips?.length).toEqual(2); - expect(journey.baseTrips?.[1]).toBeInstanceOf(BaseTrip); expect(journey._weights).toBeDefined(); }); @@ -96,7 +62,6 @@ describe('BaseJourney', () => { const journey = new BaseJourney(minimalAttributes); expect(journey).toBeInstanceOf(BaseJourney); expect(journey._uuid).toEqual(validUUID); - expect(journey.baseTripChains).toBeUndefined(); }); it('should validate a BaseJourney instance', () => { @@ -125,9 +90,6 @@ describe('BaseJourney', () => { endDate: new Date(), endTime: 7200, name: 'Valid Journey', - baseVisitedPlaces: [], - baseTrips: [], - baseTripChains: [], _weights: [], }; @@ -143,9 +105,6 @@ describe('BaseJourney', () => { endDate: 'invalid-date', endTime: 'invalid-time', name: 123, - baseVisitedPlaces: {}, - baseTrips: 'not-an-array', - baseTripChains: 42 }; const errors = BaseJourney.validateParams(invalidParams); @@ -157,11 +116,14 @@ describe('BaseJourney', () => { new Error('BaseJourney validateParams: endDate is required and should be a valid date'), new Error('BaseJourney validateParams: endTime is required and should be a non-negative number'), new Error('BaseJourney validateParams: name should be a string'), - new Error('BaseJourney validateParams: baseVisitedPlaces should be an array of BaseVisitedPlace'), - new Error('BaseJourney validateParams: baseTrips should be an array of BaseTrip'), - new Error('BaseJourney validateParams: baseTripChains should be an array of BaseTripChain'), ]); }); + it('should unserialize object', () => { + const instance = BaseJourney.unserialize(baseJourneyAttributes); + expect(instance).toBeInstanceOf(BaseJourney); + expect(instance.name).toEqual(baseJourneyAttributes.name); + }); + }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseOrganization.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseOrganization.test.ts index 2d78ac55..f0c32cb8 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseOrganization.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseOrganization.test.ts @@ -8,9 +8,6 @@ import { v4 as uuidV4 } from 'uuid'; import { BaseOrganization, BaseOrganizationAttributes, ExtendedOrganizationAttributes } from '../BaseOrganization'; import * as OAttr from '../attributeTypes/OrganizationAttributes'; -import { BaseVehicle } from '../BaseVehicle'; -import { BasePerson } from '../BasePerson'; -import { BasePlace } from '../BasePlace'; import { Weight } from '../Weight'; import { WeightMethod } from '../WeightMethod'; @@ -31,16 +28,11 @@ describe('BaseOrganization', () => { shortname: 'xyz_corp', category: 'company' as OAttr.OrganizationCategory, size: 500, - baseVehicles: [new BaseVehicle({ _uuid: uuidV4() }), new BaseVehicle({ _uuid: uuidV4() })], contactEmail: 'test@test.test', - contactPerson: new BasePerson({ _uuid: uuidV4() }), contactPhoneNumber: '123-456-7890', - basePersons: [new BasePerson({ _uuid: uuidV4() }), new BasePerson({ _uuid: uuidV4() }), new BasePerson({ _uuid: uuidV4() })], vehicleNumber: 23, pluginHybridVehicleNumber: 12, electricVehicleNumber: 0, - baseLocations: [new BasePlace({ _uuid: uuidV4() }), new BasePlace({ _uuid: uuidV4() })], - baseHeadquarter: new BasePlace({ _uuid: uuidV4() }), _weights: [{ weight: 0.1, method: new WeightMethod(weightMethodAttributes) }], }; @@ -53,23 +45,11 @@ describe('BaseOrganization', () => { expect(organization.shortname).toEqual('xyz_corp'); expect(organization.category).toEqual('company'); expect(organization.size).toEqual(500); - expect(organization.baseVehicles).toHaveLength(2); - expect(organization.baseVehicles?.[0]).toBeInstanceOf(BaseVehicle); - expect(organization.baseVehicles?.[1]).toBeInstanceOf(BaseVehicle); expect(organization.contactEmail).toEqual('test@test.test'); - expect(organization.contactPerson).toBeInstanceOf(BasePerson); expect(organization.contactPhoneNumber).toEqual('123-456-7890'); - expect(organization.basePersons).toHaveLength(3); - expect(organization.basePersons?.[0]).toBeInstanceOf(BasePerson); - expect(organization.basePersons?.[1]).toBeInstanceOf(BasePerson); - expect(organization.basePersons?.[2]).toBeInstanceOf(BasePerson); expect(organization.vehicleNumber).toEqual(23); expect(organization.pluginHybridVehicleNumber).toEqual(12); expect(organization.electricVehicleNumber).toEqual(0); - expect(organization.baseLocations).toHaveLength(2); - expect(organization.baseLocations?.[0]).toBeInstanceOf(BasePlace); - expect(organization.baseLocations?.[1]).toBeInstanceOf(BasePlace); - expect(organization.baseHeadquarter).toBeInstanceOf(BasePlace); expect(organization._weights).toBeDefined(); }); @@ -125,11 +105,6 @@ describe('BaseOrganization', () => { vehicleNumber: 5, pluginHybridVehicleNumber: 2, electricVehicleNumber: 3, - basePersons: [new BasePerson({}), new BasePerson({})], - baseLocations: [new BasePlace({}), new BasePlace({})], - baseHeadquarter: new BasePlace({}), - baseVehicles: [new BaseVehicle({}), new BaseVehicle({})], - contactPerson: new BasePerson({}), contactPhoneNumber: '123-456-7890', contactEmail: 'valid@example.com', _weights: [{ weight: 2.333, method: new WeightMethod(weightMethodAttributes) }], @@ -149,11 +124,6 @@ describe('BaseOrganization', () => { vehicleNumber: 'invalid', // Non-integer vehicleNumber pluginHybridVehicleNumber: -2, // Negative pluginHybridVehicleNumber electricVehicleNumber: 'invalid', // Non-integer electricVehicleNumber - basePersons: [new BasePerson({}), {}], // Invalid basePersons - baseLocations: 'not-an-array', // Invalid baseLocations - baseHeadquarter: {}, // Invalid baseHeadquarter - baseVehicles: [new BaseVehicle({}), 'invalid'], // Invalid baseVehicles - contactPerson: 'invalid', // Invalid contactPerson contactPhoneNumber: 1234567890, // Invalid contactPhoneNumber contactEmail: 'invalid-email', // Invalid contactEmail _weights: 'not-an-array', // Invalid _weights @@ -171,11 +141,6 @@ describe('BaseOrganization', () => { new Error('BaseOrganization validateParams: vehicleNumber should be a positive integer'), new Error('BaseOrganization validateParams: pluginHybridVehicleNumber should be a positive integer'), new Error('BaseOrganization validateParams: electricVehicleNumber should be a positive integer'), - new Error('BaseOrganization validateParams: basePersons should be an array of BasePerson'), - new Error('BaseOrganization validateParams: baseLocations should be an array of BasePlace'), - new Error('BaseOrganization validateParams: baseHeadquarter should be an instance of BasePlace'), - new Error('BaseOrganization validateParams: baseVehicles should be an array of BaseVehicle'), - new Error('BaseOrganization validateParams: contactPerson should be an instance of BasePerson'), new Error('BaseOrganization validateParams: contactPhoneNumber should be a string'), ]); }); @@ -189,4 +154,10 @@ describe('BaseOrganization', () => { const errors = BaseOrganization.validateParams(params); expect(errors.length).toBe(0); }); + + it('should unserialize object', () => { + const instance = BaseOrganization.unserialize(baseOrganizationAttributes); + expect(instance).toBeInstanceOf(BaseOrganization); + expect(instance.name).toEqual(baseOrganizationAttributes.name); + }); }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BasePerson.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BasePerson.test.ts index 76d8530e..e2847c08 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BasePerson.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BasePerson.test.ts @@ -7,10 +7,6 @@ import { BasePerson, BasePersonAttributes, ExtendedPersonAttributes } from '../BasePerson'; import { v4 as uuidV4 } from 'uuid'; import * as PAttr from '../attributeTypes/PersonAttributes'; -import { BaseTrip, BaseTripAttributes } from '../BaseTrip'; -import { BaseVisitedPlace, BaseVisitedPlaceAttributes } from '../BaseVisitedPlace'; -import { BasePlace, BasePlaceAttributes } from '../BasePlace'; -import { BaseVehicle, BaseVehicleAttributes } from '../BaseVehicle'; import { Weight } from '../Weight'; import { WeightMethod } from '../WeightMethod'; @@ -44,12 +40,6 @@ describe('BasePerson', () => { isOnTheRoadWorker: 'no' as PAttr.IsOnTheRoadWorker, // Valid on-the-road worker status isJobTelecommuteCompatible: 'yes' as PAttr.IsJobTelecommuteCompatible, // Valid telecommute compatibility status educationalAttainment: 'PhD' as PAttr.EducationalAttainment, // Valid educational attainment - baseTrips: [new BaseTrip({} as BaseTripAttributes), new BaseTrip({} as BaseTripAttributes)], // Valid trips - baseVisitedPlaces: [new BaseVisitedPlace({} as BaseVisitedPlaceAttributes)], // Valid visited places - baseVehicles: [new BaseVehicle({} as BaseVehicleAttributes)], // Empty baseVehicles - baseWorkPlaces: [new BasePlace({} as BasePlaceAttributes)], // Valid workPlaces - baseSchoolPlaces: [new BasePlace({} as BasePlaceAttributes)], // Valid schoolPlaces - baseHome: new BasePlace({} as BasePlaceAttributes), // Valid home _weights: [{ weight: 1.5, method: new WeightMethod(weightMethodAttributes) }], foo: 'bar', // extended attribute }; @@ -78,30 +68,16 @@ describe('BasePerson', () => { expect(person.isOnTheRoadWorker).toBe('no'); expect(person.isJobTelecommuteCompatible).toBe('yes'); expect(person.educationalAttainment).toBe('PhD'); - expect(person.baseVisitedPlaces?.length).toEqual(1); - expect(person.baseTrips?.length).toEqual(2); - expect(person.baseVehicles?.length).toEqual(1); }); it('should allow empty arrays and home', () => { const personAttributes2: ExtendedPersonAttributes = { _uuid: validUuid, - baseTrips: [], // Empty trips - baseVisitedPlaces: [], // Empty visited places - baseVehicles: [], // Empty vehicles - baseWorkPlaces: [], // Empty work places - baseSchoolPlaces: [], // Empty school places - baseHome: undefined, foo: 'bar', // extended attribute }; const person2 = new BasePerson(personAttributes2); - expect(person2.baseVisitedPlaces?.length).toEqual(0); - expect(person2.baseTrips?.length).toEqual(0); - expect(person2.baseVehicles?.length).toEqual(0); - expect(person2.baseWorkPlaces?.length).toEqual(0); - expect(person2.baseSchoolPlaces?.length).toEqual(0); - expect(person2.baseHome).toBeUndefined(); + expect(person2).toBeInstanceOf(BasePerson); }); it('should validate a BasePerson instance', () => { @@ -143,12 +119,6 @@ describe('BasePerson', () => { isOnTheRoadWorker: true, isJobTelecommuteCompatible: false, educationalAttainment: 'Ph.D', - baseWorkPlaces: [new BasePlace({})], - baseSchoolPlaces: [new BasePlace({})], - baseHome: new BasePlace({}), - baseVisitedPlaces: [new BaseVisitedPlace({})], - baseTrips: [new BaseTrip({})], - baseVehicles: [new BaseVehicle({})], nickname: 'John Doe', contactPhoneNumber: '123-456-7890', contactEmail: 'john.doe@example.com', @@ -177,12 +147,6 @@ describe('BasePerson', () => { isOnTheRoadWorker: Infinity, isJobTelecommuteCompatible: 1234, educationalAttainment: 5678, - baseWorkPlaces: 'InvalidArray', // Invalid type - baseSchoolPlaces: [new BasePlace({}), 'InvalidPlace'], // Invalid type in array - baseHome: 'InvalidPlace', // Invalid type - baseVisitedPlaces: [new BaseVisitedPlace({}), 'InvalidPlace'], // Invalid type in array - baseTrips: [new BaseTrip({}), 'InvalidTrip'], // Invalid type in array - baseVehicles: [new BaseVehicle({}), 'InvalidVehicle'], // Invalid type in array contactPhoneNumber: 123, // Invalid type contactEmail: 43.4, // Invalid email format }; @@ -207,12 +171,6 @@ describe('BasePerson', () => { new Error('BasePerson validateParams: isOnTheRoadWorker should be a boolean'), new Error('BasePerson validateParams: isJobTelecommuteCompatible should be a boolean'), new Error('BasePerson validateParams: educationalAttainment is not a valid value'), - new Error('BasePerson validateParams: baseWorkPlaces should be an array of BasePlace'), - new Error('BasePerson validateParams: baseSchoolPlaces should be an array of BasePlace'), - new Error('BasePerson validateParams: baseHome should be an instance of BasePlace'), - new Error('BasePerson validateParams: baseVisitedPlaces should be an array of BaseVisitedPlace'), - new Error('BasePerson validateParams: baseTrips should be an array of BaseTrip'), - new Error('BasePerson validateParams: baseVehicles should be an array of BaseVehicle'), new Error('BasePerson validateParams: contactPhoneNumber should be a string'), new Error('BasePerson validateParams: contactEmail should be a string'), ]); @@ -275,12 +233,6 @@ describe('BasePerson', () => { isOnTheRoadWorker: true, isJobTelecommuteCompatible: false, educationalAttainment: 'none', - baseWorkPlaces: [new BasePlace({})], - baseSchoolPlaces: [new BasePlace({})], - baseHome: new BasePlace({}), - baseVisitedPlaces: [new BaseVisitedPlace({})], - baseTrips: [new BaseTrip({})], - baseVehicles: [new BaseVehicle({})], nickname: 'John Doe', contactPhoneNumber: '123-456-7890', contactEmail: 'john.doe@example.com', @@ -290,4 +242,10 @@ describe('BasePerson', () => { expect(errors.length).toBe(0); }); + it('should unserialize object', () => { + const instance = BasePerson.unserialize(personAttributes); + expect(instance).toBeInstanceOf(BasePerson); + expect(instance.age).toEqual(personAttributes.age); + }); + }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BasePlace.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BasePlace.test.ts index bd81343f..64428b21 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BasePlace.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BasePlace.test.ts @@ -8,67 +8,67 @@ import { v4 as uuidV4 } from 'uuid'; import { BasePlace, BasePlaceAttributes } from '../BasePlace'; import { GeocodingPrecisionCategory } from '../attributeTypes/PlaceAttributes'; -import { BaseAddress } from '../BaseAddress'; +import { BaseAddress, BaseAddressAttributes } from '../BaseAddress'; const validUUID = uuidV4(); const validUUID2 = uuidV4(); +const baseAddressAttributes: BaseAddressAttributes = { + _uuid: validUUID, + civicNumber: 123, + civicNumberSuffix: 'A', + unitNumber: 456, + streetName: 'Main St', + streetNameHomogenized: 'main street', + streetNameId: 'street123', + streetNameInternalId: 'internalStreet123', + municipalityName: 'Sample City', + municipalityCode: 'sampleCode', + postalMunicipalityName: 'Sample Postal City', + region: 'Sample State', + country: 'Sample Country', + postalCode: '12345', + addressId: 'address123', + internalId: 'internal123', +}; +const geojson = { + type: 'Feature', + id: 112223, + geometry: { + type: 'Point', + coordinates: [45.5, -89.0033423], + }, + properties: { + foo: 'boo', + bar: 'far' + }, +} as GeoJSON.Feature; -describe('BasePlace', () => { - const baseAddress: BaseAddress = { - _uuid: validUUID, - civicNumber: 123, - civicNumberSuffix: 'A', - unitNumber: 456, - streetName: 'Main St', - streetNameHomogenized: 'main street', - streetNameId: 'street123', - streetNameInternalId: 'internalStreet123', - municipalityName: 'Sample City', - municipalityCode: 'sampleCode', - postalMunicipalityName: 'Sample Postal City', - region: 'Sample State', - country: 'Sample Country', - postalCode: '12345', - addressId: 'address123', - internalId: 'internal123', - }; +const basePlaceAttributes: BasePlaceAttributes = { + _uuid: validUUID2, + geography: geojson, + name: 'Sample Place', + shortname: 'Sample', + osmId: 'n1234', + landRoleId: 'land123', + postalId: 'postal123', + buildingId: 'building123', + internalId: 'internal123', + geocodingPrecisionCategory: 'high' as GeocodingPrecisionCategory, + geocodingPrecisionMeters: 100, + geocodingQueryString: 'Sample query', + lastAction: 'preGeocoded', + deviceUsed: 'tablet', + zoom: 14, +}; - const geojson = { - type: 'Feature', - id: 112223, - geometry: { - type: 'Point', - coordinates: [45.5, -89.0033423], - }, - properties: { - foo: 'boo', - bar: 'far' - }, - } as GeoJSON.Feature; +const baseAddress = new BaseAddress(baseAddressAttributes); - const basePlaceAttributes: BasePlaceAttributes = { - _uuid: validUUID2, - geography: geojson, - name: 'Sample Place', - shortname: 'Sample', - address: baseAddress, - osmId: 'n1234', - landRoleId: 'land123', - postalId: 'postal123', - buildingId: 'building123', - internalId: 'internal123', - geocodingPrecisionCategory: 'high' as GeocodingPrecisionCategory, - geocodingPrecisionMeters: 100, - geocodingQueryString: 'Sample query', - lastAction: 'preGeocoded', - deviceUsed: 'tablet', - zoom: 14, - }; +describe('BasePlace', () => { it('should create a new BasePlace instance', () => { - const place = new BasePlace(basePlaceAttributes); + const place = new BasePlace({ ...basePlaceAttributes, address: baseAddress }); expect(place).toBeInstanceOf(BasePlace); expect(place._uuid).toEqual(validUUID2); expect(place.name).toEqual('Sample Place'); @@ -278,4 +278,12 @@ describe('validateParams', () => { expect(errors.length).toBe(0); }); + it('should unserialize object', () => { + const instance = BasePlace.unserialize({ ...basePlaceAttributes, address: baseAddressAttributes }); + expect(instance).toBeInstanceOf(BasePlace); + expect(instance.landRoleId).toEqual(basePlaceAttributes.landRoleId); + expect(instance.address).toBeInstanceOf(BaseAddress); + expect(instance.address?.civicNumber).toEqual(baseAddressAttributes.civicNumber); + }); + }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseSegment.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseSegment.test.ts index 19e72297..7e3bb405 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseSegment.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseSegment.test.ts @@ -14,18 +14,9 @@ import * as SAttr from '../attributeTypes/SegmentAttributes'; const validUUID = uuidV4(); describe('BaseSegment', () => { - const baseVehicleAttributes: BaseVehicleAttributes = { - _uuid: uuidV4(), - make: 'Toyota' as VAttr.Make, - model: 'Camry' as VAttr.Model, - licensePlateNumber: 'ABC123', - capacitySeated: 5, - capacityStanding: 2 - }; const baseSegmentAttributes: BaseSegmentAttributes = { _uuid: validUUID, - baseVehicle: new BaseVehicle(baseVehicleAttributes), modeCategory: 'car' as SAttr.ModeCategory, mode: 'carDriver' as SAttr.Mode, }; @@ -34,7 +25,6 @@ describe('BaseSegment', () => { const segment = new BaseSegment(baseSegmentAttributes); expect(segment).toBeInstanceOf(BaseSegment); expect(segment._uuid).toEqual(validUUID); - expect(segment.baseVehicle).toBeInstanceOf(BaseVehicle); expect(segment.modeCategory).toEqual('car'); expect(segment.mode).toEqual('carDriver'); }); @@ -47,7 +37,6 @@ describe('BaseSegment', () => { const segment = new BaseSegment(minimalAttributes); expect(segment).toBeInstanceOf(BaseSegment); expect(segment._uuid).toEqual(validUUID); - expect(segment.baseVehicle).toBeUndefined(); expect(segment.modeCategory).toBeUndefined(); expect(segment.mode).toBeUndefined(); }); @@ -94,7 +83,6 @@ describe('BaseSegment', () => { const params = { modeCategory: 42, // Invalid type mode: new Date(), // Invalid type - baseVehicle: new BaseVehicle({}), }; const result = BaseSegment.validateParams(params); @@ -107,18 +95,9 @@ describe('BaseSegment', () => { ]); }); - it('should return an array of errors for missing vehicleType in baseVehicle', () => { - const params = { - modeCategory: 'transit', - mode: 'tram', - baseVehicle: {}, // wrong vehicleType - }; - - const result = BaseSegment.validateParams(params); - - expect(result).toHaveLength(1); - expect(result).toEqual([ - new Error('BaseSegment validateParams: baseVehicle should be an instance of BaseVehicle'), - ]); + it('should unserialize object', () => { + const instance = BaseSegment.unserialize(baseSegmentAttributes); + expect(instance).toBeInstanceOf(BaseSegment); + expect(instance.mode).toEqual(baseSegmentAttributes.mode); }); }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseTrip.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseTrip.test.ts index 33ababc9..7dffbcb2 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseTrip.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseTrip.test.ts @@ -17,16 +17,6 @@ import { WeightMethod } from '../WeightMethod'; const validUUID = uuidV4(); describe('BaseTrip', () => { - const baseVisitedPlaceAttributes: BaseVisitedPlaceAttributes = { - _uuid: uuidV4(), - basePlace: new BasePlace({}), - arrivalDate: new Date('2023-10-01'), - departureDate: new Date(), - arrivalTime: 36000, // 10:00 AM in seconds since midnight - departureTime: 39600, // 11:00 AM in seconds since midnight - activityCategory: 'leisure' as VPAttr.ActivityCategory, - activity: 'leisureStroll' as VPAttr.Activity, - }; const weightMethodAttributes = { _uuid: uuidV4(), @@ -38,33 +28,22 @@ describe('BaseTrip', () => { const baseTripAttributes: BaseTripAttributes = { _uuid: validUUID, _weights: [{ weight: 34.444, method: new WeightMethod(weightMethodAttributes) }], - baseOrigin: new BaseVisitedPlace(baseVisitedPlaceAttributes), - baseDestination: new BaseVisitedPlace(baseVisitedPlaceAttributes), - baseSegments: [new BaseSegment({ _uuid: uuidV4() } as BaseSegmentAttributes), new BaseSegment({ _uuid: uuidV4() } as BaseSegmentAttributes)], }; it('should create a new BaseTrip instance', () => { const trip = new BaseTrip(baseTripAttributes); expect(trip).toBeInstanceOf(BaseTrip); expect(trip._uuid).toEqual(validUUID); - expect(trip.baseOrigin).toBeInstanceOf(BaseVisitedPlace); - expect(trip.baseDestination).toBeInstanceOf(BaseVisitedPlace); - expect(trip.baseSegments?.length).toEqual(2); }); it('should create a new BaseTrip instance with minimal attributes', () => { const minimalAttributes: BaseTripAttributes = { _uuid: validUUID, - baseSegments: [], }; const trip = new BaseTrip(minimalAttributes); expect(trip).toBeInstanceOf(BaseTrip); expect(trip._uuid).toEqual(validUUID); - expect(trip.baseOrigin).toBeUndefined(); - expect(trip.baseDestination).toBeUndefined(); - expect(trip.baseSegments).toEqual([]); - expect(trip.baseSegments?.length).toEqual(0); }); it('should validate a BaseTrip instance', () => { @@ -99,8 +78,8 @@ describe('BaseTrip', () => { const params = { _uuid: uuidV4(), - baseOrigin: new BaseVisitedPlace({}), - baseDestination: new BaseVisitedPlace({}), + baseOrigin: new BaseVisitedPlace({ basePlace: new BasePlace({}) }), + baseDestination: new BaseVisitedPlace({ basePlace: new BasePlace({}) }), baseSegments: [ new BaseSegment({}), new BaseSegment({}), @@ -114,22 +93,12 @@ describe('BaseTrip', () => { it('should return errors for invalid parameters', () => { const params = { _uuid: 'invalid-uuid', - baseOrigin: 'invalid-origin', - baseDestination: new Date(), - baseSegments: [ - {}, - 'invalid-segment', - ], }; const errors = BaseTrip.validateParams(params); - expect(errors).toHaveLength(5); // Two errors expected + expect(errors).toHaveLength(1); // Two errors expected expect(errors).toEqual([ new Error('Uuidable validateParams: invalid uuid'), - new Error('BaseTrip validateParams: baseOrigin should be an object'), - new Error('BaseTrip validateParams: baseDestination should be an object'), - new Error('BaseTrip validateParams: baseSegments at index 0 should be an instance of BaseSegment'), - new Error('BaseTrip validateParams: baseSegments at index 1 should be an instance of BaseSegment'), ]); }); @@ -139,4 +108,9 @@ describe('BaseTrip', () => { expect(errors).toHaveLength(0); }); + it('should unserialize object', () => { + const instance = BaseTrip.unserialize(baseTripAttributes); + expect(instance).toBeInstanceOf(BaseTrip); + expect(instance._uuid).toEqual(baseTripAttributes._uuid); + }); }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseTripChain.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseTripChain.test.ts index c9d56e42..bfb49953 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseTripChain.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseTripChain.test.ts @@ -17,10 +17,7 @@ const validUUID = uuidV4(); describe('BaseTripChain', () => { const baseTripAttributes: BaseTripAttributes = { - _uuid: uuidV4(), - baseOrigin: undefined, - baseDestination: undefined, - baseSegments: [], + _uuid: uuidV4() }; const weightMethodAttributes = { @@ -33,7 +30,6 @@ describe('BaseTripChain', () => { const baseTripChainAttributes: BaseTripChainAttributes = { _uuid: validUUID, _weights: [{ weight: 0.0, method: new WeightMethod(weightMethodAttributes) }], - baseTrips: [new BaseTrip(baseTripAttributes)], isMultiloop: false, isConstrained: true, category: 'simple' as TCAttr.TripChainCategory, @@ -45,12 +41,6 @@ describe('BaseTripChain', () => { const tripChain = new BaseTripChain(baseTripChainAttributes); expect(tripChain).toBeInstanceOf(BaseTripChain); expect(tripChain._uuid).toEqual(validUUID); - expect(tripChain.baseTrips?.length).toEqual(1); - expect(tripChain.baseTrips?.[0]).toBeInstanceOf(BaseTrip); - expect(tripChain.baseTrips?.[0]._uuid).toEqual(baseTripAttributes._uuid); - expect(tripChain.baseTrips?.[0].baseSegments?.length).toEqual(0); - expect(tripChain.baseTrips?.[0].baseOrigin).toBeUndefined(); - expect(tripChain.baseTrips?.[0].baseDestination).toBeUndefined(); expect(tripChain.isMultiloop).toEqual(false); expect(tripChain.isConstrained).toEqual(true); expect(tripChain.category).toEqual('simple'); @@ -61,7 +51,6 @@ describe('BaseTripChain', () => { it('should create a new BaseTripChain instance with minimal attributes', () => { const minimalAttributes: BaseTripChainAttributes = { _uuid: validUUID, - baseTrips: [], isMultiloop: true, isConstrained: false, category: 'complex' as TCAttr.TripChainCategory, @@ -70,7 +59,6 @@ describe('BaseTripChain', () => { const tripChain = new BaseTripChain(minimalAttributes); expect(tripChain).toBeInstanceOf(BaseTripChain); expect(tripChain._uuid).toEqual(validUUID); - expect(tripChain.baseTrips).toEqual([]); expect(tripChain.isMultiloop).toEqual(true); expect(tripChain.isConstrained).toEqual(false); expect(tripChain.category).toEqual('complex'); @@ -115,7 +103,6 @@ describe('BaseTripChain', () => { category: 'category', mainActivityCategory: 'work', mainActivity: 'workUsual', - baseTrips: [], _weights: [{ weight: 1, method: new WeightMethod(weightMethodAttributes) }], }; @@ -131,7 +118,6 @@ describe('BaseTripChain', () => { category: 2355, mainActivityCategory: -324.5, mainActivity: {}, - baseTrips: [{}, 'invalid trip'], _weights: [{ weight: 1, method: new WeightMethod(weightMethodAttributes) }], }; @@ -143,8 +129,6 @@ describe('BaseTripChain', () => { new Error('BaseTripChain validateParams: category should be a string'), new Error('BaseTripChain validateParams: mainActivityCategory should be a string'), new Error('BaseTripChain validateParams: mainActivity should be a string'), - new Error('BaseTripChain validateParams: baseTrips at index 0 should be an instance of BaseTrip'), - new Error('BaseTripChain validateParams: baseTrips at index 1 should be an instance of BaseTrip'), ]); }); @@ -154,4 +138,10 @@ describe('BaseTripChain', () => { expect(errors).toHaveLength(0); }); + it('should unserialize object', () => { + const instance = BaseTripChain.unserialize(baseTripChainAttributes); + expect(instance).toBeInstanceOf(BaseTripChain); + expect(instance.category).toEqual(baseTripChainAttributes.category); + }); + }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseVehicle.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseVehicle.test.ts index ffd6c445..1a025e01 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseVehicle.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseVehicle.test.ts @@ -129,4 +129,10 @@ describe('BaseVehicle', () => { expect(errors).toHaveLength(0); }); + it('should unserialize object', () => { + const instance = BaseVehicle.unserialize(baseVehicleAttributes); + expect(instance).toBeInstanceOf(BaseVehicle); + expect(instance.make).toEqual(baseVehicleAttributes.make); + }); + }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/BaseVisitedPlace.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/BaseVisitedPlace.test.ts index 3a1e8aa9..bba8b34b 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/BaseVisitedPlace.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/BaseVisitedPlace.test.ts @@ -43,7 +43,6 @@ describe('BaseVisitedPlace', () => { const baseVisitedPlaceAttributes: BaseVisitedPlaceAttributes = { _uuid: validUUID, - basePlace: new BasePlace(basePlaceAttributes), arrivalDate: new Date('2023-10-05'), departureDate: new Date('2023-10-06'), arrivalTime: 36000, // 10:00 AM in seconds since midnight @@ -54,7 +53,7 @@ describe('BaseVisitedPlace', () => { }; it('should create a new BaseVisitedPlace instance', () => { - const visitedPlace = new BaseVisitedPlace(baseVisitedPlaceAttributes); + const visitedPlace = new BaseVisitedPlace({ ...baseVisitedPlaceAttributes, basePlace: new BasePlace(basePlaceAttributes) }); expect(visitedPlace).toBeInstanceOf(BaseVisitedPlace); expect(visitedPlace._uuid).toEqual(validUUID); expect(visitedPlace.basePlace).toBeInstanceOf(BasePlace); @@ -80,12 +79,11 @@ describe('BaseVisitedPlace', () => { const minimalVisitedPlaceAttributes: BaseVisitedPlaceAttributes = { _uuid: validUUID, - basePlace: new BasePlace(minimalBasePlaceAttributes), activityCategory: 'home' as VPAttr.ActivityCategory, activity: 'home' as VPAttr.Activity, }; - const visitedPlace = new BaseVisitedPlace(minimalVisitedPlaceAttributes); + const visitedPlace = new BaseVisitedPlace({ ...minimalVisitedPlaceAttributes, basePlace: new BasePlace(minimalBasePlaceAttributes) }); expect(visitedPlace).toBeInstanceOf(BaseVisitedPlace); expect(visitedPlace._uuid).toEqual(validUUID); expect(visitedPlace.basePlace).toBeInstanceOf(BasePlace); @@ -100,7 +98,7 @@ describe('BaseVisitedPlace', () => { }); it('should validate a BaseVisitedPlace instance', () => { - const visitedPlace = new BaseVisitedPlace(baseVisitedPlaceAttributes); + const visitedPlace = new BaseVisitedPlace({ ...baseVisitedPlaceAttributes, basePlace: new BasePlace(basePlaceAttributes) }); expect(visitedPlace.isValid()).toBeUndefined(); const validationResult = visitedPlace.validate(); expect(validationResult).toBe(true); @@ -113,12 +111,12 @@ describe('BaseVisitedPlace', () => { customAttribute: 'Custom Value', }; - const visitedPlace = new BaseVisitedPlace(extendedVisitedPlaceAttributes); + const visitedPlace = new BaseVisitedPlace({ ...extendedVisitedPlaceAttributes, basePlace: new BasePlace(basePlaceAttributes) }); expect(visitedPlace).toBeInstanceOf(BaseVisitedPlace); }); it('should set weight and method correctly', () => { - const visitedPlace = new BaseVisitedPlace(baseVisitedPlaceAttributes); + const visitedPlace = new BaseVisitedPlace({ ...baseVisitedPlaceAttributes, basePlace: new BasePlace(basePlaceAttributes) }); const weight: Weight = visitedPlace._weights?.[0] as Weight; expect(weight.weight).toBe(.9911); expect(weight.method).toBeInstanceOf(WeightMethod); @@ -168,8 +166,8 @@ describe('BaseVisitedPlace', () => { const invalidParams = { _uuid: 'foo', // Invalid UUID //basePlace: new BasePlace({} as BasePlaceAttributes), - arrivalDate: '2023-01-15', // Invalid date string - departureDate: '2023-01-16', // Invalid date string + arrivalDate: {}, // Invalid date string + departureDate: [], // Invalid date string arrivalTime: -1, // Negative arrival time departureTime: '12:00 PM', // Invalid departure time string activityCategory: 44, // Invalid activity category @@ -217,4 +215,10 @@ describe('BaseVisitedPlace', () => { expect(invalidResult[0]).toBeInstanceOf(Error); expect(validResult).toBeInstanceOf(BaseVisitedPlace); }); + + it('should unserialize object', () => { + const instance = BaseVisitedPlace.unserialize({ ...baseVisitedPlaceAttributes, basePlace: BasePlace.unserialize(basePlaceAttributes) }); + expect(instance).toBeInstanceOf(BaseVisitedPlace); + expect(instance.activity).toEqual(baseVisitedPlaceAttributes.activity); + }); }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/Sample.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/Sample.test.ts index c3d87b9b..ab602edc 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/Sample.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/Sample.test.ts @@ -67,9 +67,8 @@ describe('Sample Class', () => { // Missing required attributes: const missingRequired = Sample.validateParams({}); - expect(missingRequired.length).toEqual(2); - expect(missingRequired[0].message).toEqual('Sample validateParams: name is required'); - expect(missingRequired[1].message).toEqual('Sample validateParams: shortname is required'); + expect(missingRequired.length).toEqual(1); + expect(missingRequired[0].message).toEqual('Sample validateParams: shortname is required'); }); }); diff --git a/packages/evolution-common/src/services/baseObjects/__tests__/Survey.test.ts b/packages/evolution-common/src/services/baseObjects/__tests__/Survey.test.ts index faef9a8d..2399e050 100644 --- a/packages/evolution-common/src/services/baseObjects/__tests__/Survey.test.ts +++ b/packages/evolution-common/src/services/baseObjects/__tests__/Survey.test.ts @@ -67,11 +67,8 @@ describe('Survey Class', () => { // Missing required attributes: const missingRequired = Survey.validateParams({ }); - expect(missingRequired.length).toEqual(4); - expect(missingRequired[0].message).toEqual('Survey validateParams: name is required'); - expect(missingRequired[1].message).toEqual('Survey validateParams: shortname is required'); - expect(missingRequired[2].message).toEqual('Survey validateParams: startDate is required'); - expect(missingRequired[3].message).toEqual('Survey validateParams: endDate is required'); + expect(missingRequired.length).toEqual(1); + expect(missingRequired[0].message).toEqual('Survey validateParams: shortname is required'); }); }); diff --git a/packages/evolution-common/src/services/baseObjects/_template.ts b/packages/evolution-common/src/services/baseObjects/_template.ts deleted file mode 100644 index fec7c4c4..00000000 --- a/packages/evolution-common/src/services/baseObjects/_template.ts +++ /dev/null @@ -1,45 +0,0 @@ -/* - * Copyright 2023, Polytechnique Montreal and contributors - * - * This file is licensed under the MIT License. - * License text available at https://opensource.org/licenses/MIT - */ - -import { OptionalValidity, IValidatable } from './Validatable'; -import { Weightable, Weight } from './Weight'; -import { Uuidable } from './Uuidable'; - -type BaseCLASSAttributes = { - _uuid?: string; - - // custom attributes -} & Weightable; - -type ExtendedCLASSAttributes = BaseCLASSAttributes & { [key: string]: any }; - -// eslint-disable-next-line @typescript-eslint/no-empty-interface -interface IBaseCLASSAttributes extends BaseCLASSAttributes {} - -class BaseCLASS extends Uuidable implements IBaseCLASSAttributes, IValidatable { - _isValid: OptionalValidity; - _weights?: Weight[]; - - constructor(params: BaseCLASSAttributes | ExtendedCLASSAttributes) { - super(params._uuid); - - this._isValid = undefined; - this._weights = params._weights; - } - - validate(): OptionalValidity { - // TODO: implement: - this._isValid = true; - return true; - } - - isValid(): OptionalValidity { - return this._isValid; - } -} - -export { BaseCLASS, BaseCLASSAttributes, ExtendedCLASSAttributes }; diff --git a/packages/evolution-common/src/utils/DateUtils.ts b/packages/evolution-common/src/utils/DateUtils.ts new file mode 100644 index 00000000..8b3e364c --- /dev/null +++ b/packages/evolution-common/src/utils/DateUtils.ts @@ -0,0 +1,24 @@ +/* + * Copyright 2024, Polytechnique Montreal and contributors + * + * This file is licensed under the MIT License. + * License text available at https://opensource.org/licenses/MIT + */ +/** + * This file provides a router with the routes to validate interviews + * */ + +// can return invalid dates: use isNaN on date.getTime() to check if it's valid +export const parseDate = function (date: string | Date | undefined, showErrorOnValidate = false): Date | undefined { + if (date === undefined) { + return undefined; + } + if (date instanceof Date) { + return date; + } + const parsedDate = new Date(date); + if (showErrorOnValidate && isNaN(parsedDate.getDate())) { + console.error('DateUtils parseDate: invalid date'); + } + return parsedDate; +}; From 9d874cbfd8f94b49c5482f1694c91b3f122a884e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pierre-L=C3=A9o=20Bourbonnais?= Date: Fri, 9 Feb 2024 14:32:53 -0500 Subject: [PATCH 2/2] add tests to DateUtils --- .../evolution-common/src/utils/DateUtils.ts | 4 +- .../src/utils/__tests__/DateUtils.test.ts | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 packages/evolution-common/src/utils/__tests__/DateUtils.test.ts diff --git a/packages/evolution-common/src/utils/DateUtils.ts b/packages/evolution-common/src/utils/DateUtils.ts index 8b3e364c..5534149e 100644 --- a/packages/evolution-common/src/utils/DateUtils.ts +++ b/packages/evolution-common/src/utils/DateUtils.ts @@ -9,8 +9,8 @@ * */ // can return invalid dates: use isNaN on date.getTime() to check if it's valid -export const parseDate = function (date: string | Date | undefined, showErrorOnValidate = false): Date | undefined { - if (date === undefined) { +export const parseDate = function (date: string | Date | undefined | null, showErrorOnValidate = false): Date | undefined { + if (date === undefined || date === '' || date === null) { return undefined; } if (date instanceof Date) { diff --git a/packages/evolution-common/src/utils/__tests__/DateUtils.test.ts b/packages/evolution-common/src/utils/__tests__/DateUtils.test.ts new file mode 100644 index 00000000..df6559a4 --- /dev/null +++ b/packages/evolution-common/src/utils/__tests__/DateUtils.test.ts @@ -0,0 +1,44 @@ +/* + * Copyright 2024, Polytechnique Montreal and contributors + * + * This file is licensed under the MIT License. + * License text available at https://opensource.org/licenses/MIT + */ + +import { parseDate } from '../DateUtils'; + +describe('parseDate function', () => { + // Test cases for undefined input and Date object input + test.each([ + [undefined, undefined, 'returns undefined for undefined input'], + ['', undefined, 'returns undefined for empty string input'], + [null, undefined, 'returns undefined for null input'], + [new Date('2020-01-01'), new Date('2020-01-01'), 'returns the same Date object for Date input'], + ])('%s: %s', (input, expected, description) => { + expect(parseDate(input)).toEqual(expected); + }); + + // Test cases for string input + test.each([ + ['2020-01-01', true, 'returns a valid Date object for valid date string input'], + ['invalid-date', false, 'returns an invalid Date object for invalid date string input'], + ])('%s: %s', (input, isValid, description) => { + const result = parseDate(input); + expect(result instanceof Date).toBeTruthy(); + expect(isValid ? !isNaN((result as Date).getTime()) : isNaN((result as Date).getTime())).toBeTruthy(); + }); + + // Test case for logging error on invalid date string input + test.each([ + ['invalid-date', true, 'logs an error for invalid date string input when showErrorOnValidate is true'], + ])('%s with showErrorOnValidate=%s: %s', (input, showErrorOnValidate, description) => { + const consoleSpy = jest.spyOn(console, 'error').mockImplementation(); + parseDate(input, showErrorOnValidate); + if (showErrorOnValidate) { + expect(consoleSpy).toHaveBeenCalledWith('DateUtils parseDate: invalid date'); + } else { + expect(consoleSpy).not.toHaveBeenCalled(); + } + consoleSpy.mockRestore(); + }); +});