This repository has been archived on 2026-04-01. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Inventory_backend/test/app.e2e-spec.ts

1196 lines
38 KiB
TypeScript

import { INestApplication } from '@nestjs/common';
import { Test, TestingModule } from '@nestjs/testing';
import * as request from 'supertest';
import { AppModule } from './../src/app.module';
import { PrismaService } from '../src/prisma/prisma.service';
type Nullable<T> = T | null;
type SiteRecord = {
id: string;
name: string;
contactName: string;
contactPhone: string;
contactAddress: string;
contactPostalCode: string;
contactCity: string;
createdAt: Date;
updatedAt: Date;
};
type TypeComposantRecord = {
id: string;
name: string;
description: Nullable<string>;
createdAt: Date;
updatedAt: Date;
};
type TypePieceRecord = {
id: string;
name: string;
description: Nullable<string>;
createdAt: Date;
updatedAt: Date;
};
type TypeMachineRecord = {
id: string;
name: string;
description: Nullable<string>;
category: Nullable<string>;
maintenanceFrequency: Nullable<string>;
components: any;
machinePieces: any;
specifications: any;
createdAt: Date;
updatedAt: Date;
};
type TypeMachineComponentRequirementRecord = {
id: string;
typeMachineId: string;
typeComposantId: string;
label: Nullable<string>;
minCount: number;
maxCount: Nullable<number>;
required: boolean;
allowNewModels: boolean;
};
type TypeMachinePieceRequirementRecord = {
id: string;
typeMachineId: string;
typePieceId: string;
label: Nullable<string>;
minCount: number;
maxCount: Nullable<number>;
required: boolean;
allowNewModels: boolean;
};
type MachineRecord = {
id: string;
name: string;
reference: Nullable<string>;
constructeurId: Nullable<string>;
prix: Nullable<string>;
emplacement: Nullable<string>;
siteId: string;
typeMachineId: Nullable<string>;
createdAt: Date;
updatedAt: Date;
};
type ComposantRecord = {
id: string;
name: string;
reference: Nullable<string>;
prix: Nullable<string>;
emplacement: Nullable<string>;
machineId: Nullable<string>;
parentComposantId: Nullable<string>;
typeComposantId: Nullable<string>;
composantModelId: Nullable<string>;
typeMachineComponentRequirementId: Nullable<string>;
constructeurId: Nullable<string>;
createdAt: Date;
updatedAt: Date;
};
type PieceRecord = {
id: string;
name: string;
reference: Nullable<string>;
prix: Nullable<string>;
emplacement: Nullable<string>;
machineId: Nullable<string>;
composantId: Nullable<string>;
typePieceId: Nullable<string>;
pieceModelId: Nullable<string>;
typeMachinePieceRequirementId: Nullable<string>;
constructeurId: Nullable<string>;
createdAt: Date;
updatedAt: Date;
};
type CustomFieldRecord = {
id: string;
name: string;
type: string;
required: boolean;
defaultValue: Nullable<string>;
options: string[];
typeMachineId: Nullable<string>;
typeComposantId: Nullable<string>;
typePieceId: Nullable<string>;
createdAt: Date;
updatedAt: Date;
};
type CustomFieldValueRecord = {
id: string;
value: string;
customFieldId: string;
machineId: Nullable<string>;
composantId: Nullable<string>;
pieceId: Nullable<string>;
createdAt: Date;
updatedAt: Date;
};
type ProfileRecord = {
id: string;
firstName: string;
lastName: string;
isActive: boolean;
createdAt: Date;
updatedAt: Date;
};
function generateId(prefix: string): string {
return `${prefix}_${Math.random().toString(36).slice(2, 12)}`;
}
class InMemoryPrismaService {
private sites: SiteRecord[] = [];
private typeComposants: TypeComposantRecord[] = [];
private typePieces: TypePieceRecord[] = [];
private typeMachines: TypeMachineRecord[] = [];
private typeMachineComponentRequirements: TypeMachineComponentRequirementRecord[] = [];
private typeMachinePieceRequirements: TypeMachinePieceRequirementRecord[] = [];
private machines: MachineRecord[] = [];
private composants: ComposantRecord[] = [];
private pieces: PieceRecord[] = [];
private customFields: CustomFieldRecord[] = [];
private customFieldValues: CustomFieldValueRecord[] = [];
private profiles: ProfileRecord[] = [];
async onModuleInit() {}
async onModuleDestroy() {}
async $connect() {}
async $disconnect() {}
async $transaction<T>(fn: (tx: this) => Promise<T>): Promise<T> {
return fn(this);
}
site = {
create: async ({ data }: any) => {
const now = new Date();
const record: SiteRecord = {
id: generateId('site'),
name: data.name,
contactName: data.contactName,
contactPhone: data.contactPhone,
contactAddress: data.contactAddress,
contactPostalCode: data.contactPostalCode,
contactCity: data.contactCity,
createdAt: now,
updatedAt: now,
};
this.sites.push(record);
return { ...record };
},
findMany: async ({ include }: any = {}) => {
return this.sites.map((site) => this.buildSite(site, include));
},
findUnique: async ({ where, include }: any) => {
const site = this.sites.find((item) => item.id === where.id);
if (!site) {
return null;
}
return this.buildSite(site, include);
},
update: async ({ where, data }: any) => {
const site = this.sites.find((item) => item.id === where.id);
if (!site) {
throw new Error('Site not found');
}
Object.assign(site, data, { updatedAt: new Date() });
return { ...site };
},
delete: async ({ where }: any) => {
const index = this.sites.findIndex((item) => item.id === where.id);
if (index === -1) {
throw new Error('Site not found');
}
const [deleted] = this.sites.splice(index, 1);
return { ...deleted };
},
};
typeComposant = {
create: async ({ data, include }: any) => {
const now = new Date();
const record: TypeComposantRecord = {
id: generateId('type_comp'),
name: data.name,
description: data.description ?? null,
createdAt: now,
updatedAt: now,
};
this.typeComposants.push(record);
if (data.customFields?.create) {
for (const field of data.customFields.create) {
this.customFields.push({
id: generateId('cf'),
name: field.name,
type: field.type,
required: field.required ?? false,
defaultValue: field.defaultValue ?? null,
options: field.options ?? [],
typeMachineId: null,
typeComposantId: record.id,
typePieceId: null,
createdAt: now,
updatedAt: now,
});
}
}
return include?.customFields
? {
...record,
customFields: this.customFields.filter((field) => field.typeComposantId === record.id),
}
: { ...record };
},
findFirst: async ({ where }: any) => {
if (!where) {
return this.typeComposants[0] ?? null;
}
if (where.id) {
return this.typeComposants.find((item) => item.id === where.id) ?? null;
}
if (where.name) {
return this.typeComposants.find((item) => item.name === where.name) ?? null;
}
return null;
},
findMany: async () => {
return this.typeComposants.map((item) => ({ ...item }));
},
findUnique: async ({ where }: any) => {
return this.typeComposants.find((item) => item.id === where.id) ?? null;
},
};
typePiece = {
create: async ({ data, include }: any) => {
const now = new Date();
const record: TypePieceRecord = {
id: generateId('type_piece'),
name: data.name,
description: data.description ?? null,
createdAt: now,
updatedAt: now,
};
this.typePieces.push(record);
if (data.customFields?.create) {
for (const field of data.customFields.create) {
this.customFields.push({
id: generateId('cf'),
name: field.name,
type: field.type,
required: field.required ?? false,
defaultValue: field.defaultValue ?? null,
options: field.options ?? [],
typeMachineId: null,
typeComposantId: null,
typePieceId: record.id,
createdAt: now,
updatedAt: now,
});
}
}
return include?.customFields
? {
...record,
customFields: this.customFields.filter((field) => field.typePieceId === record.id),
}
: { ...record };
},
findFirst: async ({ where }: any) => {
if (!where) {
return this.typePieces[0] ?? null;
}
if (where.id) {
return this.typePieces.find((item) => item.id === where.id) ?? null;
}
if (where.name) {
return this.typePieces.find((item) => item.name === where.name) ?? null;
}
return null;
},
findUnique: async ({ where }: any) => {
return this.typePieces.find((item) => item.id === where.id) ?? null;
},
};
typeMachine = {
create: async ({ data, include }: any) => {
const now = new Date();
const record: TypeMachineRecord = {
id: generateId('type_machine'),
name: data.name,
description: data.description ?? null,
category: data.category ?? null,
maintenanceFrequency: data.maintenanceFrequency ?? null,
components: data.components ?? null,
machinePieces: data.machinePieces ?? null,
specifications: data.specifications ?? null,
createdAt: now,
updatedAt: now,
};
this.typeMachines.push(record);
if (data.customFields?.create) {
for (const field of data.customFields.create) {
this.customFields.push({
id: generateId('cf'),
name: field.name,
type: field.type,
required: field.required ?? false,
defaultValue: field.defaultValue ?? null,
options: field.options ?? [],
typeMachineId: record.id,
typeComposantId: null,
typePieceId: null,
createdAt: now,
updatedAt: now,
});
}
}
let componentRequirements: TypeMachineComponentRequirementRecord[] = [];
if (data.componentRequirements?.create) {
componentRequirements = data.componentRequirements.create.map((requirement) => {
const req: TypeMachineComponentRequirementRecord = {
id: generateId('tmc_req'),
typeMachineId: record.id,
typeComposantId: requirement.typeComposant.connect.id,
label: requirement.label ?? null,
minCount: requirement.minCount ?? 1,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? true,
allowNewModels: requirement.allowNewModels ?? true,
};
this.typeMachineComponentRequirements.push(req);
return req;
});
}
let pieceRequirements: TypeMachinePieceRequirementRecord[] = [];
if (data.pieceRequirements?.create) {
pieceRequirements = data.pieceRequirements.create.map((requirement) => {
const req: TypeMachinePieceRequirementRecord = {
id: generateId('tmp_req'),
typeMachineId: record.id,
typePieceId: requirement.typePiece.connect.id,
label: requirement.label ?? null,
minCount: requirement.minCount ?? 0,
maxCount: requirement.maxCount ?? null,
required: requirement.required ?? false,
allowNewModels: requirement.allowNewModels ?? true,
};
this.typeMachinePieceRequirements.push(req);
return req;
});
}
return this.buildTypeMachine(record, include ?? {}, componentRequirements, pieceRequirements);
},
findUnique: async ({ where, include }: any) => {
const typeMachine = this.typeMachines.find((item) => item.id === where.id);
if (!typeMachine) {
return null;
}
const componentRequirements = this.typeMachineComponentRequirements.filter(
(item) => item.typeMachineId === typeMachine.id,
);
const pieceRequirements = this.typeMachinePieceRequirements.filter(
(item) => item.typeMachineId === typeMachine.id,
);
return this.buildTypeMachine(typeMachine, include ?? {}, componentRequirements, pieceRequirements);
},
findMany: async ({ include }: any = {}) => {
return this.typeMachines.map((item) => {
const componentRequirements = this.typeMachineComponentRequirements.filter(
(req) => req.typeMachineId === item.id,
);
const pieceRequirements = this.typeMachinePieceRequirements.filter((req) => req.typeMachineId === item.id);
return this.buildTypeMachine(item, include ?? {}, componentRequirements, pieceRequirements);
});
},
};
machine = {
create: async ({ data, include }: any) => {
const now = new Date();
const record: MachineRecord = {
id: generateId('machine'),
name: data.name,
reference: data.reference ?? null,
constructeurId: data.constructeurId ?? null,
prix: data.prix ?? null,
emplacement: data.emplacement ?? null,
siteId: data.siteId,
typeMachineId: data.typeMachineId ?? null,
createdAt: now,
updatedAt: now,
};
this.machines.push(record);
return this.buildMachine(record, include ?? {});
},
findUnique: async ({ where, include }: any) => {
const machine = this.machines.find((item) => item.id === where.id);
if (!machine) {
return null;
}
return this.buildMachine(machine, include ?? {});
},
findMany: async ({ include, where }: any = {}) => {
let machines = this.machines;
if (where?.siteId) {
machines = machines.filter((machine) => machine.siteId === where.siteId);
}
return machines.map((machine) => this.buildMachine(machine, include ?? {}));
},
delete: async ({ where }: any) => {
const index = this.machines.findIndex((item) => item.id === where.id);
if (index === -1) {
throw new Error('Machine not found');
}
const [deleted] = this.machines.splice(index, 1);
return { ...deleted };
},
};
composant = {
create: async ({ data }: any) => {
const now = new Date();
const record: ComposantRecord = {
id: generateId('component'),
name: data.name,
reference: data.reference ?? null,
prix: data.prix ?? null,
emplacement: data.emplacement ?? null,
machineId: data.machineId ?? null,
parentComposantId: data.parentComposantId ?? null,
typeComposantId: data.typeComposantId ?? null,
composantModelId: data.composantModelId ?? null,
typeMachineComponentRequirementId: data.typeMachineComponentRequirementId ?? null,
constructeurId: data.constructeurId ?? null,
createdAt: now,
updatedAt: now,
};
this.composants.push(record);
return { ...record };
},
findMany: async ({ where }: any) => {
let composants = this.composants;
if (where?.machineId !== undefined) {
composants = composants.filter((item) => item.machineId === where.machineId);
}
if (where?.parentComposantId !== undefined) {
composants = composants.filter((item) => item.parentComposantId === where.parentComposantId);
}
return composants.map((item) => ({ ...item }));
},
};
piece = {
create: async ({ data }: any) => {
const now = new Date();
const record: PieceRecord = {
id: generateId('piece'),
name: data.name,
reference: data.reference ?? null,
prix: data.prix ?? null,
emplacement: data.emplacement ?? null,
machineId: data.machineId ?? null,
composantId: data.composantId ?? null,
typePieceId: data.typePieceId ?? null,
pieceModelId: data.pieceModelId ?? null,
typeMachinePieceRequirementId: data.typeMachinePieceRequirementId ?? null,
constructeurId: data.constructeurId ?? null,
createdAt: now,
updatedAt: now,
};
this.pieces.push(record);
return { ...record };
},
findMany: async ({ where }: any) => {
let pieces = this.pieces;
if (where?.machineId !== undefined) {
pieces = pieces.filter((item) => item.machineId === where.machineId);
}
if (where?.composantId !== undefined) {
pieces = pieces.filter((item) => item.composantId === where.composantId);
}
return pieces.map((item) => ({ ...item }));
},
};
customField = {
create: async ({ data }: any) => {
const now = new Date();
const record: CustomFieldRecord = {
id: generateId('cf'),
name: data.name,
type: data.type,
required: data.required ?? false,
defaultValue: data.defaultValue ?? null,
options: data.options ?? [],
typeMachineId: data.typeMachineId ?? null,
typeComposantId: data.typeComposantId ?? null,
typePieceId: data.typePieceId ?? null,
createdAt: now,
updatedAt: now,
};
this.customFields.push(record);
return { ...record };
},
findMany: async ({ where }: any) => {
return this.customFields
.filter((field) => {
if (!where) return true;
return Object.entries(where).every(([key, value]) => (field as any)[key] === value);
})
.map((field) => ({ ...field }));
},
deleteMany: async ({ where }: any) => {
const before = this.customFields.length;
this.customFields = this.customFields.filter((field) => {
return !Object.entries(where).every(([key, value]) => (field as any)[key] === value);
});
return { count: before - this.customFields.length };
},
};
customFieldValue = {
create: async ({ data, include }: any) => {
const now = new Date();
const record: CustomFieldValueRecord = {
id: generateId('cfv'),
value: data.value,
customFieldId: data.customFieldId,
machineId: data.machineId ?? null,
composantId: data.composantId ?? null,
pieceId: data.pieceId ?? null,
createdAt: now,
updatedAt: now,
};
this.customFieldValues.push(record);
return this.buildCustomFieldValue(record, include ?? {});
},
findMany: async ({ where, include }: any) => {
const records = this.customFieldValues.filter((value) => {
if (!where) return true;
return Object.entries(where).every(([key, v]) => (value as any)[key] === v);
});
return records.map((record) => this.buildCustomFieldValue(record, include ?? {}));
},
findUnique: async ({ where, include }: any) => {
const record = this.customFieldValues.find((value) => value.id === where.id);
if (!record) {
return null;
}
return this.buildCustomFieldValue(record, include ?? {});
},
findFirst: async ({ where }: any) => {
return (
this.customFieldValues.find((value) =>
Object.entries(where).every(([key, v]) => (value as any)[key] === v),
) ?? null
);
},
update: async ({ where, data, include }: any) => {
const record = this.customFieldValues.find((value) => value.id === where.id);
if (!record) {
throw new Error('Custom field value not found');
}
if (data.value !== undefined) {
record.value = data.value;
}
record.updatedAt = new Date();
return this.buildCustomFieldValue(record, include ?? {});
},
delete: async ({ where }: any) => {
const index = this.customFieldValues.findIndex((value) => value.id === where.id);
if (index === -1) {
throw new Error('Custom field value not found');
}
const [deleted] = this.customFieldValues.splice(index, 1);
return { ...deleted };
},
};
profile = {
count: async ({ where }: any) => {
return this.profiles.filter((profile) => {
if (!where) return true;
return Object.entries(where).every(([key, value]) => (profile as any)[key] === value);
}).length;
},
findMany: async ({ where, orderBy, select }: any) => {
let profiles = this.profiles;
if (where) {
profiles = profiles.filter((profile) =>
Object.entries(where).every(([key, value]) => (profile as any)[key] === value),
);
}
if (orderBy?.createdAt === 'asc') {
profiles = [...profiles].sort((a, b) => a.createdAt.getTime() - b.createdAt.getTime());
}
return profiles.map((profile) => this.applySelect(profile, select));
},
findFirst: async ({ where, select }: any) => {
const profile = this.profiles.find((item) =>
Object.entries(where).every(([key, value]) => (item as any)[key] === value),
);
return profile ? this.applySelect(profile, select) : null;
},
findUnique: async ({ where, select }: any) => {
const profile = this.profiles.find((item) => item.id === where.id);
return profile ? this.applySelect(profile, select) : null;
},
create: async ({ data, select }: any) => {
const now = new Date();
const record: ProfileRecord = {
id: generateId('profile'),
firstName: data.firstName,
lastName: data.lastName,
isActive: data.isActive ?? true,
createdAt: now,
updatedAt: now,
};
this.profiles.push(record);
return this.applySelect(record, select);
},
update: async ({ where, data, select }: any) => {
const profile = this.profiles.find((item) => item.id === where.id);
if (!profile) {
throw new Error('Profile not found');
}
Object.assign(profile, data, { updatedAt: new Date() });
return this.applySelect(profile, select);
},
};
private applySelect<T extends Record<string, any>>(record: T, select?: any) {
if (!select) {
return { ...record };
}
const result: any = {};
for (const [key, enabled] of Object.entries(select)) {
if (enabled) {
result[key] = record[key as keyof T];
}
}
return result;
}
private buildSite(site: SiteRecord, include: any) {
const base: any = { ...site };
if (include?.machines) {
const machines = this.machines.filter((machine) => machine.siteId === site.id);
base.machines = machines.map((machine) => this.buildMachine(machine, include.machines.include ?? {}));
}
if (include?.documents) {
base.documents = [];
}
return base;
}
private buildTypeMachine(
record: TypeMachineRecord,
include: any,
componentRequirements: TypeMachineComponentRequirementRecord[],
pieceRequirements: TypeMachinePieceRequirementRecord[],
) {
const base: any = { ...record };
if (include?.customFields) {
base.customFields = this.customFields.filter((field) => field.typeMachineId === record.id);
}
if (include?.componentRequirements) {
base.componentRequirements = componentRequirements.map((requirement) => ({
...requirement,
typeComposant: include.componentRequirements.include?.typeComposant
? this.typeComposants.find((item) => item.id === requirement.typeComposantId) ?? null
: undefined,
}));
}
if (include?.pieceRequirements) {
base.pieceRequirements = pieceRequirements.map((requirement) => ({
...requirement,
typePiece: include.pieceRequirements.include?.typePiece
? this.typePieces.find((item) => item.id === requirement.typePieceId) ?? null
: undefined,
}));
}
if (include?.machines) {
base.machines = this.machines
.filter((machine) => machine.typeMachineId === record.id)
.map((machine) => this.buildMachine(machine, include.machines.include ?? {}));
}
return base;
}
private buildMachine(machine: MachineRecord, include: any) {
const base: any = { ...machine };
if (include?.site) {
base.site = this.sites.find((site) => site.id === machine.siteId) ?? null;
}
if (include?.typeMachine) {
const typeMachine = machine.typeMachineId
? this.typeMachines.find((item) => item.id === machine.typeMachineId)
: null;
if (typeMachine) {
const componentRequirements = this.typeMachineComponentRequirements.filter(
(req) => req.typeMachineId === typeMachine.id,
);
const pieceRequirements = this.typeMachinePieceRequirements.filter(
(req) => req.typeMachineId === typeMachine.id,
);
base.typeMachine = this.buildTypeMachine(
typeMachine,
include.typeMachine.include ?? {},
componentRequirements,
pieceRequirements,
);
} else {
base.typeMachine = null;
}
}
if (include?.constructeur) {
base.constructeur = null;
}
if (include?.composants) {
const composants = this.composants.filter((component) => component.machineId === machine.id);
base.composants = composants
.filter((component) => component.parentComposantId === null)
.map((component) => this.buildComponent(component, include.composants.include ?? {}));
}
if (include?.pieces) {
const machinePieces = this.pieces.filter((piece) => piece.machineId === machine.id && piece.composantId === null);
base.pieces = machinePieces.map((piece) => this.buildPiece(piece, include.pieces.include ?? {}));
}
if (include?.customFieldValues) {
const values = this.customFieldValues.filter((value) => value.machineId === machine.id);
base.customFieldValues = values.map((value) => this.buildCustomFieldValue(value, include.customFieldValues.include ?? {}));
}
if (include?.documents) {
base.documents = [];
}
return base;
}
private buildComponent(component: ComposantRecord, include: any) {
const base: any = { ...component };
if (include?.typeComposant) {
base.typeComposant = component.typeComposantId
? this.typeComposants.find((item) => item.id === component.typeComposantId) ?? null
: null;
}
if (include?.composantModel) {
base.composantModel = null;
}
if (include?.typeMachineComponentRequirement) {
const requirement = component.typeMachineComponentRequirementId
? this.typeMachineComponentRequirements.find((item) => item.id === component.typeMachineComponentRequirementId) ?? null
: null;
base.typeMachineComponentRequirement = requirement
? {
...requirement,
typeComposant: include.typeMachineComponentRequirement.include?.typeComposant
? this.typeComposants.find((item) => item.id === requirement.typeComposantId) ?? null
: undefined,
}
: null;
}
if (include?.sousComposants) {
const children = this.composants.filter((item) => item.parentComposantId === component.id);
base.sousComposants = children.map((child) => this.buildComponent(child, include.sousComposants.include ?? {}));
}
if (include?.customFieldValues) {
const values = this.customFieldValues.filter((value) => value.composantId === component.id);
base.customFieldValues = values.map((value) => this.buildCustomFieldValue(value, include.customFieldValues.include ?? {}));
}
if (include?.constructeur) {
base.constructeur = null;
}
if (include?.pieces) {
const relatedPieces = this.pieces.filter((piece) => piece.composantId === component.id);
base.pieces = relatedPieces.map((piece) => this.buildPiece(piece, include.pieces.include ?? {}));
}
return base;
}
private buildPiece(piece: PieceRecord, include: any) {
const base: any = { ...piece };
if (include?.customFieldValues) {
const values = this.customFieldValues.filter((value) => value.pieceId === piece.id);
base.customFieldValues = values.map((value) => this.buildCustomFieldValue(value, include.customFieldValues.include ?? {}));
}
if (include?.constructeur) {
base.constructeur = null;
}
if (include?.pieceModel) {
base.pieceModel = null;
}
if (include?.typeMachinePieceRequirement) {
const requirement = piece.typeMachinePieceRequirementId
? this.typeMachinePieceRequirements.find((item) => item.id === piece.typeMachinePieceRequirementId) ?? null
: null;
base.typeMachinePieceRequirement = requirement
? {
...requirement,
typePiece: include.typeMachinePieceRequirement.include?.typePiece
? this.typePieces.find((item) => item.id === requirement.typePieceId) ?? null
: undefined,
}
: null;
}
if (include?.typePiece) {
base.typePiece = piece.typePieceId
? this.typePieces.find((item) => item.id === piece.typePieceId) ?? null
: null;
}
return base;
}
private buildCustomFieldValue(record: CustomFieldValueRecord, include: any) {
const base: any = { ...record };
if (include?.customField) {
base.customField = this.customFields.find((field) => field.id === record.customFieldId) ?? null;
}
return base;
}
}
describe('Inventory flow (e2e)', () => {
let app: INestApplication;
let prismaStub: InMemoryPrismaService;
beforeAll(async () => {
prismaStub = new InMemoryPrismaService();
const moduleFixture: TestingModule = await Test.createTestingModule({
imports: [AppModule],
})
.overrideProvider(PrismaService)
.useValue(prismaStub)
.compile();
app = moduleFixture.createNestApplication();
await app.init();
prisma = app.get(PrismaService);
});
afterAll(async () => {
await app.close();
});
beforeEach(async () => {
await prisma.$executeRawUnsafe(
'TRUNCATE TABLE custom_field_values, documents, pieces, composants, machines, composant_models, piece_models, type_machine_component_requirements, type_machine_piece_requirements, custom_fields, type_machines, type_composants, type_pieces, constructeurs, sites RESTART IDENTITY CASCADE',
);
});
afterEach(async () => {
await app.close();
});
afterAll(async () => {
await app.close();
});
it('should create a type, create a machine with new component/piece selections, and edit technical fields', async () => {
const siteResponse = await request(app.getHttpServer()).post('/sites').send({
name: 'Site Principal',
contactName: 'Responsable Maintenance',
contactPhone: '+33 1 23 45 67 89',
contactAddress: '1 rue de la Paix',
contactPostalCode: '75000',
contactCity: 'Paris',
});
expect(siteResponse.status).toBe(201);
const siteId = siteResponse.body.id;
expect(siteId).toBeDefined();
const typeComposantResponse = await request(app.getHttpServer())
.post('/types/composants')
.send({
name: 'Bloc moteur',
description: 'Sous-ensemble principal',
customFields: [
{
name: 'Puissance nominale',
type: 'text',
required: true,
defaultValue: '5 kW',
},
],
});
expect(typeComposantResponse.status).toBe(201);
const typeComposantId = typeComposantResponse.body.id;
expect(typeComposantId).toBeDefined();
const typePieceResponse = await request(app.getHttpServer())
.post('/types/pieces')
.send({
name: 'Kit maintenance',
description: 'Kit standard',
customFields: [
{
name: 'Référence fournisseur',
type: 'text',
defaultValue: 'STD-001',
},
],
});
expect(typePieceResponse.status).toBe(201);
const typePieceId = typePieceResponse.body.id;
expect(typePieceId).toBeDefined();
const typeMachineResponse = await request(app.getHttpServer())
.post('/types/machines')
.send({
name: 'Presse hydraulique',
description: 'Presse pour la ligne A',
componentRequirements: [
{
typeComposantId,
label: 'Bloc moteur obligatoire',
minCount: 1,
required: true,
allowNewModels: true,
},
],
pieceRequirements: [
{
typePieceId,
label: 'Kit de maintenance',
minCount: 1,
required: true,
allowNewModels: true,
},
],
});
expect(typeMachineResponse.status).toBe(201);
const typeMachine = typeMachineResponse.body;
const componentRequirementId = typeMachine.componentRequirements[0].id;
const pieceRequirementId = typeMachine.pieceRequirements[0].id;
const machineResponse = await request(app.getHttpServer())
.post('/machines')
.send({
name: 'Presse HP-2000',
siteId,
typeMachineId: typeMachine.id,
componentSelections: [
{
requirementId: componentRequirementId,
definition: {
name: 'Bloc moteur série X',
reference: 'COMP-001',
customFields: [
{
name: 'Puissance nominale',
type: 'text',
required: true,
defaultValue: '7 kW',
},
],
},
},
],
pieceSelections: [
{
requirementId: pieceRequirementId,
definition: {
name: 'Kit maintenance niveau 1',
reference: 'KIT-001',
customFields: [
{
name: 'Référence fournisseur',
type: 'text',
defaultValue: 'STD-002',
},
],
},
},
],
});
expect(machineResponse.status).toBe(201);
const machineId = machineResponse.body.id;
const machineDetailsResponse = await request(app.getHttpServer()).get(`/machines/${machineId}`);
expect(machineDetailsResponse.status).toBe(200);
const machine = machineDetailsResponse.body;
expect(machine.composants).toHaveLength(1);
expect(machine.pieces).toHaveLength(1);
const component = machine.composants[0];
expect(component.name).toBe('Bloc moteur série X');
expect(component.customFieldValues[0].value).toBe('7 kW');
const piece = machine.pieces[0];
expect(piece.name).toBe('Kit maintenance niveau 1');
expect(piece.customFieldValues[0].value).toBe('STD-002');
const customFieldValueId = component.customFieldValues[0].id;
const updateResponse = await request(app.getHttpServer())
.patch(`/custom-fields/values/${customFieldValueId}`)
.send({ value: '8 kW' });
expect(updateResponse.status).toBe(200);
expect(updateResponse.body.value).toBe('8 kW');
const refreshedMachineResponse = await request(app.getHttpServer()).get(`/machines/${machine.id}`);
expect(refreshedMachineResponse.status).toBe(200);
const refreshedComponent = refreshedMachineResponse.body.composants[0];
expect(refreshedComponent.customFieldValues[0].value).toBe('8 kW');
});
describe('POST /composants', () => {
it('accepts creation when requirement matches the machine skeleton', async () => {
prisma.machine.findUnique.mockResolvedValue({
id: 'machine-1',
typeMachine: {
componentRequirements: [
{ id: 'req-1', typeComposantId: 'type-comp-1' },
],
},
});
const created = { id: 'component-1' };
prisma.composant.create.mockResolvedValue(created);
const response = await request(app.getHttpServer())
.post('/composants')
.send({
name: 'Comp A',
machineId: 'machine-1',
typeComposantId: 'type-comp-1',
typeMachineComponentRequirementId: 'req-1',
})
.expect(201);
expect(response.body).toEqual(created);
expect(prisma.composant.create).toHaveBeenCalled();
});
it('refuses creation when requirement is not part of the machine skeleton', async () => {
prisma.machine.findUnique.mockResolvedValue({
id: 'machine-1',
typeMachine: {
componentRequirements: [
{ id: 'req-1', typeComposantId: 'type-comp-1' },
],
},
});
await request(app.getHttpServer())
.post('/composants')
.send({
name: 'Comp A',
machineId: 'machine-1',
typeComposantId: 'type-comp-1',
typeMachineComponentRequirementId: 'req-2',
})
.expect(400);
expect(prisma.composant.create).not.toHaveBeenCalled();
});
});
describe('POST /pieces', () => {
it('accepts creation when requirement matches the machine skeleton', async () => {
prisma.machine.findUnique.mockResolvedValue({
id: 'machine-1',
typeMachine: {
pieceRequirements: [
{ id: 'req-1', typePieceId: 'type-piece-1' },
],
},
});
const created = { id: 'piece-1' };
prisma.piece.create.mockResolvedValue(created);
const response = await request(app.getHttpServer())
.post('/pieces')
.send({
name: 'Piece A',
machineId: 'machine-1',
typePieceId: 'type-piece-1',
typeMachinePieceRequirementId: 'req-1',
})
.expect(201);
expect(response.body).toEqual(created);
expect(prisma.piece.create).toHaveBeenCalled();
});
it('refuses creation when requirement is not part of the machine skeleton', async () => {
prisma.machine.findUnique.mockResolvedValue({
id: 'machine-1',
typeMachine: {
pieceRequirements: [
{ id: 'req-1', typePieceId: 'type-piece-1' },
],
},
});
await request(app.getHttpServer())
.post('/pieces')
.send({
name: 'Piece A',
machineId: 'machine-1',
typePieceId: 'type-piece-1',
typeMachinePieceRequirementId: 'req-2',
})
.expect(400);
expect(prisma.piece.create).not.toHaveBeenCalled();
});
});
});