test: stub prisma modelType delegate

This commit is contained in:
Matthieu
2025-09-26 08:22:01 +02:00
parent b10a6baf47
commit 1727526e89

View File

@@ -111,6 +111,17 @@ type PieceRecord = {
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;
@@ -152,6 +163,7 @@ class InMemoryPrismaService {
private sites: SiteRecord[] = [];
private typeComposants: TypeComposantRecord[] = [];
private typePieces: TypePieceRecord[] = [];
private modelTypes: ModelTypeRecord[] = [];
private modelTypeCodeCounter = 0;
private typeMachines: TypeMachineRecord[] = [];
private typeMachineComponentRequirements: TypeMachineComponentRequirementRecord[] =
@@ -178,6 +190,7 @@ class InMemoryPrismaService {
this.sites = [];
this.typeComposants = [];
this.typePieces = [];
this.modelTypes = [];
this.modelTypeCodeCounter = 0;
this.typeMachines = [];
this.typeMachineComponentRequirements = [];
@@ -190,6 +203,131 @@ class InMemoryPrismaService {
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();
@@ -604,6 +742,26 @@ class InMemoryPrismaService {
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) => {
@@ -766,6 +924,183 @@ class InMemoryPrismaService {
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?.models) {
base.models = [];
}
if (include?.pieceModels) {
base.pieceModels = [];
}
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) {