/** * Jeu de données du serveur e2e. * * Le faux pool associe un fragment de SQL à un résultat : l'ordre compte, le * premier fragment qui correspond gagne. Les entrées les plus spécifiques * doivent donc précéder les plus générales. */ import bcrypt from "bcryptjs"; import { rows } from "../../../api/test/helpers/fakePool.js"; export const E2E_USER = "nico"; export const E2E_PASSWORD = "motdepasse-e2e"; // Coût 4 : suffisant pour exercer le vrai chemin bcrypt sans les ~330 ms du // coût 12 utilisé en production. const PASSWORD_HASH = bcrypt.hashSync(E2E_PASSWORD, 4); const BANDS = [ { ma_id: 1, name: "Mayhem", country: "NO", status: "Active", genre: "Black Metal", location_text: "Oslo", themes: "Death, Satanism", formed_year: 1984, enriched: true, lat: 59.91, lon: 10.75, crawled_at: "2026-08-01T10:00:00Z", updated_at: "2026-08-10T10:00:00Z", first_seen_at: "2026-01-01T00:00:00Z", locked_fields: {} }, { ma_id: 2, name: "Darkthrone", country: "NO", status: "Active", genre: "Black Metal", location_text: "Kolbotn", themes: "Cold, Winter", formed_year: 1986, enriched: true, lat: null, lon: null, crawled_at: "2026-08-02T10:00:00Z", updated_at: "2026-08-11T10:00:00Z", first_seen_at: "2026-01-01T00:00:00Z", locked_fields: { name: true } }, { ma_id: 3, name: "Gojira", country: "FR", status: "Active", genre: "Progressive Death Metal", location_text: "Bayonne", themes: "Nature", formed_year: 1996, enriched: false, lat: 43.49, lon: -1.47, crawled_at: null, updated_at: "2026-08-12T10:00:00Z", first_seen_at: "2026-02-01T00:00:00Z", locked_fields: {} }, ]; const LOCATIONS = [ { id: 11, ma_id: 2, band_name: "Darkthrone", country: "NO", step_order: 0, step_label: null, location_raw: "Kolbotn", is_country_only: false, lat: null, lon: null, geocode_status: "error", geocode_provider: null, geocode_confidence: null, geocode_granularity: null, geocode_query: "Kolbotn, Norway", geocode_error: "Aucun résultat au-dessus du seuil de confiance", geocode_tries_geo: 3, geocode_tries_llm: 0, geocode_next_at: "2026-08-12T09:00:00Z", updated_at: "2026-08-12T09:00:00Z" }, { id: 12, ma_id: 3, band_name: "Gojira", country: "FR", step_order: 0, step_label: "early", location_raw: "Ondres / Bayonne", is_country_only: false, lat: null, lon: null, geocode_status: "llm_needed", geocode_provider: null, geocode_confidence: null, geocode_granularity: null, geocode_query: null, geocode_error: null, geocode_tries_geo: 3, geocode_tries_llm: 1, geocode_next_at: "2026-08-12T08:00:00Z", updated_at: "2026-08-12T08:00:00Z" }, ]; /** * @param {object} [over] surcharges par scénario (ex. aucun run actif) */ export function seedHandlers(over = {}) { const { activeRuns = [], pendingJobs = [], geoStatuses = [ { status: "done", n: 1840 }, { status: "error", n: 12 }, { status: "llm_needed", n: 5 }, { status: "queued", n: 3 }, ], bands = BANDS, locations = LOCATIONS, neverEnriched = 1, } = over; return [ // ---- Authentification ---- // Fragment volontairement précis : « SELECT count(*)::int AS n » seul // capturait aussi le comptage du cache de géocodage. { match: "FROM admin_login_attempts\n WHERE success = false", result: rows({ n: 0 }) }, { match: "FROM admin_users", result: rows({ password_hash: PASSWORD_HASH }) }, { match: "INSERT INTO admin_login_attempts", result: rows() }, { match: "UPDATE admin_users", result: rows() }, // ---- Écritures ---- { match: "INSERT INTO admin_audit_log", result: rows() }, { match: "INSERT INTO crawl_log", result: rows() }, { match: "INSERT INTO job_triggers", result: rows({ id: 77 }) }, { match: "UPDATE crawl_run", result: rows({ id: 5, run_type: "enrich" }) }, { match: "UPDATE job_triggers", result: rows({ id: 7, job_type: "enrich" }) }, // ---- Détail d'un groupe (avant les listes : fragment plus spécifique) ---- { match: "SELECT * FROM bands WHERE ma_id", result: (_s, v) => rows(bands.find((b) => b.ma_id === Number(v[0])) || bands[0]) }, { match: "UPDATE bands SET", result: (_s, v) => rows({ ...bands[0], ma_id: Number(v[0]), name: v[1] ?? bands[0].name }) }, // ---- Localisations ---- { match: "SELECT bl.id, bl.ma_id", result: rows(...locations) }, { match: "count(*)::int AS total\n FROM band_locations", result: rows({ total: locations.length }) }, { match: "SELECT * FROM band_locations", result: rows(locations[0]) }, { match: "UPDATE band_locations", result: rows({ ...locations[0], lat: 59.79, lon: 10.8, geocode_status: "done" }) }, { match: "FROM band_locations\n WHERE ma_id", result: rows(locations[0]) }, // ---- Statistiques ---- { match: "never_enriched", result: rows({ total: 1950, enriched: 1200, not_enriched: 750, geocoded: 1840, not_geocoded: 110, crawled_by_current_system: 1900, never_enriched: neverEnriched, stale: 4 }) }, { match: "new_bands", result: rows({ new_bands: 3, modified_since_enrich: 1, legacy_pending: 0, stale: 4 }) }, { match: "run_type = 'enrich'", result: rows() }, { match: "AS status, count(*)::int AS total", result: rows({ status: "Active", total: 1500 }, { status: "Split-up", total: 450 }) }, { match: "AS country, count", result: rows({ country: "NO", total: 900 }, { country: "FR", total: 400 }) }, { match: "SELECT genre, count", result: rows({ genre: "Black Metal", total: 800 }) }, // ---- Supervision ---- { match: "WHERE status = 'running'", result: rows(...activeRuns) }, { match: "WHERE status = 'pending'", result: rows(...pendingJobs) }, { match: "geocode_status = 'processing'", result: rows() }, { match: "GROUP BY geocode_status", result: rows(...geoStatuses) }, { match: "FROM service_health", result: rows( { service: "crawler", ok: true, checks: { database: true, flaresolverr: true }, error: null, checked_at: "2026-08-18T14:00:00Z", stale: false }, { service: "geocoder", ok: true, checks: { database: true, geoapify_joignable: true }, error: null, checked_at: "2026-08-18T14:00:00Z", stale: false }) }, { match: "FROM crawl_checkpoint", result: rows({ key: "last_full_crawl_at", value: "2026-08-01", updated_at: "2026-08-01T00:00:00Z" }) }, // ---- Géocodage / LLM ---- { match: "FROM geocode_cache", result: rows({ n: 1500 }) }, { match: "b.geocoded_at IS NOT NULL", result: rows() }, { match: "sum(cost_usd)", result: rows({ n: 320, total_cost_usd: "0.041200", n_null: 18 }) }, { match: "COALESCE(geocode_provider", result: rows({ provider: "geoapify", n: 1800, avg_conf: "0.91" }) }, { match: "SELECT model,", result: rows({ model: "llama-3.3-70b-versatile", n: 320, n_null: 18, cost: "0.041200" }) }, { match: "FROM llm_cache lc", result: rows({ id: 3, ma_id: 3, band_name: "Gojira", model: "llama-3.3-70b-versatile", location_raw: "Ondres / Bayonne", country: "FR", parsed_city: "Bayonne", parsed_country: "FR", is_null: false, tokens_in: 180, tokens_out: 12, cost_usd: "0.000090", prompt: "Extract the city…", response: '{"city":"Bayonne"}', created_at: "2026-08-12T08:00:00Z" }) }, // ---- Journal ---- { match: "UNION ALL", result: (sql) => sql.includes("count(*)") ? rows({ total: 2 }) : rows( { type: "run", id: 5, subtype: "enrich", status: "done", actor: null, ts: "2026-08-12T07:00:00Z", finished_at: "2026-08-12T07:30:00Z", summary: { bands_seen: 500, bands_new: 12, bands_updated: 30, bands_enriched: 480, error: null, countries: null } }, { type: "admin_action", id: 2, subtype: "update", status: "done", actor: "nico", ts: "2026-08-12T06:00:00Z", finished_at: "2026-08-12T06:00:00Z", summary: { target_table: "bands", target_id: "1", before: { name: "Mayhem" }, after: { name: "Mayhem" } } }) }, { match: "FROM crawl_log", result: rows({ id: 1, run_id: 5, level: "info", message: "enrich done", ma_id: null, created_at: "2026-08-12T07:30:00Z" }) }, // ---- Liste des groupes (fragments génériques : en dernier) ---- { match: "SELECT ma_id, name, country", result: rows(...bands) }, { match: "count(*)::int AS total FROM bands", result: rows({ total: bands.length }) }, { match: "FROM llm_cache", result: rows() }, ]; }