Source: database/repositories/baselineRepo.js

/**
 * @module database/repositories/baselineRepo
 * @description Visual regression baseline CRUD (DIF-001).
 *
 * Baselines are the "golden" screenshots that subsequent runs diff against.
 * Each row references a PNG on disk under `artifacts/baselines/<testId>/`.
 * Rows are identified by `(testId, stepNumber, browser)` — `stepNumber = 0`
 * is the final end-of-test screenshot, `stepNumber >= 1` are per-step
 * captures (DIF-016).
 */

import { getDatabase } from "../sqlite.js";

/**
 * Get a baseline entry for a specific test + step.
 *
 * @param {string} testId
 * @param {number} [stepNumber=0]
 * @param {string} [browser="chromium"]
 * @returns {Object|undefined}
 */
export function get(testId, stepNumber = 0, browser = "chromium") {
  const db = getDatabase();
  return db.prepare(
    "SELECT * FROM baseline_screenshots WHERE testId = ? AND stepNumber = ? AND browser = ?"
  ).get(testId, stepNumber, browser) || undefined;
}

/**
 * List all baselines for a test, ordered by stepNumber.
 *
 * @param {string} testId
 * @param {string} [browser]
 * @returns {Array<Object>}
 */
export function getAllByTestId(testId, browser) {
  const db = getDatabase();
  if (browser) {
    return db.prepare(
      "SELECT * FROM baseline_screenshots WHERE testId = ? AND browser = ? ORDER BY stepNumber ASC"
    ).all(testId, browser);
  }
  return db.prepare(
    "SELECT * FROM baseline_screenshots WHERE testId = ? ORDER BY browser ASC, stepNumber ASC"
  ).all(testId);
}

/**
 * Upsert a baseline row. Creates on first call, updates `updatedAt` on
 * subsequent calls (used when the user accepts visual changes).
 *
 * @param {Object} entry
 * @param {string} entry.testId
 * @param {number} entry.stepNumber
 * @param {string} [entry.browser]
 * @param {string} entry.imagePath   - Relative artifact path (e.g. `/artifacts/baselines/TC-1/step-0.png`).
 * @param {number} [entry.width]
 * @param {number} [entry.height]
 * @returns {void}
 */
export function upsert(entry) {
  const db = getDatabase();
  const now = new Date().toISOString();
  db.prepare(`
    INSERT INTO baseline_screenshots (testId, stepNumber, browser, imagePath, width, height, createdAt, updatedAt)
    VALUES (@testId, @stepNumber, @browser, @imagePath, @width, @height, @createdAt, @updatedAt)
    ON CONFLICT(testId, stepNumber, browser) DO UPDATE SET
      imagePath = @imagePath,
      width = @width,
      height = @height,
      updatedAt = @updatedAt
  `).run({
    testId: entry.testId,
    stepNumber: entry.stepNumber ?? 0,
    browser: entry.browser || "chromium",
    imagePath: entry.imagePath,
    width: entry.width ?? null,
    height: entry.height ?? null,
    createdAt: now,
    updatedAt: now,
  });
}

/**
 * Delete all baselines for a test (invoked when a test is hard-deleted).
 *
 * @param {string} testId
 * @returns {number} rows deleted
 */
export function deleteByTestId(testId) {
  const db = getDatabase();
  const info = db.prepare("DELETE FROM baseline_screenshots WHERE testId = ?").run(testId);
  return info.changes || 0;
}

/**
 * Delete a single baseline row for a specific test + step + browser.
 *
 * @param {string} testId
 * @param {number} stepNumber
 * @param {string} [browser="chromium"]
 * @returns {number} rows deleted
 */
export function deleteOne(testId, stepNumber, browser = "chromium") {
  const db = getDatabase();
  const info = db.prepare(
    "DELETE FROM baseline_screenshots WHERE testId = ? AND stepNumber = ? AND browser = ?"
  ).run(testId, stepNumber, browser);
  return info.changes || 0;
}