import { PrismaClient } from '@prisma/client';
import { faker } from '@faker-js/faker';
const prisma = new PrismaClient();
async function main() {
// Setăm seed-ul Faker pentru determinism în CI
faker.seed(42);
const staticUsers = Array.from({ length: 5 }).map((_, i) => ({
id: `user-ci-${i + 1}`,
email: faker.internet.email().toLowerCase(),
name: faker.person.fullName(),
role: i === 0 ? 'ADMIN' : 'USER',
}));
for (const user of staticUsers) {
await prisma.user.upsert({
where: { id: user.id },
update: {
name: user.name,
email: user.email,
},
create: user,
});
}
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});Seeding-ul în CI e una dintre chestiile alea pe care le ignori până când vezi că pipeline-ul tău de GitHub Actions durează 12 minute doar pentru teste de integrare. Am pățit-o anul trecut la un proiect cu vreo 80 de tabele unde prisma db seed băga date la întâmplare și pica o dată la trei build-uri. Soluția n-a fost să scoatem testele, ci să rescriem seeding-ul astfel încât să fie 100% determinist și bazat pe upsert.
Trapa datelor aleatorii cu Faker
Chestia cu @faker-js/faker este că e excelentă când vrei UI plin cu avatare și nume amuzante pe dev-ul local, dar e un dezastru în CI dacă nu o controlezi. Când rulezi teste de integrare, nu vrei ca utilizatorul de test să aibă un e-mail diferit la fiecare rulare de pipeline.
Dacă vrei date pseudo-aleatorii dar reproducibile, trucul e să folosești faker.seed(123) înainte să generezi orice entitate. În modul ăsta, la fiecare rulare de CI, Faker va genera exact aceleași string-uri, în aceeași ordine. Dacă un test pică joi la 11 noaptea, poți să reproduci exact aceeași stare a bazei de date pe laptopul tău fără să-ți prinzi urechile.
Create vs Upsert: De ce crapă pipeline-ul la rerulare
Marea greșeală pe care am văzut-o la mulți devi e să folosească prisma.model.create() în scriptul de seed fără să curețe baza înainte. Dacă containerul de Postgres din CI e refolosit sau dacă rulezi scriptul local de două ori la rând, pac: Unique constraint failed.
Nu-ți băga picioarele făcând DELETE FROM pe toate tabelele la început de seed. Pe lângă faptul că strici secvențele din Postgres, crești inutil timpul de execuție. Folosește upsert. În modul ăsta, dacă recordul există deja (după id sau un e-mail unic), Prisma îi face update, iar dacă nu există, îl creează curat.
Trade-off-uri reale de performanță
Metoda cu upsert vine cu un cost clar. În loc de o singură interogare INSERT, Prisma execută instrucțiuni mai complexe sub capotă (INSERT ... ON CONFLICT pe Postgres). La un volum de 10.000 de înregistrări, chestia asta se simte extrem de tare. Am avut un caz unde seed-ul dura 45 de secunde doar din cauza asta.
Cum am rezolvat? Am separat complet scripturile:
- Static CI Seed (
seed-ci.ts): Rulează doar ce e critic pentru teste (20 utilizatori de test, 3 role-uri, setările de bază). Durează sub 2 secunde. - Dev Seed (
seed-dev.ts): Populează baza cu 5.000 de produse fictive pentru scroll infinit pe localhost. Scriptul ăsta n-are ce căuta în pipeline-ul de CI.
Voi cum gestionați datele de test în CI? Curățați tot containerul la fiecare pas sau preferați seed-uri idempotențe cu upsert?