AUDIT / src /lib /acceptancePatternsExtended.ts
Arypulka98's picture
feat(audit): deploy full backend cluster node (part 2)
cc11e77 verified
Raw
History Blame Contribute Delete
15.1 kB
// acceptancePatternsExtended.ts — S-P3-1: split da acceptancePatterns.ts
// Sezioni: GraphQL / Subscriptions, WebSocket / Real-time, Pagination,
// i18n / Localization, E-commerce / Cart, Social features,
// Map / Geolocation, Calendar / Booking, OAuth / Social Login,
// Caching / Redis, CLI / Command-line
import type { GoalPattern } from './acceptancePatternsCore';
export const EXTENDED_PATTERNS: GoalPattern[] = [
// ── S412: GraphQL / Subscriptions ───────────────────────────────────────────
{
re: /graphql|apollo|subscri(?:ption|zioni)|gql|query.*mutation|mutation.*query/i,
tests: [
{
requirement: "Schema GraphQL o client Apollo presente",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasSchema = /type Query|type Mutation|type Subscription|typeDefs|gql\`|makeExecutableSchema/i.test(allCode);
const hasClient = /ApolloClient|useQuery|useMutation|useSubscription|graphql-request/i.test(allCode);
const hasFile = files.some(f => /\\.graphql$|\\.gql$|schema\\.(ts|js)|resolvers/i.test(f.path));
const pass = hasSchema || hasClient || hasFile;
return { pass,
detail: pass
? "GraphQL trovato: " + [hasSchema && "schema/typeDefs", hasClient && "Apollo client/hooks", hasFile && "file .graphql"].filter(Boolean).join(", ")
: "Manca schema GraphQL, Apollo client o file .graphql/.gql" };
`,
},
],
},
// ── S412: WebSocket / Real-time / Chat ──────────────────────────────────────
{
re: /websocket|socket\.io|real.?time|live.*(?:chat|update|feed)|pusher|ably|supabase.*realtime|broadcast/i,
tests: [
{
requirement: "Connessione real-time / WebSocket implementata",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasWS = /new WebSocket|socket\.io|io\(|useSocket|ws\\.on|ws\\.emit/i.test(allCode);
const hasPusher = /pusher|ably|centrifuge|phoenix\.socket/i.test(allCode);
const hasRealtime= /supabase.*realtime|\.on\\(.*message|\.subscribe\\(.*channel/i.test(allCode);
const pass = hasWS || hasPusher || hasRealtime;
return { pass,
detail: pass
? "Real-time trovato: " + [hasWS && "WebSocket/socket.io", hasPusher && "Pusher/Ably", hasRealtime && "Supabase Realtime"].filter(Boolean).join(", ")
: "Manca connessione WebSocket, socket.io, Pusher o Supabase Realtime" };
`,
},
],
},
// ── S412: Pagination / Infinite Scroll ──────────────────────────────────────
{
re: /paginaz|pagination|pagina.*\d+|infinite.*scroll|load.*more|cursor.*based|offset.*limit/i,
tests: [
{
requirement: "Sistema paginazione implementato",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasOffset = /offset|skip|LIMIT.*OFFSET|page.*size|pageSize|per_page/i.test(allCode);
const hasCursor = /cursor|after|before|hasNextPage|endCursor|nextCursor/i.test(allCode);
const hasInfinite= /IntersectionObserver|useInfiniteQuery|loadMore|fetchNextPage|onEndReached/i.test(allCode);
const pass = hasOffset || hasCursor || hasInfinite;
return { pass,
detail: pass
? "Paginazione trovata: " + [hasOffset && "offset/limit", hasCursor && "cursor-based", hasInfinite && "infinite scroll"].filter(Boolean).join(", ")
: "Manca paginazione (nessun offset/limit, cursor, useInfiniteQuery o IntersectionObserver)" };
`,
},
],
},
// ── S412: i18n / Localization / Multi-language ──────────────────────────────
{
re: /i18n|internazionalizzaz|localizz|multi.?language|multilingual|traduzion|react.?intl|next.?intl|i18next/i,
tests: [
{
requirement: "Sistema internazionalizzazione (i18n) presente",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasLib = /i18next|react-intl|next-intl|lingui|vue-i18n|python-babel|gettext/i.test(allCode);
const hasT = /useTranslation|t\\("|__\\("|_\\("|formatMessage|<Trans|<FormattedMessage/i.test(allCode);
const hasLocale = files.some(f => /locale|translations?|messages?|lang/.test(f.path) && /\\.(?:json|yaml|po|mo)$/.test(f.path));
const pass = (hasLib || hasT) && hasLocale;
return { pass,
detail: pass
? "i18n trovato: " + [hasLib && "libreria i18n", hasT && "hook t()/useTranslation", hasLocale && "file traduzioni"].filter(Boolean).join(", ")
: "Manca: " + (!hasLib && !hasT ? "libreria i18n o hook t(); " : "") + (!hasLocale ? "file di traduzione (.json/.yaml/.po)" : "") };
`,
},
],
},
// ── S412: E-commerce / Cart / Orders ────────────────────────────────────────
{
re: /e.?commerce|negozio.*online|shop|carrello|cart|ordini?|checkout|acquisto|prodotto.*prezzo|catalogo.*prodotti/i,
tests: [
{
requirement: "Carrello acquisti implementato",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasCart = /cart|carrello|basket|shopping_cart|addToCart|removeFromCart/i.test(allCode);
const hasOrder = /order|ordine|checkout|purchase|acquisto/i.test(allCode);
const hasPrice = /price|prezzo|cost|costo|amount|total|subtotal/i.test(allCode);
const pass = hasCart && hasOrder && hasPrice;
return { pass,
detail: pass
? "E-commerce trovato: carrello + ordini + prezzi"
: "Manca: " + (!hasCart ? "gestione carrello; " : "") + (!hasOrder ? "gestione ordini; " : "") + (!hasPrice ? "campo prezzo/total" : "") };
`,
},
{
requirement: "Stato carrello persiste (store o DB)",
critical: false,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasStore = /useCart|cartStore|cartContext|useShoppingCart|localStorage.*cart|sessionStorage.*cart/i.test(allCode);
const hasDB = /cart.*table|orders.*table|supabase.*cart|insert.*cart/i.test(allCode);
return { pass: hasStore || hasDB,
detail: (hasStore || hasDB)
? "Persistenza carrello trovata: " + [hasStore && "store/context", hasDB && "DB"].filter(Boolean).join(", ")
: "Carrello non persiste (manca store, localStorage o DB)" };
`,
},
],
},
// ── S412: Social features / Comments / Likes ────────────────────────────────
{
re: /commenti?|comment|like|reazione|reaction|follow|follower|condividi|share|social.*feature|feed.*social/i,
tests: [
{
requirement: "Feature social (commenti / like / follow) implementata",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasComment = /comment|commento|reply|risposta/i.test(allCode);
const hasLike = /like|unlike|reaction|upvote|downvote|heart/i.test(allCode);
const hasFollow = /follow|unfollow|subscribe|following|follower/i.test(allCode);
const pass = hasComment || hasLike || hasFollow;
return { pass,
detail: pass
? "Social trovato: " + [hasComment && "commenti", hasLike && "like/reaction", hasFollow && "follow"].filter(Boolean).join(", ")
: "Manca: commenti, like/reaction e follow" };
`,
},
{
requirement: "Tabella commenti o likes nel DB",
critical: false,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasTable = /create table.*comment|comments.*table|likes.*table|reactions.*table|comment.*schema|like.*schema/i.test(allCode);
return { pass: hasTable,
detail: hasTable ? "Schema DB commenti/likes trovato" : "Manca schema DB per commenti/likes" };
`,
},
],
},
// ── S412: Map / Geolocation ─────────────────────────────────────────────────
{
re: /mappa|map(?:box|libre)?|google.*maps|leaflet|geolocalizzaz|coordinate|gps.*track|marker|geocod/i,
tests: [
{
requirement: "Componente mappa / geolocalizzazione presente",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasLib = /leaflet|mapbox|google.*maps|maplibre|react-map-gl|folium|geopandas/i.test(allCode);
const hasCoords = /latitude|longitude|lat(?:itude)?\\s*:|lng\\s*:|coordinates|LatLng/i.test(allCode);
const hasGeo = /navigator\\.geolocation|getCurrentPosition|watchPosition|geocode/i.test(allCode);
const pass = hasLib || (hasCoords && hasGeo);
return { pass,
detail: pass
? "Mappa trovata: " + [hasLib && "libreria mappa", hasCoords && "coordinate", hasGeo && "geolocalizzazione"].filter(Boolean).join(", ")
: "Manca libreria mappa (Leaflet/Mapbox/Google Maps) o navigator.geolocation" };
`,
},
],
},
// ── S412: Calendar / Booking / Scheduling ───────────────────────────────────
{
re: /calendario?|calendar|prenotaz|booking|appuntament|schedul|event.*date|date.*picker|slot.*orar/i,
tests: [
{
requirement: "Sistema calendario / prenotazioni implementato",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasCal = /FullCalendar|react-calendar|date-fns|dayjs|moment|Calendar|DatePicker/i.test(allCode);
const hasEvent = /event|evento|appuntamento|appointment|booking|prenotazione|slot/i.test(allCode);
const hasDate = /start_date|end_date|startDate|endDate|date_from|date_to|scheduled_at/i.test(allCode);
const pass = (hasCal || hasEvent) && hasDate;
return { pass,
detail: pass
? "Calendario trovato: " + [hasCal && "libreria calendario", hasEvent && "eventi/prenotazioni", hasDate && "date start/end"].filter(Boolean).join(", ")
: "Manca: " + (!hasCal && !hasEvent ? "componente calendario; " : "") + (!hasDate ? "campi data start/end" : "") };
`,
},
],
},
// ── S412: OAuth / Social Login ──────────────────────────────────────────────
{
re: /oauth|google.*sign.?in|sign.?in.*google|github.*auth|login.*(?:google|github|facebook|apple|microsoft)|sso|openid/i,
tests: [
{
requirement: "OAuth / social login implementato",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasOAuth = /oauth|openid|supabase.*auth.*provider|next-auth|passport\\.use|google.*strategy|github.*strategy/i.test(allCode);
const hasButton = /LoginWithGoogle|SignInWithGithub|OAuthButton|SocialLogin|provider.*google|provider.*github/i.test(allCode);
const hasConfig = /GOOGLE_CLIENT_ID|GITHUB_CLIENT_ID|OAUTH_CLIENT|AUTH_PROVIDER|clientId.*google/i.test(allCode);
const pass = hasOAuth && (hasButton || hasConfig);
return { pass,
detail: pass
? "OAuth trovato: strategia/provider + configurazione client"
: "Manca: " + (!hasOAuth ? "libreria OAuth (next-auth/passport/supabase auth); " : "") + (!hasButton && !hasConfig ? "pulsante login o config CLIENT_ID" : "") };
`,
},
],
},
// ── S412: Caching / Redis / Memoization ────────────────────────────────────
{
re: /cach(?:e|ing)|redis|memcach|cdn.*cache|stale.?while.?revalidate|memoiz|lru.*cache|cache.*invalidat/i,
tests: [
{
requirement: "Strategia di caching implementata",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasRedis = /redis|ioredis|upstash|Redis\\.createClient/i.test(allCode);
const hasMemo = /useMemo|useCallback|React\\.memo|lru-cache|memoize|functools\\.lru_cache/i.test(allCode);
const hasHTTP = /Cache-Control|ETag|stale-while-revalidate|max-age=|s-maxage/i.test(allCode);
const hasNext = /revalidate|unstable_cache|next.*cache|SWR|useQuery.*staleTime/i.test(allCode);
const pass = hasRedis || hasMemo || hasHTTP || hasNext;
return { pass,
detail: pass
? "Caching trovato: " + [hasRedis && "Redis/Upstash", hasMemo && "memoization", hasHTTP && "HTTP Cache-Control", hasNext && "SWR/React Query/Next cache"].filter(Boolean).join(", ")
: "Nessuna strategia di caching trovata (Redis, useMemo, Cache-Control, SWR)" };
`,
},
],
},
// ── S412: CLI / Command-line tool ───────────────────────────────────────────
{
re: /cli|command.?line|terminal.*app|console.*app|argparse|commander|typer|click.*python|yargs/i,
tests: [
{
requirement: "CLI / tool a riga di comando implementato",
critical: true,
checkFn: `
const allCode = files.map(f => f.content || "").join("\\n");
const hasArgs = /argparse|ArgumentParser|commander|yargs|typer|click|process\\.argv|sys\\.argv/i.test(allCode);
const hasBin = files.some(f => f.path.includes("bin/") || f.path.endsWith("/cli.ts") || f.path.endsWith("/cli.js") || f.path.endsWith("/cli.py"));
const hasMain = /if\\s+__name__.*main|cli\\(\\)|program\\.parse|app\\(\\)/i.test(allCode);
const pass = hasArgs || hasBin || hasMain;
return { pass,
detail: pass
? "CLI trovato: " + [hasArgs && "parser argomenti", hasBin && "file bin/cli", hasMain && "entry point main"].filter(Boolean).join(", ")
: "Manca: argparse/commander/yargs, file bin/ o entry point CLI" };
`,
},
],
},
];