// Gate-B re-run HIGH — dsar_purge_jobs admin/auditor read isolation [DIRECT-DEV].
//
// dsar_purge_jobs is RLS-EXEMPT (the worker claims jobs cross-tenant under an empty GUC). That
// exemption is correct for the worker, but the admin/auditor READ helpers had no tenant filter, so
// a tenant-A admin could read tenant-B's PDPO data-subject erasure records. The helpers now REQUIRE
// the caller's tenantId and filter on it (app-level isolation, since the table carries no RLS). This
// proves tenant A's jobs are invisible to tenant B and a cross-tenant id lookup returns null.
//
// Requires DATABASE_URL + boot secrets.

import { test, describe, before, after } from "node:test";
import assert from "node:assert/strict";
import { sql } from "drizzle-orm";
import { auditDb } from "../server/db.js";
import { listDsarPurgeJobs, getDsarPurgeJob, getDsarPurgeJobsForDsar } from "../server/lib/dsar-purge-worker.js";

const RUN = `dsarpiso-${Date.now()}`;
const TENANT_A = `${RUN}-A`;
const TENANT_B = `${RUN}-B`;
const JOB_A = `${RUN}-jobA`;
const JOB_B = `${RUN}-jobB`;
const DSAR_A = `${RUN}-dsarA`;
const DSAR_B = `${RUN}-dsarB`;

async function seedJob(id: string, dsarId: string, tenantId: string, customerRef: string) {
  await auditDb.execute(sql`
    INSERT INTO dsar_purge_jobs (id, job_ref, dsar_id, customer_ref, status, enqueued_by, tenant_id)
    VALUES (${id}, ${id}, ${dsarId}, ${customerRef}, 'pending', 'tester', ${tenantId})`);
}

describe("Gate-B HIGH — dsar_purge_jobs read helpers are tenant-scoped", () => {
  before(async () => {
    await seedJob(JOB_A, DSAR_A, TENANT_A, `${RUN}-custA`);
    await seedJob(JOB_B, DSAR_B, TENANT_B, `${RUN}-custB`);
  });
  after(async () => {
    try { await auditDb.execute(sql`DELETE FROM dsar_purge_jobs WHERE id LIKE ${RUN + "%"}`); } catch { /* throwaway DB */ }
  });

  test("listDsarPurgeJobs(tenantA) returns tenant A's job and NOT tenant B's", async () => {
    const jobs = await listDsarPurgeJobs(TENANT_A);
    const ids = jobs.map((j) => j.id);
    assert.ok(ids.includes(JOB_A), "tenant A must see its own purge job");
    assert.ok(!ids.includes(JOB_B), "tenant A must NOT see tenant B's purge job (cross-tenant disclosure closed)");
  });

  test("getDsarPurgeJob(tenantB-jobId, tenantA) returns null (cross-tenant lookup denied)", async () => {
    const foreign = await getDsarPurgeJob(JOB_B, TENANT_A);
    assert.equal(foreign, null, "a tenant-B job id under tenant-A must resolve to null, not disclose the row");
    const own = await getDsarPurgeJob(JOB_A, TENANT_A);
    assert.ok(own && own.job.id === JOB_A, "tenant A must still resolve its own job (positive control)");
  });

  test("getDsarPurgeJobsForDsar is tenant-scoped too", async () => {
    const crossed = await getDsarPurgeJobsForDsar(DSAR_B, TENANT_A);
    assert.equal(crossed.length, 0, "tenant A querying tenant B's dsarId must get zero rows");
    const own = await getDsarPurgeJobsForDsar(DSAR_A, TENANT_A);
    assert.ok(own.length >= 1, "tenant A must see its own dsar's purge jobs");
  });
});
