1647 lines
48 KiB
TypeScript
1647 lines
48 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>;
|
|
siteId: string;
|
|
typeMachineId: Nullable<string>;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
};
|
|
|
|
type ComposantRecord = {
|
|
id: string;
|
|
name: string;
|
|
reference: Nullable<string>;
|
|
prix: Nullable<string>;
|
|
machineId: Nullable<string>;
|
|
parentComposantId: Nullable<string>;
|
|
typeComposantId: Nullable<string>;
|
|
typeMachineComponentRequirementId: Nullable<string>;
|
|
constructeurId: Nullable<string>;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
};
|
|
|
|
type PieceRecord = {
|
|
id: string;
|
|
name: string;
|
|
reference: Nullable<string>;
|
|
prix: Nullable<string>;
|
|
machineId: Nullable<string>;
|
|
composantId: Nullable<string>;
|
|
typePieceId: Nullable<string>;
|
|
typeMachinePieceRequirementId: Nullable<string>;
|
|
constructeurId: Nullable<string>;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
};
|
|
|
|
type ModelTypeRecord = {
|
|
id: string;
|
|
name: string;
|
|
code: string;
|
|
category: 'COMPONENT' | 'PIECE';
|
|
description: Nullable<string>;
|
|
notes: Nullable<string>;
|
|
createdAt: Date;
|
|
updatedAt: Date;
|
|
};
|
|
|
|
type CustomFieldRecord = {
|
|
id: string;
|
|
name: string;
|
|
type: string;
|
|
required: boolean;
|
|
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 modelTypes: ModelTypeRecord[] = [];
|
|
private modelTypeCodeCounter = 0;
|
|
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);
|
|
}
|
|
|
|
reset() {
|
|
this.sites = [];
|
|
this.typeComposants = [];
|
|
this.typePieces = [];
|
|
this.modelTypes = [];
|
|
this.modelTypeCodeCounter = 0;
|
|
this.typeMachines = [];
|
|
this.typeMachineComponentRequirements = [];
|
|
this.typeMachinePieceRequirements = [];
|
|
this.machines = [];
|
|
this.composants = [];
|
|
this.pieces = [];
|
|
this.customFields = [];
|
|
this.customFieldValues = [];
|
|
this.profiles = [];
|
|
}
|
|
|
|
private readonly modelTypeDelegate = {
|
|
create: async ({ data, include }: any) => {
|
|
const now = new Date();
|
|
const category: 'COMPONENT' | 'PIECE' = data.category ?? 'COMPONENT';
|
|
const record: ModelTypeRecord = {
|
|
id: generateId('model_type'),
|
|
name: data.name,
|
|
code: data.code ?? this.generateModelTypeCode(data.name),
|
|
category,
|
|
description: data.description ?? null,
|
|
notes: data.notes ?? null,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
};
|
|
this.modelTypes.push(record);
|
|
|
|
if (data.customFields?.create) {
|
|
for (const field of data.customFields.create) {
|
|
this.createCustomFieldForModel(record.id, 'COMPONENT', field);
|
|
}
|
|
}
|
|
|
|
if (data.pieceCustomFields?.create) {
|
|
for (const field of data.pieceCustomFields.create) {
|
|
this.createCustomFieldForModel(record.id, 'PIECE', field);
|
|
}
|
|
}
|
|
|
|
this.syncModelType(record);
|
|
const resolvedInclude = typeof include === 'object' ? include : {};
|
|
return this.buildModelType(record, resolvedInclude);
|
|
},
|
|
|
|
findMany: async ({ where, include, orderBy }: any = {}) => {
|
|
let records = [...this.modelTypes];
|
|
if (where) {
|
|
records = records.filter((item) =>
|
|
this.matchesModelTypeWhere(item, where),
|
|
);
|
|
}
|
|
if (orderBy?.name) {
|
|
records.sort((a, b) =>
|
|
orderBy.name === 'desc'
|
|
? b.name.localeCompare(a.name)
|
|
: a.name.localeCompare(b.name),
|
|
);
|
|
}
|
|
const resolvedInclude = typeof include === 'object' ? include : {};
|
|
return records.map((record) =>
|
|
this.buildModelType(record, resolvedInclude),
|
|
);
|
|
},
|
|
|
|
findFirst: async ({ where, include }: any = {}) => {
|
|
const record = this.modelTypes.find((item) =>
|
|
this.matchesModelTypeWhere(item, where),
|
|
);
|
|
const resolvedInclude = typeof include === 'object' ? include : {};
|
|
return record ? this.buildModelType(record, resolvedInclude) : null;
|
|
},
|
|
|
|
findUnique: async ({ where, include }: any) => {
|
|
const record = this.modelTypes.find((item) => {
|
|
if (!where) return false;
|
|
if (where.id) {
|
|
return item.id === where.id;
|
|
}
|
|
if (where.code) {
|
|
return item.code === where.code;
|
|
}
|
|
return this.matchesModelTypeWhere(item, where);
|
|
});
|
|
const resolvedInclude = typeof include === 'object' ? include : {};
|
|
return record ? this.buildModelType(record, resolvedInclude) : null;
|
|
},
|
|
|
|
update: async ({ where, data, include }: any) => {
|
|
const record = this.modelTypes.find((item) =>
|
|
this.matchesModelTypeWhere(item, where),
|
|
);
|
|
if (!record) {
|
|
throw new Error('ModelType not found');
|
|
}
|
|
|
|
if (data.name !== undefined) {
|
|
record.name = this.applyUpdateValue(data.name);
|
|
}
|
|
if (data.description !== undefined) {
|
|
record.description = this.applyUpdateValue(data.description);
|
|
}
|
|
if (data.notes !== undefined) {
|
|
record.notes = this.applyUpdateValue(data.notes);
|
|
}
|
|
if (data.code !== undefined) {
|
|
record.code = this.applyUpdateValue(data.code);
|
|
}
|
|
|
|
record.updatedAt = new Date();
|
|
this.syncModelType(record);
|
|
const resolvedInclude = typeof include === 'object' ? include : {};
|
|
return this.buildModelType(record, resolvedInclude);
|
|
},
|
|
|
|
delete: async ({ where }: any) => {
|
|
const index = this.modelTypes.findIndex((item) =>
|
|
this.matchesModelTypeWhere(item, where),
|
|
);
|
|
if (index === -1) {
|
|
throw new Error('ModelType not found');
|
|
}
|
|
const [record] = this.modelTypes.splice(index, 1);
|
|
this.removeModelType(record);
|
|
this.customFields = this.customFields.filter(
|
|
(field) =>
|
|
field.typeComposantId !== record.id &&
|
|
field.typePieceId !== record.id,
|
|
);
|
|
return { ...record };
|
|
},
|
|
};
|
|
|
|
get modelType() {
|
|
return this.modelTypeDelegate;
|
|
}
|
|
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
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,
|
|
machineId: data.machineId ?? null,
|
|
parentComposantId: data.parentComposantId ?? null,
|
|
typeComposantId: data.typeComposantId ?? 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,
|
|
machineId: data.machineId ?? null,
|
|
composantId: data.composantId ?? null,
|
|
typePieceId: data.typePieceId ?? 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,
|
|
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 };
|
|
},
|
|
createMany: async ({ data }: any) => {
|
|
const now = new Date();
|
|
const records = data.map((item: any) => {
|
|
const record: CustomFieldRecord = {
|
|
id: generateId('cf'),
|
|
name: item.name,
|
|
type: item.type,
|
|
required: item.required ?? false,
|
|
options: item.options ?? [],
|
|
typeMachineId: item.typeMachineId ?? null,
|
|
typeComposantId: item.typeComposantId ?? null,
|
|
typePieceId: item.typePieceId ?? null,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
};
|
|
this.customFields.push(record);
|
|
return record;
|
|
});
|
|
return { count: records.length };
|
|
},
|
|
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 matchesModelTypeWhere(record: ModelTypeRecord, where?: any) {
|
|
if (!where) {
|
|
return true;
|
|
}
|
|
if (where.AND && Array.isArray(where.AND)) {
|
|
return where.AND.every((clause: any) =>
|
|
this.matchesModelTypeWhere(record, clause),
|
|
);
|
|
}
|
|
if (where.OR && Array.isArray(where.OR)) {
|
|
return where.OR.some((clause: any) =>
|
|
this.matchesModelTypeWhere(record, clause),
|
|
);
|
|
}
|
|
if (where.NOT && Array.isArray(where.NOT)) {
|
|
return where.NOT.every(
|
|
(clause: any) => !this.matchesModelTypeWhere(record, clause),
|
|
);
|
|
}
|
|
return Object.entries(where).every(([key, value]) => {
|
|
if (value === undefined) {
|
|
return true;
|
|
}
|
|
if (
|
|
key === 'id' ||
|
|
key === 'code' ||
|
|
key === 'category' ||
|
|
key === 'name'
|
|
) {
|
|
return (record as any)[key] === value;
|
|
}
|
|
return true;
|
|
});
|
|
}
|
|
|
|
private createCustomFieldForModel(
|
|
modelTypeId: string,
|
|
category: 'COMPONENT' | 'PIECE',
|
|
field: any,
|
|
) {
|
|
const now = new Date();
|
|
const record: CustomFieldRecord = {
|
|
id: generateId('cf'),
|
|
name: field.name,
|
|
type: field.type,
|
|
required: field.required ?? false,
|
|
options: field.options ?? [],
|
|
typeMachineId: null,
|
|
typeComposantId: category === 'COMPONENT' ? modelTypeId : null,
|
|
typePieceId: category === 'PIECE' ? modelTypeId : null,
|
|
createdAt: now,
|
|
updatedAt: now,
|
|
};
|
|
this.customFields.push(record);
|
|
return record;
|
|
}
|
|
|
|
private syncModelType(record: ModelTypeRecord) {
|
|
const base = {
|
|
id: record.id,
|
|
name: record.name,
|
|
description: record.description ?? null,
|
|
createdAt: record.createdAt,
|
|
updatedAt: record.updatedAt,
|
|
};
|
|
|
|
if (record.category === 'COMPONENT') {
|
|
const existing = this.typeComposants.find(
|
|
(item) => item.id === record.id,
|
|
);
|
|
if (existing) {
|
|
existing.name = base.name;
|
|
existing.description = base.description;
|
|
existing.updatedAt = record.updatedAt;
|
|
} else {
|
|
this.typeComposants.push({ ...base });
|
|
}
|
|
} else {
|
|
const existing = this.typePieces.find((item) => item.id === record.id);
|
|
if (existing) {
|
|
existing.name = base.name;
|
|
existing.description = base.description;
|
|
existing.updatedAt = record.updatedAt;
|
|
} else {
|
|
this.typePieces.push({ ...base });
|
|
}
|
|
}
|
|
}
|
|
|
|
private removeModelType(record: ModelTypeRecord) {
|
|
if (record.category === 'COMPONENT') {
|
|
this.typeComposants = this.typeComposants.filter(
|
|
(item) => item.id !== record.id,
|
|
);
|
|
} else {
|
|
this.typePieces = this.typePieces.filter((item) => item.id !== record.id);
|
|
}
|
|
}
|
|
|
|
private generateModelTypeCode(name: string) {
|
|
const base =
|
|
(name || 'type')
|
|
.toLowerCase()
|
|
.normalize('NFD')
|
|
.replace(/[^a-z0-9]+/g, '-')
|
|
.replace(/^-+|-+$/g, '') || 'type';
|
|
let candidate = base;
|
|
let suffix = 1;
|
|
while (this.modelTypes.some((item) => item.code === candidate)) {
|
|
candidate = `${base}-${suffix++}`;
|
|
}
|
|
return candidate;
|
|
}
|
|
|
|
private applyUpdateValue<T>(value: any): T {
|
|
if (value && typeof value === 'object' && 'set' in value) {
|
|
return value.set as T;
|
|
}
|
|
return value as T;
|
|
}
|
|
|
|
private buildModelType(record: ModelTypeRecord, include: any) {
|
|
const base: any = { ...record };
|
|
|
|
const customFieldsInclude = include?.customFields;
|
|
if (customFieldsInclude) {
|
|
const select =
|
|
typeof customFieldsInclude === 'object'
|
|
? customFieldsInclude.select
|
|
: undefined;
|
|
base.customFields = this.customFields
|
|
.filter((field) => field.typeComposantId === record.id)
|
|
.map((field) => this.applySelect(field, select));
|
|
}
|
|
|
|
const pieceCustomFieldsInclude = include?.pieceCustomFields;
|
|
if (pieceCustomFieldsInclude) {
|
|
const select =
|
|
typeof pieceCustomFieldsInclude === 'object'
|
|
? pieceCustomFieldsInclude.select
|
|
: undefined;
|
|
base.pieceCustomFields = this.customFields
|
|
.filter((field) => field.typePieceId === record.id)
|
|
.map((field) => this.applySelect(field, select));
|
|
}
|
|
|
|
if (include?.composants) {
|
|
base.composants = this.composants
|
|
.filter((item) => item.typeComposantId === record.id)
|
|
.map((item) => ({ ...item }));
|
|
}
|
|
|
|
if (include?.pieceRequirements) {
|
|
base.pieceRequirements = [];
|
|
}
|
|
|
|
if (include?.pieces) {
|
|
base.pieces = this.pieces
|
|
.filter((item) => item.typePieceId === record.id)
|
|
.map((item) => ({ ...item }));
|
|
}
|
|
|
|
if (include?.componentRequirements) {
|
|
base.componentRequirements = [];
|
|
}
|
|
|
|
return base;
|
|
}
|
|
|
|
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?.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?.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;
|
|
let prisma: 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 = prismaStub;
|
|
});
|
|
|
|
afterAll(async () => {
|
|
await app.close();
|
|
});
|
|
|
|
beforeEach(() => {
|
|
prisma.reset();
|
|
});
|
|
|
|
afterEach(() => {
|
|
jest.restoreAllMocks();
|
|
});
|
|
|
|
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,
|
|
},
|
|
],
|
|
});
|
|
|
|
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',
|
|
},
|
|
],
|
|
});
|
|
|
|
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,
|
|
value: '7 kW',
|
|
},
|
|
],
|
|
},
|
|
},
|
|
],
|
|
pieceSelections: [
|
|
{
|
|
requirementId: pieceRequirementId,
|
|
definition: {
|
|
name: 'Kit maintenance niveau 1',
|
|
reference: 'KIT-001',
|
|
customFields: [
|
|
{
|
|
name: 'Référence fournisseur',
|
|
type: 'text',
|
|
value: '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 () => {
|
|
jest.spyOn(prisma.machine, 'findUnique').mockResolvedValue({
|
|
id: 'machine-1',
|
|
typeMachine: {
|
|
componentRequirements: [
|
|
{ id: 'req-1', typeComposantId: 'type-comp-1' },
|
|
],
|
|
},
|
|
} as any);
|
|
|
|
const created = { id: 'component-1' };
|
|
const createSpy = jest
|
|
.spyOn(prisma.composant, 'create')
|
|
.mockResolvedValue(created as any);
|
|
|
|
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(createSpy).toHaveBeenCalled();
|
|
});
|
|
|
|
it('refuses creation when requirement is not part of the machine skeleton', async () => {
|
|
jest.spyOn(prisma.machine, 'findUnique').mockResolvedValue({
|
|
id: 'machine-1',
|
|
typeMachine: {
|
|
componentRequirements: [
|
|
{ id: 'req-1', typeComposantId: 'type-comp-1' },
|
|
],
|
|
},
|
|
} as any);
|
|
|
|
const createSpy = jest.spyOn(prisma.composant, 'create');
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/composants')
|
|
.send({
|
|
name: 'Comp A',
|
|
machineId: 'machine-1',
|
|
typeComposantId: 'type-comp-1',
|
|
typeMachineComponentRequirementId: 'req-2',
|
|
})
|
|
.expect(400);
|
|
|
|
expect(createSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
|
|
describe('POST /pieces', () => {
|
|
it('accepts creation when requirement matches the machine skeleton', async () => {
|
|
jest.spyOn(prisma.machine, 'findUnique').mockResolvedValue({
|
|
id: 'machine-1',
|
|
typeMachine: {
|
|
pieceRequirements: [{ id: 'req-1', typePieceId: 'type-piece-1' }],
|
|
},
|
|
} as any);
|
|
|
|
const created = { id: 'piece-1' };
|
|
const createSpy = jest
|
|
.spyOn(prisma.piece, 'create')
|
|
.mockResolvedValue(created as any);
|
|
|
|
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(createSpy).toHaveBeenCalled();
|
|
});
|
|
|
|
it('refuses creation when requirement is not part of the machine skeleton', async () => {
|
|
jest.spyOn(prisma.machine, 'findUnique').mockResolvedValue({
|
|
id: 'machine-1',
|
|
typeMachine: {
|
|
pieceRequirements: [{ id: 'req-1', typePieceId: 'type-piece-1' }],
|
|
},
|
|
} as any);
|
|
|
|
const createSpy = jest.spyOn(prisma.piece, 'create');
|
|
|
|
await request(app.getHttpServer())
|
|
.post('/pieces')
|
|
.send({
|
|
name: 'Piece A',
|
|
machineId: 'machine-1',
|
|
typePieceId: 'type-piece-1',
|
|
typeMachinePieceRequirementId: 'req-2',
|
|
})
|
|
.expect(400);
|
|
|
|
expect(createSpy).not.toHaveBeenCalled();
|
|
});
|
|
});
|
|
});
|