import { PrismaClient } from '@prisma/client';
import { faker } from '@faker-js/faker';
const prisma = new PrismaClient();
async function main() {
const isCI = process.env.CI === 'true';
// Determinism garantat în CI
if (isCI) {
faker.seed(42);
}
const totalUsers = isCI ? 10 : 100;
for (let i = 0; i < totalUsers; i++) {
const email = `user-${i}@example.test`;
await prisma.user.upsert({
where: { email },
update: { name: faker.person.fullName() },
create: {
email,
name: faker.person.fullName(),
role: i === 0 ? 'ADMIN' : 'USER',
},
});
}
}
main()
.catch((e) => {
console.error(e);
process.exit(1);
})
.finally(async () => {
await prisma.$disconnect();
});Acum vreo doi ani, la un proiect cu peste 15 microservicii și vreo 8k useri activi, aveam o problemă extrem de enervantă pe pipeline-ul de CI. Testele de integrare picau cam o dată la 5-6 commit-uri fără absolut niciun motiv logic în codul schimbat.
După ce am pierdut vreo 2 zile făcând debug pe runnerii de GitHub Actions, m-am prins: problema era în scriptul nostru de prisma db seed. Generam date random cu Faker, iar uneori edge-case-urile generate (string-uri prea lungi, caractere speciale neașteptate sau e-mail-uri duplicate din pură coincidență) dădeau peste cap testele de API.
De ce Faker nedeterminist îți distruge pipeline-ul
Implicit, librăriile de tip @faker-js/faker generează valori folosind o sursă de pseudorandomness bazată pe timestamp-ul curent. În dev local e super ok, că vrei să vezi aplicația plină de date variate. În CI, e o rețetă sigură pentru dezastru.
Soluția simplă pe care mulți o uită e setarea unui seed fix: faker.seed(12345). Dacă apelezi asta la începutul scriptului de seed, Faker va genera exact aceeași secvență de date de fiecare dată când rulează pe CI. Dacă testul a trecut o dată, va trece și a 100-a oară.
Trapa createMany vs siguranța din upsert
Un alt moment în care ne-am furat-o a fost când rulam teste în paralel pe același container de bază de date fără să facem cleanup complet. Foloseam prisma.user.createMany(), iar la a doua rulare crăpa totul din cauza unique constraints pe email.
Am schimbat abordarea și am trecut pe upsert. În loc să presupunem că baza e mereu goală, facem match pe un identifier unic (de exemplu email sau un id determinist).
Există un trade-off clar aici. upsert e sensibil mai lent decât un createMany brut sau un SQL INSERT direct. La noi, timpul de seed pentru ~400 de entități a crescut de la 1.1 secunde la aproape 4 secunde. Dar cinstit? Prefer să aștept 3 secunde în plus în CI decât să am un build roșu aiurea la ora 7 seara înainte de deploy.
Separarea mediilor: Dev vs CI
Nu vrei 10.000 de înregistrări în CI când testezi doar fluxul de checkout. Timpul de build e scump.
Am împărțit scriptul de seed folosind o variabilă de mediu simplă: CI=true sau SEED_SIZE. Dacă suntem în CI, generăm doar minimul necesar (de exemplu 5 categorii și 10 produse) cu faker.seed(). Dacă suntem în dev local pe laptop, lăsăm Faker liber și generăm 500 de produse ca să testăm paginarea și UI-ul.
Voi cum gestionați teardown-ul și datele de test în CI? Resetați schema complet cu prisma migrate reset --force la fiecare run sau mergeți pe tranzacții cu rollback?