eduardweb.
Prisma ORMIntermediar#typescript#prisma#ci-cd#backend#testing

Cum scrii seed-uri de Prisma pentru CI fără să-ți crape pipeline-ul la al doilea run

De Gabriela Neagu, 4 aug. 2026 · 8 vizualizări · 2 like-uri

Postat 4 aug. 2026
typescript
import { PrismaClient } from '@prisma/client';
import { faker } from '@faker-js/faker';

const prisma = new PrismaClient();

async function main() { // Fixăm seed-ul pentru Faker -> Date 100% deterministe în CI
  faker.seed(42);

  // 1. Upsert pentru date de bază (Idempotent)
  const adminRole = await prisma.role.upsert({
    where: { name: 'ADMIN' },
    update: {},
    create: {
      name: 'ADMIN',
      description: 'Administrator sistem',
    },
  });

  // 2. Generare deterministă de useri
  const dummyUsers = Array.from({ length: 5 }).map(() => ({
    email: faker.internet.email().toLowerCase(),
    name: faker.person.fullName(),
    roleId: adminRole.id,
  }));

  for (const user of dummyUsers) {
    await prisma.user.upsert({
      where: { email: user.email },
      update: { name: user.name },
      create: user,
    });
  }

  console.log('Seed executat cu succes!');
}

main()
  .catch((e) => {
    console.error(e);
    process.exit(1);
  })
  .finally(async () => {
    await prisma.$disconnect();
  });

Salutare! Săptămâna trecută m-am certat vreo două ore cu un pipeline de GitHub Actions care pica total aleatoriu. Se pare că un coleg făcuse seed-ul bazei de date cu Faker, dar uita de un detaliu banal: datele se schimbau la fiecare rulare și rupeau testele de Cypress. Dacă vrei un mediu de CI/CD predictibil, datele tale de test trebuie să fie identice de fiecare dată.

De ce crapă scripturile de seed în CI

Am avut prin 2022 un proiect mai măricel, cu 40 de tabele în PostgreSQL și o suită stufoasă de teste e2e. La început, băieții dăduseră prisma db seed folosind simplu prisma.user.create(). Prima dată pe o bază curată mergea brici. A doua oară când rula pipeline-ul pe un volum persistent sau când dădeai re-run la job... bam! Unique constraint failed on the fields: (email).

Problematic nu e doar faptul că pica scriptul, ci și modul în care erau generate datele. Dacă ai 50 de utilizatori de test și names-urile lor variază de la „Ana” la „Alexandru-Constantin”, interfața se va comporta diferit la layout. În CI ai nevoie de determinism pure, nu de surprize.

1. Idempotență pură cu upsert în loc de create

Regula de aur pentru datele de referință (roluri, setări din sistem, planuri de abonament) este să renunți complet la .create(). Folosește întotdeauna .upsert(). Chiar dacă scriptul tău rulează de 10 ori la rând pe aceeași bază, starea finală trebuie să rămână identică.

Cheia e să folosești un identificator unic stabil în clauza where. Pentru un rol, folosești code-ul sau name-ul, nu un ID auto-generat de DB.

2. Faker determinist folosind faker.seed()

Librăria @faker-js/faker este excelentă pentru generat mock-uri, dar implicit folosește Math.random(). Asta înseamnă că fiecare rulare scuipă altceva. Soluția e stupid de simplă: setează un seed fix la începutul fișierului.

Dacă pui faker.seed(12345) la prima linie din scriptul tău, Faker va genera exact aceeași secvență de nume, emailuri și adrese de fiecare dată când rulezi comanda. Astfel, dacă un test de Cypress caută utilizatorul „Ion Popescu”, știi sigur că acel utilizator va exista și va avea exact același email.

Trade-off sincer: Performanță vs. Siguranță

Trebuie să fiu sincer cu voi. upsert-ul vine cu un cost de performanță. La un proiect unde trebuia să generăm vreo 8.000 de tranzacții dummy pentru niște rapoarte, un loop cu upsert dura aproape 45 de secunde în CI. În schimb, un createMany le trântea pe toate în 2 secunde.

Cum am rezolvat-o? Am împărțit seed-ul în două faze:

  1. Core Data (Upsert): Roluri, admini, configurări globale. Aici folosim upsert pentru că datele trebuie să persiste curat.
  2. Transactional Mock Data (Delete + createMany): Ștergem tabelul de tranzacții de test (deleteMany) și băgăm createMany cu seed-ul fixat din Faker. E ultra rapid și evită erorile de cheie unică.

Voi cum gestionați datele de test în CI? Curățați baza cu totul la fiecare run sau mergeți pe scripturi idempotente?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.