Spaces:
Sleeping
Sleeping
| // acceptancePatternsCore.ts — S-P3-1: split da acceptancePatterns.ts | |
| // Sezioni: Auth / Login, CRUD, Dashboard, API / Backend, Database, | |
| // Pagamenti, SaaS / Multi-tenant, Test suite, Package.json / deps | |
| import type { AcceptanceTest } from './acceptanceTypes'; // anti-ciclo: tipo estratto da goalTestDeriver | |
| export interface GoalPattern { | |
| re: RegExp; | |
| tests: Array<Omit<AcceptanceTest, "id">>; | |
| } | |
| export const CORE_PATTERNS: GoalPattern[] = [ | |
| // ── Auth / Login / Registration ───────────────────────────────────────────── | |
| { | |
| re: /login|signin|sign.?in|sign.?up|signup|registr(?:az|ation|ati)?|autenticaz|authentication|auth(?:orization|entication)?|logout|sessione?|password|jwt|bearer|token.?auth|oauth|2fa|mfa|otp/i, | |
| tests: [ | |
| { | |
| requirement: "File di autenticazione presente", | |
| critical: true, | |
| checkFn: ` | |
| const authFiles = files.filter(f => | |
| /auth|login|signin|session/i.test(f.path) && f.content | |
| ); | |
| return { pass: authFiles.length > 0, | |
| detail: authFiles.length > 0 | |
| ? "Trovato: " + authFiles.map(f=>f.path).join(", ") | |
| : "Mancano file auth/login nel VFS" }; | |
| `, | |
| }, | |
| { | |
| requirement: "Form di login con input email/password", | |
| critical: false, | |
| checkFn: ` | |
| const loginFile = files.find(f => | |
| /login|signin/i.test(f.path) && /email|password|input/i.test(f.content||"") | |
| ); | |
| return { pass: !!loginFile, | |
| detail: loginFile ? loginFile.path : "Nessun form login con email+password trovato" }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| // ── CRUD (crud|crm|list|crea|create|gestione) ─────────────────────────────── | |
| { | |
| re: /\bcrud\b|(?:crea|aggiungi|add).{0,50}(?:modifica|edit|update).{0,50}(?:elimina|delete|rimuovi)|(?:insert|update|delete).{0,30}(?:insert|update|delete)|gestione.{0,40}(?:utenti|prodotti|ordini|clienti|record|entit)|lista.*(?:crea|aggiungi|modifica|elimina)|tabella.*modificabil/i, | |
| tests: [ | |
| { | |
| requirement: "Operazioni CRUD presenti nel codice", | |
| critical: true, | |
| checkFn: ` | |
| const crudOps = { create: false, read: false, update: false, delete: false }; | |
| for (const f of files) { | |
| const c = (f.content||"").toLowerCase(); | |
| if (/insert|create|post.*body|supabase.*insert|\.add\(/i.test(c)) crudOps.create = true; | |
| if (/select|find|get.*list|fetch.*all|supabase.*select/i.test(c)) crudOps.read = true; | |
| if (/update|patch|put.*body|supabase.*update|\.set\(/i.test(c)) crudOps.update = true; | |
| if (/delete|remove|supabase.*delete|\.destroy\(/i.test(c)) crudOps.delete = true; | |
| } | |
| const missing = Object.entries(crudOps).filter(([,v])=>!v).map(([k])=>k); | |
| return { pass: missing.length === 0, | |
| detail: missing.length === 0 | |
| ? "C/R/U/D tutte presenti" | |
| : "Mancano operazioni: " + missing.join(", ") }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| // ── Dashboard ─────────────────────────────────────────────────────────────── | |
| { | |
| re: /dashboard|pannello|statistiche|analytics|chart|grafico|kpi/i, | |
| tests: [ | |
| { | |
| requirement: "Componente dashboard / statistiche presente", | |
| critical: true, | |
| checkFn: ` | |
| const dash = files.find(f => | |
| /dashboard|stats|analytics/i.test(f.path) && f.content | |
| ); | |
| return { pass: !!dash, | |
| detail: dash ? dash.path : "Manca file dashboard/stats nel VFS" }; | |
| `, | |
| }, | |
| { | |
| requirement: "Dati / metriche visualizzati (chart o tabella)", | |
| critical: false, | |
| checkFn: ` | |
| const hasChart = files.some(f => | |
| /recharts|chart\.js|apex|d3|nivo|canvas|<table|<tr/i.test(f.content||"") | |
| ); | |
| return { pass: hasChart, | |
| detail: hasChart ? "Libreria chart o tabella trovata" : "Nessuna visualizzazione dati trovata" }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| // ── API / Backend ─────────────────────────────────────────────────────────── | |
| { | |
| re: /api|endpoint|rest|fastapi|express|backend|server/i, | |
| tests: [ | |
| { | |
| requirement: "File route/endpoint presente", | |
| critical: true, | |
| checkFn: ` | |
| const route = files.find(f => | |
| /route|endpoint|controller|api\//i.test(f.path) && | |
| /(app\.(get|post|put|delete|patch)|router\.|@app\.|@router\.)/i.test(f.content||"") | |
| ); | |
| return { pass: !!route, | |
| detail: route ? route.path : "Nessun route/endpoint trovato" }; | |
| `, | |
| }, | |
| { | |
| requirement: "Almeno 1 endpoint GET e 1 POST", | |
| critical: false, | |
| checkFn: ` | |
| const allCode = files.map(f=>f.content||"").join("\\n"); | |
| const hasGet = /(app|router)\.(get|route)\(|@app\.get|@router\.get/i.test(allCode); | |
| const hasPost = /(app|router)\.(post|route)\(|@app\.post|@router\.post/i.test(allCode); | |
| return { pass: hasGet && hasPost, | |
| detail: (!hasGet ? "Manca GET " : "") + (!hasPost ? "Manca POST" : "GET e POST presenti") }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| // ── Database ──────────────────────────────────────────────────────────────── | |
| { | |
| re: /database|supabase|postgresql|postgres|mysql|mongodb|sqlite|prisma|drizzle/i, | |
| tests: [ | |
| { | |
| requirement: "Schema / modello dati presente", | |
| critical: true, | |
| checkFn: ` | |
| const schema = files.find(f => | |
| /schema|model|migration|\.sql$|prisma/i.test(f.path) && f.content | |
| ); | |
| const hasTableDef = files.some(f => | |
| /create table|defineTable|pgTable|model.*{|Schema\(/i.test(f.content||"") | |
| ); | |
| return { pass: !!(schema || hasTableDef), | |
| detail: schema ? schema.path : (hasTableDef ? "Schema trovato in codice" : "Manca schema/modello dati") }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| // ── Pagamenti ─────────────────────────────────────────────────────────────── | |
| { | |
| re: /pagamento|stripe|payment|checkout|abbonamento|subscription/i, | |
| tests: [ | |
| { | |
| requirement: "Integrazione pagamenti (Stripe / checkout)", | |
| critical: true, | |
| checkFn: ` | |
| const payment = files.find(f => | |
| /stripe|payment|checkout/i.test(f.content||"") | |
| ); | |
| return { pass: !!payment, | |
| detail: payment ? payment.path : "Nessuna integrazione pagamenti trovata" }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| // ── SaaS / Multi-tenant ───────────────────────────────────────────────────── | |
| { | |
| re: /saas|multi.?tenant|organization|workspace|team/i, | |
| tests: [ | |
| { | |
| requirement: "Isolamento tenant (organization_id / workspace_id)", | |
| critical: true, | |
| checkFn: ` | |
| const allCode = files.map(f=>f.content||"").join("\\n"); | |
| const hasTenant = /organization_id|tenant_id|workspace_id|org_id|team_id/i.test(allCode); | |
| return { pass: hasTenant, | |
| detail: hasTenant ? "Tenant isolation trovata" : "Manca organization_id/tenant_id nei modelli" }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| // ── Test suite ────────────────────────────────────────────────────────────── | |
| { | |
| re: /test.*suite|unit test|integration test|vitest|jest|pytest/i, | |
| tests: [ | |
| { | |
| requirement: "File di test presenti", | |
| critical: true, | |
| checkFn: ` | |
| const tests = files.filter(f => /\\.test\\.|spec\\.|__tests__/i.test(f.path)); | |
| return { pass: tests.length > 0, | |
| detail: tests.length > 0 | |
| ? tests.length + " file di test: " + tests.slice(0,3).map(f=>f.path).join(", ") | |
| : "Nessun file .test.ts / spec.ts trovato" }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| // ── Package.json / deps ───────────────────────────────────────────────────── | |
| { | |
| re: /\bapp\b|\bsito\b|progetto|project|applicazione/i, | |
| tests: [ | |
| { | |
| requirement: "package.json o requirements.txt presente", | |
| critical: false, | |
| checkFn: ` | |
| const pkg = files.find(f => f.path === "package.json" || f.path === "/package.json" | |
| || f.path === "requirements.txt"); | |
| return { pass: !!pkg, detail: pkg ? pkg.path + " trovato" : "Manca package.json / requirements.txt" }; | |
| `, | |
| }, | |
| ], | |
| }, | |
| ]; | |