feat(db): add test fixtures script
This commit is contained in:
@@ -10,7 +10,8 @@
|
|||||||
"prisma:generate": "prisma generate",
|
"prisma:generate": "prisma generate",
|
||||||
"prisma:migrate": "prisma migrate dev",
|
"prisma:migrate": "prisma migrate dev",
|
||||||
"prisma:studio": "prisma studio",
|
"prisma:studio": "prisma studio",
|
||||||
"seed": "ts-node prisma/seed.ts"
|
"seed": "ts-node prisma/seed.ts",
|
||||||
|
"fixtures": "ts-node prisma/fixtures.ts"
|
||||||
},
|
},
|
||||||
"keywords": [],
|
"keywords": [],
|
||||||
"author": "",
|
"author": "",
|
||||||
|
|||||||
@@ -0,0 +1,479 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import { PrismaClient } from '@prisma/client';
|
||||||
|
import { PrismaMssql } from '@prisma/adapter-mssql';
|
||||||
|
|
||||||
|
// Fixtures de DEV/TEST. À lancer après `npm run seed` (dépend des données de référence).
|
||||||
|
// Non destructif : si déjà chargées (email témoin présent), le script s'arrête.
|
||||||
|
|
||||||
|
function getEnv(name: string): string {
|
||||||
|
const value = process.env[name];
|
||||||
|
|
||||||
|
if (!value) {
|
||||||
|
throw new Error(`Variable d'environnement manquante: ${name}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
function required<T>(value: T | null, message: string): T {
|
||||||
|
if (value === null) {
|
||||||
|
throw new Error(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
const adapter = new PrismaMssql({
|
||||||
|
server: getEnv('DB_SERVER'),
|
||||||
|
port: Number(process.env.DB_PORT ?? 1433),
|
||||||
|
database: getEnv('DB_NAME'),
|
||||||
|
user: getEnv('DB_USER'),
|
||||||
|
password: getEnv('DB_PASSWORD'),
|
||||||
|
options: {
|
||||||
|
encrypt: true,
|
||||||
|
trustServerCertificate: true,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const prisma = new PrismaClient({ adapter });
|
||||||
|
|
||||||
|
const WITNESS_EMAIL = 'marie.dupont@ensitech.eu';
|
||||||
|
|
||||||
|
const now = new Date();
|
||||||
|
function daysAgo(n: number): Date {
|
||||||
|
const d = new Date(now);
|
||||||
|
d.setDate(d.getDate() - n);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
function daysFromNow(n: number): Date {
|
||||||
|
const d = new Date(now);
|
||||||
|
d.setDate(d.getDate() + n);
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function main(): Promise<void> {
|
||||||
|
// Idempotence : on ne recharge pas si déjà présent.
|
||||||
|
const witness = await prisma.utilisateur.findUnique({ where: { email: WITNESS_EMAIL } });
|
||||||
|
if (witness) {
|
||||||
|
console.log('[fixtures] deja chargees — rien a faire');
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Références (doivent exister via le seed).
|
||||||
|
const roleEtudiant = required(
|
||||||
|
await prisma.role.findUnique({ where: { code: 'ETUDIANT' } }),
|
||||||
|
'Role ETUDIANT manquant — lance d\'abord `npm run seed`',
|
||||||
|
);
|
||||||
|
const roleResponsable = required(
|
||||||
|
await prisma.role.findUnique({ where: { code: 'RESPONSABLE' } }),
|
||||||
|
'Role RESPONSABLE manquant — lance d\'abord `npm run seed`',
|
||||||
|
);
|
||||||
|
const campus = required(
|
||||||
|
await prisma.campus.findFirst({ where: { nom: 'Campus Saint-Christophe' } }),
|
||||||
|
'Campus manquant — lance d\'abord `npm run seed`',
|
||||||
|
);
|
||||||
|
const salle = required(
|
||||||
|
await prisma.sallePret.findFirst({ where: { campusId: campus.id } }),
|
||||||
|
'Salle de pret manquante — lance d\'abord `npm run seed`',
|
||||||
|
);
|
||||||
|
const poste = required(
|
||||||
|
await prisma.posteEmprunt.findFirst({ where: { sallePretId: salle.id } }),
|
||||||
|
'Poste manquant — lance d\'abord `npm run seed`',
|
||||||
|
);
|
||||||
|
const catOrdi = required(
|
||||||
|
await prisma.categorieMateriel.findFirst({ where: { nom: 'Ordinateur portable' } }),
|
||||||
|
'Categorie Ordinateur portable manquante',
|
||||||
|
);
|
||||||
|
const catVideo = required(
|
||||||
|
await prisma.categorieMateriel.findFirst({ where: { nom: 'Vidéoprojecteur' } }),
|
||||||
|
'Categorie Vidéoprojecteur manquante',
|
||||||
|
);
|
||||||
|
const catTablette = required(
|
||||||
|
await prisma.categorieMateriel.findFirst({ where: { nom: 'Tablette' } }),
|
||||||
|
'Categorie Tablette manquante',
|
||||||
|
);
|
||||||
|
const catCamera = required(
|
||||||
|
await prisma.categorieMateriel.findFirst({ where: { nom: 'Caméra' } }),
|
||||||
|
'Categorie Caméra manquante',
|
||||||
|
);
|
||||||
|
const catAudio = required(
|
||||||
|
await prisma.categorieMateriel.findFirst({ where: { nom: 'Périphérique audio' } }),
|
||||||
|
'Categorie Périphérique audio manquante',
|
||||||
|
);
|
||||||
|
|
||||||
|
// Utilisateurs : 3 étudiants (2 info @ensitech.eu, 1 autre @ensup.eu) + 1 responsable.
|
||||||
|
const marie = await prisma.utilisateur.create({
|
||||||
|
data: {
|
||||||
|
microsoftId: 'ms-marie-dupont',
|
||||||
|
nom: 'Dupont',
|
||||||
|
prenom: 'Marie',
|
||||||
|
email: WITNESS_EMAIL,
|
||||||
|
classe: 'B3 Informatique',
|
||||||
|
roleId: roleEtudiant.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const lucas = await prisma.utilisateur.create({
|
||||||
|
data: {
|
||||||
|
microsoftId: 'ms-lucas-martin',
|
||||||
|
nom: 'Martin',
|
||||||
|
prenom: 'Lucas',
|
||||||
|
email: 'lucas.martin@ensitech.eu',
|
||||||
|
classe: 'B2 Informatique',
|
||||||
|
roleId: roleEtudiant.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const sofia = await prisma.utilisateur.create({
|
||||||
|
data: {
|
||||||
|
microsoftId: 'ms-sofia-bernard',
|
||||||
|
nom: 'Bernard',
|
||||||
|
prenom: 'Sofia',
|
||||||
|
email: 'sofia.bernard@ensup.eu',
|
||||||
|
classe: 'B1 Marketing',
|
||||||
|
roleId: roleEtudiant.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const responsable = await prisma.utilisateur.create({
|
||||||
|
data: {
|
||||||
|
microsoftId: 'ms-karim-benali',
|
||||||
|
nom: 'Benali',
|
||||||
|
prenom: 'Karim',
|
||||||
|
email: 'karim.benali@ensup.eu',
|
||||||
|
classe: null, // un responsable n'a pas de classe
|
||||||
|
roleId: roleResponsable.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('[fixtures] 4 utilisateurs crees (3 etudiants + 1 responsable)');
|
||||||
|
|
||||||
|
// Cartes étudiantes (qrCode unique par carte).
|
||||||
|
await prisma.carteEtudiante.createMany({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
utilisateurId: marie.id,
|
||||||
|
qrCode: 'QR-ETU-0001',
|
||||||
|
dateActivation: daysAgo(300),
|
||||||
|
dateExpiration: daysFromNow(400),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
utilisateurId: lucas.id,
|
||||||
|
qrCode: 'QR-ETU-0002',
|
||||||
|
dateActivation: daysAgo(300),
|
||||||
|
dateExpiration: daysFromNow(400),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
utilisateurId: sofia.id,
|
||||||
|
qrCode: 'QR-ETU-0003',
|
||||||
|
dateActivation: daysAgo(300),
|
||||||
|
dateExpiration: daysFromNow(400),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
console.log('[fixtures] 3 cartes etudiantes creees');
|
||||||
|
|
||||||
|
// Accessoires.
|
||||||
|
const chargeur = await prisma.accessoire.create({
|
||||||
|
data: { nom: 'Chargeur', description: 'Chargeur secteur.' },
|
||||||
|
});
|
||||||
|
const souris = await prisma.accessoire.create({
|
||||||
|
data: { nom: 'Souris', description: 'Souris filaire.' },
|
||||||
|
});
|
||||||
|
const housse = await prisma.accessoire.create({
|
||||||
|
data: { nom: 'Housse', description: 'Housse de protection.' },
|
||||||
|
});
|
||||||
|
|
||||||
|
// Matériels (statut explicite — pas de défaut sur ce champ).
|
||||||
|
const pcDell = await prisma.materiel.create({
|
||||||
|
data: {
|
||||||
|
nom: 'PC portable Dell Latitude',
|
||||||
|
marque: 'Dell',
|
||||||
|
modele: 'Latitude 5440',
|
||||||
|
reference: 'REF-PC-001',
|
||||||
|
numeroInventaire: 'INV-PC-001',
|
||||||
|
numeroSerie: 'SN-DELL-001',
|
||||||
|
statut: 'EMPRUNTE',
|
||||||
|
etatGeneral: 'Bon etat',
|
||||||
|
categorieId: catOrdi.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const projecteur = await prisma.materiel.create({
|
||||||
|
data: {
|
||||||
|
nom: 'Vidéoprojecteur Epson',
|
||||||
|
marque: 'Epson',
|
||||||
|
modele: 'EB-W06',
|
||||||
|
reference: 'REF-VP-001',
|
||||||
|
numeroInventaire: 'INV-VP-001',
|
||||||
|
numeroSerie: 'SN-EPSON-001',
|
||||||
|
statut: 'EMPRUNTE',
|
||||||
|
etatGeneral: 'Bon etat',
|
||||||
|
categorieId: catVideo.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const ipad = await prisma.materiel.create({
|
||||||
|
data: {
|
||||||
|
nom: 'Tablette iPad',
|
||||||
|
marque: 'Apple',
|
||||||
|
modele: 'iPad 10',
|
||||||
|
reference: 'REF-TAB-001',
|
||||||
|
numeroInventaire: 'INV-TAB-001',
|
||||||
|
numeroSerie: 'SN-IPAD-001',
|
||||||
|
statut: 'DISPONIBLE',
|
||||||
|
etatGeneral: 'Bon etat',
|
||||||
|
categorieId: catTablette.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const camera = await prisma.materiel.create({
|
||||||
|
data: {
|
||||||
|
nom: 'Caméra Sony',
|
||||||
|
marque: 'Sony',
|
||||||
|
modele: 'Alpha 6400',
|
||||||
|
reference: 'REF-CAM-001',
|
||||||
|
numeroInventaire: 'INV-CAM-001',
|
||||||
|
numeroSerie: 'SN-SONY-001',
|
||||||
|
statut: 'DETERIORE',
|
||||||
|
etatGeneral: 'Housse manquante au retour',
|
||||||
|
categorieId: catCamera.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.materiel.create({
|
||||||
|
data: {
|
||||||
|
nom: 'Micro Rode',
|
||||||
|
marque: 'Rode',
|
||||||
|
modele: 'NT-USB',
|
||||||
|
reference: 'REF-MIC-001',
|
||||||
|
numeroInventaire: 'INV-MIC-001',
|
||||||
|
numeroSerie: 'SN-RODE-001',
|
||||||
|
statut: 'DISPONIBLE',
|
||||||
|
etatGeneral: 'Bon etat',
|
||||||
|
categorieId: catAudio.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.materiel.create({
|
||||||
|
data: {
|
||||||
|
nom: 'PC portable HP',
|
||||||
|
marque: 'HP',
|
||||||
|
modele: 'ProBook 450',
|
||||||
|
reference: 'REF-PC-002',
|
||||||
|
numeroInventaire: 'INV-PC-002',
|
||||||
|
numeroSerie: 'SN-HP-002',
|
||||||
|
statut: 'MAINTENANCE',
|
||||||
|
etatGeneral: 'En revision',
|
||||||
|
categorieId: catOrdi.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('[fixtures] 6 materiels crees');
|
||||||
|
|
||||||
|
// Accessoires attendus (obligatoire explicite — pas de défaut).
|
||||||
|
await prisma.materielAccessoire.createMany({
|
||||||
|
data: [
|
||||||
|
{ materielId: pcDell.id, accessoireId: chargeur.id, quantiteAttendue: 1, obligatoire: true },
|
||||||
|
{ materielId: pcDell.id, accessoireId: souris.id, quantiteAttendue: 1, obligatoire: false },
|
||||||
|
{ materielId: camera.id, accessoireId: housse.id, quantiteAttendue: 1, obligatoire: true },
|
||||||
|
{ materielId: camera.id, accessoireId: chargeur.id, quantiteAttendue: 1, obligatoire: true },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emprunt 1 — EN_COURS (Marie / PC Dell) + checklist de départ.
|
||||||
|
const empruntEnCours = await prisma.emprunt.create({
|
||||||
|
data: {
|
||||||
|
utilisateurId: marie.id,
|
||||||
|
materielId: pcDell.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
sallePretId: salle.id,
|
||||||
|
posteEmpruntId: poste.id,
|
||||||
|
dateEmprunt: daysAgo(2),
|
||||||
|
dateRetourPrevue: daysFromNow(5),
|
||||||
|
statut: 'EN_COURS',
|
||||||
|
modeIdentificationEmprunt: 'QR_CODE',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const clDepart1 = await prisma.checklist.create({
|
||||||
|
data: {
|
||||||
|
empruntId: empruntEnCours.id,
|
||||||
|
utilisateurId: marie.id,
|
||||||
|
type: 'DEPART',
|
||||||
|
dateVerification: daysAgo(2),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.checklistElement.createMany({
|
||||||
|
data: [
|
||||||
|
{ checklistId: clDepart1.id, accessoireId: chargeur.id, nomElement: 'Chargeur', etat: 'PRESENT', quantiteConstatee: 1 },
|
||||||
|
{ checklistId: clDepart1.id, accessoireId: souris.id, nomElement: 'Souris', etat: 'PRESENT', quantiteConstatee: 1 },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emprunt 2 — EN_RETARD (Lucas / Vidéoprojecteur), date de retour dépassée.
|
||||||
|
const empruntEnRetard = await prisma.emprunt.create({
|
||||||
|
data: {
|
||||||
|
utilisateurId: lucas.id,
|
||||||
|
materielId: projecteur.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
sallePretId: salle.id,
|
||||||
|
posteEmpruntId: poste.id,
|
||||||
|
dateEmprunt: daysAgo(20),
|
||||||
|
dateRetourPrevue: daysAgo(6),
|
||||||
|
statut: 'EN_RETARD',
|
||||||
|
modeIdentificationEmprunt: 'MAIL_ENSUP',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.checklist.create({
|
||||||
|
data: {
|
||||||
|
empruntId: empruntEnRetard.id,
|
||||||
|
utilisateurId: lucas.id,
|
||||||
|
type: 'DEPART',
|
||||||
|
dateVerification: daysAgo(20),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emprunt 3 — CLOTURE (Marie / iPad), retour conforme.
|
||||||
|
const empruntCloture = await prisma.emprunt.create({
|
||||||
|
data: {
|
||||||
|
utilisateurId: marie.id,
|
||||||
|
materielId: ipad.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
sallePretId: salle.id,
|
||||||
|
posteEmpruntId: poste.id,
|
||||||
|
dateEmprunt: daysAgo(10),
|
||||||
|
dateRetourPrevue: daysAgo(3),
|
||||||
|
dateRetourReelle: daysAgo(3),
|
||||||
|
statut: 'CLOTURE',
|
||||||
|
modeIdentificationEmprunt: 'QR_CODE',
|
||||||
|
modeIdentificationRetour: 'QR_CODE',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.checklist.create({
|
||||||
|
data: {
|
||||||
|
empruntId: empruntCloture.id,
|
||||||
|
utilisateurId: marie.id,
|
||||||
|
type: 'DEPART',
|
||||||
|
dateVerification: daysAgo(10),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.checklist.create({
|
||||||
|
data: {
|
||||||
|
empruntId: empruntCloture.id,
|
||||||
|
utilisateurId: marie.id,
|
||||||
|
type: 'RETOUR',
|
||||||
|
dateVerification: daysAgo(3),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// Emprunt 4 — RETOUR_NON_CONFORME (Sofia / Caméra) : housse absente au retour.
|
||||||
|
const empruntNonConforme = await prisma.emprunt.create({
|
||||||
|
data: {
|
||||||
|
utilisateurId: sofia.id,
|
||||||
|
materielId: camera.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
sallePretId: salle.id,
|
||||||
|
posteEmpruntId: poste.id,
|
||||||
|
dateEmprunt: daysAgo(5),
|
||||||
|
dateRetourPrevue: daysAgo(1),
|
||||||
|
dateRetourReelle: daysAgo(1),
|
||||||
|
statut: 'RETOUR_NON_CONFORME',
|
||||||
|
modeIdentificationEmprunt: 'MAIL_ENSUP',
|
||||||
|
modeIdentificationRetour: 'MAIL_ENSUP',
|
||||||
|
commentaireRetour: 'Housse non rapportee.',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
const clDepart4 = await prisma.checklist.create({
|
||||||
|
data: {
|
||||||
|
empruntId: empruntNonConforme.id,
|
||||||
|
utilisateurId: sofia.id,
|
||||||
|
type: 'DEPART',
|
||||||
|
dateVerification: daysAgo(5),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.checklistElement.createMany({
|
||||||
|
data: [
|
||||||
|
{ checklistId: clDepart4.id, accessoireId: housse.id, nomElement: 'Housse', etat: 'PRESENT', quantiteConstatee: 1 },
|
||||||
|
{ checklistId: clDepart4.id, accessoireId: chargeur.id, nomElement: 'Chargeur', etat: 'PRESENT', quantiteConstatee: 1 },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
const clRetour4 = await prisma.checklist.create({
|
||||||
|
data: {
|
||||||
|
empruntId: empruntNonConforme.id,
|
||||||
|
utilisateurId: sofia.id,
|
||||||
|
type: 'RETOUR',
|
||||||
|
dateVerification: daysAgo(1),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.checklistElement.createMany({
|
||||||
|
data: [
|
||||||
|
{ checklistId: clRetour4.id, accessoireId: housse.id, nomElement: 'Housse', etat: 'ABSENT', quantiteConstatee: 0, commentaire: 'Manquante au retour' },
|
||||||
|
{ checklistId: clRetour4.id, accessoireId: chargeur.id, nomElement: 'Chargeur', etat: 'PRESENT', quantiteConstatee: 1 },
|
||||||
|
],
|
||||||
|
});
|
||||||
|
console.log('[fixtures] 4 emprunts + checklists crees');
|
||||||
|
|
||||||
|
// Anomalie sur l'emprunt non conforme + notification au responsable.
|
||||||
|
const anomalie = await prisma.anomalie.create({
|
||||||
|
data: {
|
||||||
|
empruntId: empruntNonConforme.id,
|
||||||
|
materielId: camera.id,
|
||||||
|
utilisateurId: sofia.id,
|
||||||
|
type: 'ACCESSOIRE_MANQUANT',
|
||||||
|
description: 'Housse declaree presente au depart, absente au retour.',
|
||||||
|
statut: 'DETECTEE',
|
||||||
|
detecteeAutomatiquement: true,
|
||||||
|
dateDetection: daysAgo(1),
|
||||||
|
},
|
||||||
|
});
|
||||||
|
await prisma.notification.create({
|
||||||
|
data: {
|
||||||
|
utilisateurId: responsable.id,
|
||||||
|
anomalieId: anomalie.id,
|
||||||
|
titre: 'Nouvelle anomalie detectee',
|
||||||
|
message: 'Retour non conforme sur la Caméra Sony (housse manquante).',
|
||||||
|
type: 'ANOMALIE',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
console.log('[fixtures] 1 anomalie + 1 notification creees');
|
||||||
|
|
||||||
|
// Historique : quelques traces.
|
||||||
|
await prisma.historique.createMany({
|
||||||
|
data: [
|
||||||
|
{
|
||||||
|
utilisateurId: marie.id,
|
||||||
|
empruntId: empruntEnCours.id,
|
||||||
|
materielId: pcDell.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
sallePretId: salle.id,
|
||||||
|
posteEmpruntId: poste.id,
|
||||||
|
action: 'CREATION_EMPRUNT',
|
||||||
|
description: 'Emprunt du PC portable Dell.',
|
||||||
|
dateAction: daysAgo(2),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
utilisateurId: sofia.id,
|
||||||
|
empruntId: empruntNonConforme.id,
|
||||||
|
materielId: camera.id,
|
||||||
|
campusId: campus.id,
|
||||||
|
sallePretId: salle.id,
|
||||||
|
posteEmpruntId: poste.id,
|
||||||
|
action: 'CREATION_ANOMALIE',
|
||||||
|
description: 'Anomalie detectee au retour de la Caméra Sony.',
|
||||||
|
dateAction: daysAgo(1),
|
||||||
|
},
|
||||||
|
],
|
||||||
|
});
|
||||||
|
console.log('[fixtures] 2 entrees d\'historique creees');
|
||||||
|
}
|
||||||
|
|
||||||
|
main()
|
||||||
|
.then(async () => {
|
||||||
|
await prisma.$disconnect();
|
||||||
|
console.log('[fixtures] termine');
|
||||||
|
})
|
||||||
|
.catch(async (error: unknown) => {
|
||||||
|
console.error('[fixtures] echec', error);
|
||||||
|
await prisma.$disconnect();
|
||||||
|
process.exit(1);
|
||||||
|
});
|
||||||
@@ -85,6 +85,19 @@ En terminal interactif (VS Code), `migrate dev` fonctionne normalement (taper `y
|
|||||||
- Idempotence : `upsert` sur `Role.code` ; `findFirst` avant `create` pour les entités sans clé unique. Relance vérifiée (aucun doublon).
|
- Idempotence : `upsert` sur `Role.code` ; `findFirst` avant `create` pour les entités sans clé unique. Relance vérifiée (aucun doublon).
|
||||||
- Incident : le client Prisma était obsolète (ne connaissait que les 4 modèles de référence). Régénéré avec `npx prisma generate` avant le seed. Leçon : après des migrations, régénérer/vérifier le client avant d'exécuter du code qui l'utilise.
|
- Incident : le client Prisma était obsolète (ne connaissait que les 4 modèles de référence). Régénéré avec `npx prisma generate` avant le seed. Leçon : après des migrations, régénérer/vérifier le client avant d'exécuter du code qui l'utilise.
|
||||||
|
|
||||||
|
### Étape 10 — Fixtures de test
|
||||||
|
- Fichier `prisma/fixtures.ts` + script `npm run fixtures` (séparé du seed : données de dev/test, non installées en production).
|
||||||
|
- Non destructif : détection via un email témoin (`marie.dupont@ensitech.eu`) ; si présent, le script s'arrête (cohérent avec le principe "pas de suppression automatique").
|
||||||
|
- Contenu : 4 utilisateurs (3 étudiants + 1 responsable), 3 cartes, 6 matériels (statuts variés), 3 accessoires + liaisons, 4 emprunts couvrant `EN_COURS` / `EN_RETARD` / `CLOTURE` / `RETOUR_NON_CONFORME`, 6 checklists, 1 anomalie + 1 notification, 2 entrées d'historique.
|
||||||
|
- Idempotence vérifiée (2e exécution = skip).
|
||||||
|
|
||||||
|
#### Contexte métier clarifié — structure ENSUP (impacte la modélisation)
|
||||||
|
- ENSUP est un groupe **multi-campus** (Saint-Christophe/Cergy, Saint-Quentin, Nantes, Marseille...).
|
||||||
|
- Chaque campus héberge **deux écoles** : ENSUP (filières généralistes) et Ensitech (informatique). Bâtiments et salles **partagés**.
|
||||||
|
- La distinction d'école se fait par le **domaine email** : `ensitech.eu` (info) et `ensup.eu` (reste). La validation de domaine (RG, à venir) doit accepter les **deux**.
|
||||||
|
- Le modèle n'a pas d'entité "École" : seule `Campus` existe. Un campus = un lieu physique partagé par les deux écoles (et non une école).
|
||||||
|
- MVP : on démarre avec le **seul campus Saint-Christophe (Cergy)** ; les autres campus viendront ensuite.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Dette technique en attente
|
## Dette technique en attente
|
||||||
@@ -99,4 +112,4 @@ En terminal interactif (VS Code), `migrate dev` fonctionne normalement (taper `y
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Dernière mise à jour : 2026-06-15 — Seeds de référence en place.*
|
*Dernière mise à jour : 2026-06-15 — Fixtures de test en place, Block 2 terminé.*
|
||||||
|
|||||||
Reference in New Issue
Block a user