import assert from "node:assert/strict";
import { mkdtemp, readFile, readdir, rm } from "node:fs/promises";
import { tmpdir } from "node:os";
import { join } from "node:path";
import test from "node:test";
import { createFoundryDataProductConsumerManager } from "./foundry-data-product-consumer.mjs";

const target = {
  application: { id: "11111111-1111-4111-8111-111111111111" },
  page: { id: "map" },
  binding: {
    id: "fleet-current",
    dataProductId: "fleet.positions.current.v1",
    slotId: "points",
    delivery: "snapshot+patch",
    semanticTypes: ["map.moving_object"],
    fieldProjection: ["display_name", "operational_status"],
  },
};

function fact({ longitude = 37.61, observedAt = "2026-07-19T10:00:00.000Z" } = {}) {
  return {
    sourceId: "vehicle-001",
    semanticType: "map.moving_object",
    observedAt,
    receivedAt: observedAt,
    attributes: { display_name: "Vehicle 001", operational_status: "active" },
    geometry: { type: "Point", coordinates: [longitude, 55.75] },
  };
}

function snapshot(cursor = "7", facts = [fact()]) {
  return {
    schemaVersion: "nodedc.data-product.snapshot/v1",
    dataProduct: { id: "fleet.positions.current.v1", version: "1.0.0" },
    generatedAt: "2026-07-19T10:00:01.000Z",
    cursor,
    facts,
  };
}

function patch(cursor, previousCursor, operations) {
  return {
    schemaVersion: "nodedc.data-product.patch/v1",
    dataProduct: { id: "fleet.positions.current.v1", version: "1.0.0" },
    cursor,
    previousCursor,
    emittedAt: "2026-07-19T10:00:02.000Z",
    operations,
  };
}

function sseResponse(value, signal, counters) {
  const encoder = new TextEncoder();
  return new Response(new ReadableStream({
    start(controller) {
      counters.open += 1;
      controller.enqueue(encoder.encode(`event: nodedc.data-product.patch.v1\ndata: ${JSON.stringify(value)}\n\n`));
      const close = () => {
        counters.closed += 1;
        try { controller.close(); } catch { /* already closed */ }
      };
      signal.addEventListener("abort", close, { once: true });
    },
  }), { status: 200, headers: { "content-type": "text/event-stream" } });
}

async function waitUntil(check, label, timeoutMs = 2_000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    if (await check()) return;
    await new Promise((resolve) => setTimeout(resolve, 10));
  }
  throw new Error(`timeout:${label}`);
}

test("server-owned consumer persists cursor, shares streams, transitions stale and removes canonically", async () => {
  const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-"));
  let nowMs = Date.parse("2026-07-19T10:00:10.000Z");
  let currentSnapshot = snapshot();
  const streamPatches = [
    patch("8", "7", [{ op: "upsert", fact: fact({ longitude: 37.62 }) }]),
    patch("9", "8", [{
      op: "remove",
      sourceId: "vehicle-001",
      semanticType: "map.moving_object",
      removedAt: "2026-07-19T10:01:30.000Z",
      reason: "tombstone",
    }]),
  ];
  const counters = { catalog: 0, snapshot: 0, stream: 0, open: 0, closed: 0 };
  const fetchImpl = async (input, options = {}) => {
    const url = new URL(input);
    assert.equal(options.headers.authorization, "Bearer ndc_edprb_test-reader-capability");
    assert.equal(url.origin, "http://edp.test");
    if (url.pathname.endsWith("/reader/data-products")) {
      counters.catalog += 1;
      return Response.json({
        ok: true,
        dataProducts: [{
          id: "fleet.positions.current.v1",
          version: "1.0.0",
          deliveryMode: "snapshot+patch",
          semanticTypes: ["map.moving_object"],
          active: true,
        }],
      });
    }
    if (url.pathname.endsWith("/snapshot")) {
      counters.snapshot += 1;
      return Response.json(currentSnapshot);
    }
    if (url.pathname.endsWith("/stream")) {
      counters.stream += 1;
      const next = streamPatches.shift();
      assert.ok(next, "unexpected extra upstream stream");
      assert.equal(url.searchParams.get("after"), next.previousCursor);
      return sseResponse(next, options.signal, counters);
    }
    throw new Error(`unexpected_url:${url}`);
  };
  const sanitizeSnapshot = (value) => value;
  const sanitizePatch = (value) => value;
  const manager = createFoundryDataProductConsumerManager({
    stateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => target,
    readReaderToken: async () => "ndc_edprb_test-reader-capability",
    resolvePolicy: (product) => ({
      id: "map-moving-object-current-v1",
      version: "1.0.0",
      dataProductId: product.id,
      productVersion: product.version,
      staleAfterMs: 60_000,
      terminalStatuses: ["inactive", "no-position", "no_position"],
      removeMode: "canonical-tombstone-or-snapshot-rebase",
    }),
    sanitizeSnapshot,
    sanitizePatch,
    fetchImpl,
    now: () => nowMs,
    idleStopMs: 20,
    staleSweepMs: 10_000,
    reconnectMinMs: 10,
    reconnectMaxMs: 20,
  });

  try {
    const input = { applicationId: target.application.id, pageId: target.page.id, bindingId: target.binding.id };
    const plan = await manager.plan(input);
    assert.equal(plan.action, "create");
    assert.match(plan.planId, /^fcp1_/);
    assert.equal(JSON.stringify(plan).includes("ndc_edprb_"), false);
    assert.equal(JSON.stringify(plan).includes("http://edp.test"), false);

    const applied = await manager.apply({ ...input, planId: plan.planId });
    assert.equal(applied.consumer.cursor, "7");
    assert.equal(applied.consumer.subjectCount, 1);
    assert.equal(applied.consumer.runtimeState, "idle");

    const events = [];
    const first = await manager.subscribe({ ...target, after: "7" }, (event) => events.push(event));
    const second = await manager.subscribe({ ...target, after: "7" }, (event) => events.push(event));
    await waitUntil(async () => (await manager.status(input)).consumer.cursor === "8", "patch_cursor_8");
    assert.equal(counters.stream, 1, "two viewers must share one upstream stream");
    assert.equal((await manager.status(input)).consumer.subjectCount, 1);
    assert.equal(events.filter((event) => event.event === "nodedc.data-product.patch.v1").length, 2, "one committed patch is fanned out once per viewer");

    nowMs = Date.parse("2026-07-19T10:01:30.001Z");
    const stale = await manager.status(input);
    assert.equal(stale.consumer.subjects[0].status, "stale");
    assert.equal(stale.consumer.metrics.staleTransitions, 1);

    first.release();
    second.release();
    await waitUntil(() => counters.closed === 1, "last_viewer_closes_upstream");

    const removeEvents = [];
    const third = await manager.subscribe({ ...target, after: "8" }, (event) => removeEvents.push(event));
    await waitUntil(async () => (await manager.status(input)).consumer.cursor === "9", "remove_cursor_9");
    assert.equal((await manager.status(input)).consumer.subjectCount, 0);
    assert.equal((await manager.status(input)).consumer.metrics.canonicalRemovals, 1);
    assert.ok(removeEvents.some((event) => event.data?.operations?.some((operation) => operation.op === "remove")));
    third.release();
    await waitUntil(() => counters.closed === 2, "remove_stream_closed");

    currentSnapshot = snapshot("9", []);
    const refreshPlan = await manager.plan(input);
    await manager.apply({ ...input, planId: refreshPlan.planId });
    const empty = await manager.snapshot(target);
    assert.deepEqual(empty.facts, []);

    const rolledBack = await manager.rollback(input);
    assert.equal(rolledBack.snapshotPreserved, true);
    assert.equal(rolledBack.consumer.runtimeState, "stopped");
    await assert.rejects(() => manager.snapshot(target), /data_product_consumer_stopped/);

    const stateFiles = await readdir(stateDir);
    assert.equal(stateFiles.length, 1);
    const persisted = await readFile(join(stateDir, stateFiles[0]), "utf8");
    assert.equal(persisted.includes("ndc_edprb_"), false);
    assert.equal(persisted.includes("http://edp.test"), false);
  } finally {
    await manager.shutdown();
    await rm(stateDir, { recursive: true, force: true });
  }
});

test("restart restores the committed cursor and transient stream errors preserve subjects", async () => {
  const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-restart-"));
  const input = { applicationId: target.application.id, pageId: target.page.id, bindingId: target.binding.id };
  let streamAttempts = 0;
  const fetchImpl = async (request, options = {}) => {
    const url = new URL(request);
    assert.equal(options.headers.authorization, "Bearer ndc_edprb_test-reader-capability");
    if (url.pathname.endsWith("/reader/data-products")) {
      return Response.json({ ok: true, dataProducts: [{
        id: "fleet.positions.current.v1",
        version: "1.0.0",
        deliveryMode: "snapshot+patch",
        semanticTypes: ["map.moving_object"],
        active: true,
      }] });
    }
    if (url.pathname.endsWith("/snapshot")) return Response.json(snapshot());
    if (url.pathname.endsWith("/stream")) {
      streamAttempts += 1;
      return Response.json({ ok: false, error: "temporary_failure" }, { status: 503 });
    }
    throw new Error(`unexpected_url:${url}`);
  };
  const createManager = () => createFoundryDataProductConsumerManager({
    stateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => target,
    readReaderToken: async () => "ndc_edprb_test-reader-capability",
    resolvePolicy: (product) => ({
      id: "map-moving-object-current-v1",
      version: "1.0.0",
      dataProductId: product.id,
      productVersion: product.version,
      staleAfterMs: 60_000,
      terminalStatuses: ["inactive", "no-position", "no_position"],
      removeMode: "canonical-tombstone-or-snapshot-rebase",
    }),
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
    fetchImpl,
    now: () => Date.parse("2026-07-19T10:00:10.000Z"),
    idleStopMs: 10,
    staleSweepMs: 10_000,
    reconnectMinMs: 10,
    reconnectMaxMs: 20,
  });
  const first = createManager();
  let second;
  try {
    const plan = await first.plan(input);
    await first.apply({ ...input, planId: plan.planId });
    await first.shutdown();

    second = createManager();
    await second.resumePersisted();
    const restored = await second.status(input);
    assert.equal(restored.consumer.cursor, "7");
    assert.equal(restored.consumer.subjectCount, 1);
    const lease = await second.subscribe({ ...target, after: "7" }, () => undefined);
    await waitUntil(async () => (await second.status(input)).consumer.metrics.reconnects > 0, "transient_reconnect");
    lease.release();
    const afterFailure = await second.status(input);
    assert.equal(afterFailure.consumer.subjectCount, 1);
    assert.equal(afterFailure.consumer.cursor, "7");
    assert.ok(streamAttempts >= 1);
  } finally {
    await first.shutdown();
    await second?.shutdown();
    await rm(stateDir, { recursive: true, force: true });
  }
});

test("exact apply can ensure a missing target-scoped reader grant without exposing it in the plan", async () => {
  const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-grant-"));
  const input = { applicationId: target.application.id, pageId: target.page.id, bindingId: target.binding.id };
  const product = {
    id: "fleet.positions.current.v1",
    version: "1.0.0",
    deliveryMode: "snapshot+patch",
    semanticTypes: ["map.moving_object"],
    active: true,
  };
  let grantReady = false;
  let ensureCount = 0;
  const manager = createFoundryDataProductConsumerManager({
    stateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => target,
    readReaderToken: async () => grantReady ? "ndc_edprb_managed-reader-capability" : null,
    inspectReaderGrant: async () => ({ product, readerGrantAction: grantReady ? "reuse" : "ensure" }),
    ensureReaderGrant: async () => {
      ensureCount += 1;
      grantReady = true;
      return { ensured: true };
    },
    resolvePolicy: (resolvedProduct) => ({
      id: "map-moving-object-current-v1",
      version: "1.0.0",
      dataProductId: resolvedProduct.id,
      productVersion: resolvedProduct.version,
      staleAfterMs: 60_000,
      terminalStatuses: ["inactive", "no-position", "no_position"],
      removeMode: "canonical-tombstone-or-snapshot-rebase",
    }),
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
    fetchImpl: async (request, options = {}) => {
      assert.equal(options.headers.authorization, "Bearer ndc_edprb_managed-reader-capability");
      assert.ok(new URL(request).pathname.endsWith("/snapshot"));
      return Response.json(snapshot());
    },
  });
  try {
    const plan = await manager.plan(input);
    assert.equal(plan.configuration.readerGrantAction, "ensure");
    assert.ok(plan.effects.includes("ensure-target-scoped-reader-grant"));
    assert.equal(JSON.stringify(plan).includes("capability"), false);
    const applied = await manager.apply({ ...input, planId: plan.planId });
    assert.equal(ensureCount, 1);
    assert.equal(applied.consumer.subjectCount, 1);
    const refresh = await manager.plan(input);
    assert.equal(refresh.configuration.readerGrantAction, "reuse");
    assert.equal(refresh.effects.includes("ensure-target-scoped-reader-grant"), false);
  } finally {
    await manager.shutdown();
    await rm(stateDir, { recursive: true, force: true });
  }
});

test("Data Product replacement bootstraps a successor reader generation before revoking its predecessor", async () => {
  const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-generation-"));
  const input = { applicationId: target.application.id, pageId: target.page.id, bindingId: target.binding.id };
  let currentTarget = structuredClone(target);
  let failV2Snapshot = true;
  let failPredecessorRevoke = true;
  const grants = new Map([[1, "fleet.positions.current.v1"]]);
  const events = [];
  const product = (id) => ({
    id,
    version: id.endsWith(".v2") ? "2.0.0" : "1.0.0",
    deliveryMode: "snapshot+patch",
    semanticTypes: ["map.moving_object"],
    active: true,
  });
  const manager = createFoundryDataProductConsumerManager({
    stateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => structuredClone(currentTarget),
    readReaderToken: async (_applicationId, _pageId, _bindingId, generation = 1) => {
      return grants.has(generation) ? `ndc_edprb_generation-${generation}` : null;
    },
    inspectReaderGrant: async (resolvedTarget, { generation = 1 } = {}) => {
      const desiredProductId = resolvedTarget.binding.dataProductId;
      return {
        product: product(desiredProductId),
        readerGrantAction: grants.get(generation) === desiredProductId ? "reuse" : "ensure",
        readerGrantGeneration: generation,
      };
    },
    ensureReaderGrant: async (resolvedTarget, { generation }) => {
      grants.set(generation, resolvedTarget.binding.dataProductId);
      events.push(`ensure-g${generation}`);
      return { ensured: true, generation };
    },
    revokeReaderGrant: async (_resolvedTarget, { generation }) => {
      if (generation === 1 && failPredecessorRevoke) {
        failPredecessorRevoke = false;
        events.push("revoke-g1-failed");
        throw Object.assign(new Error("foundry_reader_grant_provisioner_unavailable"), { statusCode: 503 });
      }
      grants.delete(generation);
      events.push(`revoke-g${generation}`);
      return { revoked: true, generation };
    },
    resolvePolicy: (resolvedProduct) => ({
      id: resolvedProduct.id.endsWith(".v2") ? "map-moving-object-current-v2" : "map-moving-object-current-v1",
      version: resolvedProduct.version,
      dataProductId: resolvedProduct.id,
      productVersion: resolvedProduct.version,
      staleAfterMs: 60_000,
      terminalStatuses: ["inactive", "no-position", "no_position"],
      removeMode: "canonical-tombstone-or-snapshot-rebase",
    }),
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
    fetchImpl: async (request, options = {}) => {
      const dataProductId = decodeURIComponent(new URL(request).pathname.split("/").at(-2));
      const generation = Number(String(options.headers.authorization).match(/generation-(\d+)/)?.[1]);
      assert.equal(grants.get(generation), dataProductId);
      events.push(`snapshot-${dataProductId}-g${generation}`);
      if (dataProductId.endsWith(".v2") && failV2Snapshot) {
        throw Object.assign(new Error("data_product_runtime_unavailable"), { statusCode: 503 });
      }
      return Response.json({
        ...snapshot(dataProductId.endsWith(".v2") ? "20" : "10"),
        dataProduct: { id: dataProductId, version: dataProductId.endsWith(".v2") ? "2.0.0" : "1.0.0" },
      });
    },
    idleStopMs: 10,
    staleSweepMs: 10_000,
  });
  try {
    const createPlan = await manager.plan(input);
    assert.equal(createPlan.configuration.readerGrantGeneration, 1);
    const created = await manager.apply({ ...input, planId: createPlan.planId });
    assert.equal(created.consumer.product.id, "fleet.positions.current.v1");
    assert.equal(created.consumer.readerGrantGeneration, 1);

    currentTarget.binding.dataProductId = "fleet.positions.current.v2";
    currentTarget.binding.fieldProjection = [
      "display_name", "availability_state", "motion_state", "position_state", "freshness_state",
    ];
    const replacePlan = await manager.plan(input);
    assert.equal(replacePlan.action, "replace");
    assert.equal(replacePlan.configuration.readerGrantAction, "ensure");
    assert.equal(replacePlan.configuration.readerGrantGeneration, 2);
    assert.ok(replacePlan.effects.includes("ensure-successor-target-scoped-reader-grant"));
    await assert.rejects(
      manager.apply({ ...input, planId: replacePlan.planId }),
      /data_product_runtime_unavailable/,
    );
    const preserved = await manager.status(input);
    assert.equal(preserved.consumer.product.id, "fleet.positions.current.v1");
    assert.equal(preserved.consumer.readerGrantGeneration, 1);
    assert.equal(grants.get(1), "fleet.positions.current.v1");
    assert.equal(grants.get(2), "fleet.positions.current.v2");
    assert.equal(events.includes("revoke-g1"), false);

    failV2Snapshot = false;
    const retryPlan = await manager.plan(input);
    assert.equal(retryPlan.configuration.readerGrantGeneration, 2);
    await assert.rejects(
      manager.apply({ ...input, planId: retryPlan.planId }),
      /foundry_reader_grant_provisioner_unavailable/,
    );
    const pendingFinalization = await manager.status(input);
    assert.equal(pendingFinalization.consumer.product.id, "fleet.positions.current.v2");
    assert.equal(pendingFinalization.consumer.readerGrantGeneration, 2);
    assert.equal(grants.has(1), true);
    const finalizePlan = await manager.plan(input);
    assert.equal(finalizePlan.action, "finalize-reader-grant-rotation");
    assert.equal(finalizePlan.effects.includes("bootstrap-scoped-snapshot"), false);
    assert.ok(finalizePlan.effects.includes("revoke-predecessor-reader-grant"));
    const replaced = await manager.apply({ ...input, planId: finalizePlan.planId });
    assert.equal(replaced.consumer.product.id, "fleet.positions.current.v2");
    assert.equal(replaced.consumer.readerGrantGeneration, 2);
    assert.equal(grants.has(1), false);
    assert.equal(grants.get(2), "fleet.positions.current.v2");
    assert.ok(events.lastIndexOf("snapshot-fleet.positions.current.v2-g2") < events.indexOf("revoke-g1"));
  } finally {
    await manager.shutdown();
    await rm(stateDir, { recursive: true, force: true });
  }
});

const movingObjectV4Policy = {
  id: "map-moving-object-current-v4",
  version: "4.0.0",
  dataProductId: "fleet.positions.current.v4",
  productVersion: "4.0.0",
  staleAfterMs: null,
  terminalStatuses: ["inactive"],
  statusContract: {
    attribute: "signal_state",
    allowedValues: ["active", "inactive"],
    missing: "reject",
    freshness: "none",
  },
  removeMode: "canonical-tombstone-or-snapshot-rebase",
};

const movingObjectV4Product = {
  id: "fleet.positions.current.v4",
  version: "4.0.0",
  deliveryMode: "snapshot+patch",
  semanticTypes: ["map.moving_object"],
  active: true,
};

function movingObjectV4Target(fieldProjection = ["display_name", "signal_state", "movement_state", "speed_kph"]) {
  return {
    application: { id: "44444444-4444-4444-8444-444444444444" },
    page: { id: "map" },
    binding: {
      id: "fleet-current-v4",
      dataProductId: movingObjectV4Product.id,
      slotId: "points",
      delivery: "snapshot+patch",
      semanticTypes: ["map.moving_object"],
      fieldProjection,
    },
  };
}

function movingObjectV4Snapshot(signalState = "active") {
  return {
    schemaVersion: "nodedc.data-product.snapshot/v1",
    dataProduct: { id: movingObjectV4Product.id, version: movingObjectV4Product.version },
    generatedAt: "2026-07-20T12:00:01.000Z",
    cursor: "40",
    facts: [{
      sourceId: "gelios-unit-001",
      semanticType: "map.moving_object",
      observedAt: "2026-01-01T00:00:00.000Z",
      receivedAt: "2026-07-20T12:00:00.000Z",
      attributes: {
        display_name: "Unit 001",
        signal_state: signalState,
        movement_state: "stopped",
        speed_kph: 0,
      },
      geometry: { type: "Point", coordinates: [37.61, 55.75] },
    }],
  };
}

test("v4 consumer preserves the closed signal and movement states without inventing freshness", async () => {
  const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-v4-"));
  const currentTarget = movingObjectV4Target();
  let signalState = "active";
  const manager = createFoundryDataProductConsumerManager({
    stateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => structuredClone(currentTarget),
    readReaderToken: async () => "ndc_edprb_v4-reader-capability",
    inspectReaderGrant: async () => ({ product: movingObjectV4Product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
    resolvePolicy: () => movingObjectV4Policy,
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
    fetchImpl: async (request, options = {}) => {
      assert.equal(options.headers.authorization, "Bearer ndc_edprb_v4-reader-capability");
      assert.ok(new URL(request).pathname.endsWith("/snapshot"));
      return Response.json(movingObjectV4Snapshot(signalState));
    },
    now: () => Date.parse("2026-07-20T12:00:10.000Z"),
  });
  try {
    const input = { applicationId: currentTarget.application.id, pageId: currentTarget.page.id, bindingId: currentTarget.binding.id };
    const plan = await manager.plan(input);
    assert.deepEqual(plan.configuration.policy.statusContract.allowedValues, ["active", "inactive"]);
    assert.equal(plan.configuration.policy.staleAfterMs, null);
    const active = await manager.apply({ ...input, planId: plan.planId });
    assert.equal(active.consumer.subjects[0].status, "active");
    assert.equal(active.consumer.metrics.staleTransitions, 0);
    assert.equal((await manager.snapshot(currentTarget)).facts[0].attributes.movement_state, "stopped");

    signalState = "inactive";
    const refreshPlan = await manager.plan(input);
    const inactive = await manager.apply({ ...input, planId: refreshPlan.planId });
    assert.equal(inactive.consumer.subjects[0].status, "inactive");
    assert.equal(inactive.consumer.metrics.staleTransitions, 0);
  } finally {
    await manager.shutdown();
    await rm(stateDir, { recursive: true, force: true });
  }
});

test("v4 consumer rejects bindings that omit signal_state and snapshots outside the closed contract", async () => {
  const omittedStateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-v4-omitted-"));
  const omittedTarget = movingObjectV4Target(["display_name", "movement_state"]);
  const omittedManager = createFoundryDataProductConsumerManager({
    stateDir: omittedStateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => structuredClone(omittedTarget),
    readReaderToken: async () => "ndc_edprb_v4-reader-capability",
    inspectReaderGrant: async () => ({ product: movingObjectV4Product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
    resolvePolicy: () => movingObjectV4Policy,
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
  });
  try {
    await assert.rejects(
      omittedManager.plan({ applicationId: omittedTarget.application.id, pageId: omittedTarget.page.id, bindingId: omittedTarget.binding.id }),
      /data_product_consumer_status_field_not_projected/,
    );
  } finally {
    await omittedManager.shutdown();
    await rm(omittedStateDir, { recursive: true, force: true });
  }

  const invalidStateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-v4-invalid-"));
  const invalidTarget = movingObjectV4Target();
  const invalidManager = createFoundryDataProductConsumerManager({
    stateDir: invalidStateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => structuredClone(invalidTarget),
    readReaderToken: async () => "ndc_edprb_v4-reader-capability",
    inspectReaderGrant: async () => ({ product: movingObjectV4Product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
    resolvePolicy: () => movingObjectV4Policy,
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
    fetchImpl: async () => Response.json(movingObjectV4Snapshot("unknown")),
  });
  try {
    const input = { applicationId: invalidTarget.application.id, pageId: invalidTarget.page.id, bindingId: invalidTarget.binding.id };
    const plan = await invalidManager.plan(input);
    await assert.rejects(
      invalidManager.apply({ ...input, planId: plan.planId }),
      /data_product_consumer_fact_status_invalid/,
    );
    const failed = await invalidManager.status(input);
    assert.equal(failed.consumer.runtimeState, "error");
    assert.equal(failed.consumer.subjectCount, 0);
  } finally {
    await invalidManager.shutdown();
    await rm(invalidStateDir, { recursive: true, force: true });
  }
});

test("consumer contracts preserve bounded string arrays for classified identity aspects", async () => {
  const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-identity-"));
  const fieldProjection = ["device_imei", "available_user_logins", "custom_fields"];
  const product = {
    id: "fleet.units.identity.current.v1",
    version: "1.0.0",
    ontologyRevision: "ontology.map.moving_object.v3",
    deliveryMode: "snapshot+patch",
    semanticTypes: ["map.moving_object"],
    fields: [...fieldProjection],
    fieldContracts: {
      device_imei: { type: "string", required: false },
      available_user_logins: { type: "string_array", required: false },
      custom_fields: { type: "string_array", required: false },
    },
    active: true,
  };
  const policy = {
    id: "map-moving-object-unit-identity-current-v1",
    version: "1.0.0",
    dataProductId: product.id,
    productVersion: product.version,
    dataClass: "restricted",
    freshness: "none",
    staleAfterMs: null,
    terminalStatuses: [],
    consumerContract: {
      ontologyRevision: product.ontologyRevision,
      deliveryMode: "snapshot+patch",
      semanticTypes: ["map.moving_object"],
      fieldProjection,
      fieldTypes: {
        device_imei: "string",
        available_user_logins: "string_array",
        custom_fields: "string_array",
      },
      geometryTypes: [],
      subjectIdentity: "semantic-type+source-id",
      snapshotMode: "atomic-replace",
      patchMode: "atomic",
    },
    removeMode: "canonical-tombstone-or-snapshot-rebase",
  };
  const target = {
    application: { id: "77777777-7777-4777-8777-777777777777" },
    page: { id: "map" },
    binding: {
      id: "trike-unit-identity",
      dataProductId: product.id,
      slotId: "identity",
      delivery: "snapshot+patch",
      dataClass: "restricted",
      semanticTypes: ["map.moving_object"],
      fieldProjection,
    },
  };
  let invalid = false;
  const manager = createFoundryDataProductConsumerManager({
    stateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => structuredClone(target),
    readReaderToken: async () => "ndc_edprb_identity-reader-capability",
    inspectReaderGrant: async () => ({ product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
    resolvePolicy: () => policy,
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
    fetchImpl: async () => Response.json({
      schemaVersion: "nodedc.data-product.snapshot/v1",
      dataProduct: { id: product.id, version: product.version },
      generatedAt: "2026-07-24T08:00:01.000Z",
      cursor: "1",
      facts: [{
        sourceId: "gelios-unit-5015",
        semanticType: "map.moving_object",
        observedAt: "2026-07-24T08:00:00.000Z",
        receivedAt: "2026-07-24T08:00:00.500Z",
        attributes: {
          device_imei: "867236078012345",
          available_user_logins: invalid ? "not-an-array" : ["vasya", "dispatcher"],
          custom_fields: ["Serial number=5015"],
        },
      }],
    }),
  });
  try {
    const input = {
      applicationId: target.application.id,
      pageId: target.page.id,
      bindingId: target.binding.id,
    };
    const plan = await manager.plan(input);
    await manager.apply({ ...input, planId: plan.planId });
    assert.deepEqual(
      (await manager.snapshot(target)).facts[0].attributes.available_user_logins,
      ["vasya", "dispatcher"],
    );
    assert.equal(plan.configuration.policy.dataClass, "restricted");
    assert.equal(plan.configuration.target.dataClass, "restricted");

    invalid = true;
    const invalidPlan = await manager.plan(input);
    await assert.rejects(
      manager.apply({ ...input, planId: invalidPlan.planId }),
      /data_product_consumer_fact_contract_invalid/,
    );
  } finally {
    await manager.shutdown();
    await rm(stateDir, { recursive: true, force: true });
  }
});

const zoneV2Projection = [
  "display_name",
  "geometry_kind",
  "max_speed_kph",
  "schedule_timezone",
  "applies_to_couriers",
  "applies_to_kicksharing",
];

const zoneV2Policy = {
  id: "map-zone-current-v2",
  version: "2.0.0",
  dataProductId: "map.zones.current.v2",
  productVersion: "2.0.0",
  freshness: "none",
  staleAfterMs: null,
  terminalStatuses: [],
  consumerContract: {
    ontologyRevision: "ontology.map.zone.v1",
    deliveryMode: "snapshot+patch",
    semanticTypes: ["map.zone"],
    fieldProjection: zoneV2Projection,
    fieldTypes: {
      display_name: "string",
      geometry_kind: "string",
      max_speed_kph: "number",
      schedule_timezone: "string",
      applies_to_couriers: "boolean",
      applies_to_kicksharing: "boolean",
    },
    geometryTypes: ["Polygon", "MultiPolygon"],
    subjectIdentity: "semantic-type+source-id",
    snapshotMode: "atomic-replace",
    patchMode: "atomic",
  },
  removeMode: "canonical-tombstone-or-snapshot-rebase",
};

const zoneV2Product = {
  id: "map.zones.current.v2",
  version: "2.0.0",
  ontologyRevision: "ontology.map.zone.v1",
  deliveryMode: "snapshot+patch",
  semanticTypes: ["map.zone"],
  fields: [...zoneV2Projection, "geometry", "source_revision"],
  fieldContracts: {
    display_name: { type: "string", required: true },
    geometry_kind: { type: "string", required: true },
    max_speed_kph: { type: "number", required: false },
    schedule_timezone: { type: "string", required: false },
    applies_to_couriers: { type: "boolean", required: false },
    applies_to_kicksharing: { type: "boolean", required: false },
    geometry: { type: "geometry", required: true },
  },
  active: true,
};

function zoneV2Target(fieldProjection = zoneV2Projection) {
  return {
    application: { id: "55555555-5555-4555-8555-555555555555" },
    page: { id: "map" },
    binding: {
      id: "depttrans-pmd-slow-zones",
      dataProductId: zoneV2Product.id,
      slotId: "zones",
      delivery: "snapshot+patch",
      semanticTypes: ["map.zone"],
      fieldProjection: [...fieldProjection],
    },
  };
}

function zoneFact({ sourceId = "depttrans-zone-001", geometryType = "Polygon", maxSpeedKph = 20 } = {}) {
  const polygon = [[[37.60, 55.75], [37.61, 55.75], [37.61, 55.76], [37.60, 55.75]]];
  return {
    sourceId,
    semanticType: "map.zone",
    observedAt: "2026-07-21T10:00:00.000Z",
    receivedAt: "2026-07-21T10:00:01.000Z",
    attributes: {
      display_name: "Slow zone 001",
      geometry_kind: "polygon",
      max_speed_kph: maxSpeedKph,
      schedule_timezone: "Europe/Moscow",
      applies_to_couriers: true,
      applies_to_kicksharing: true,
    },
    geometry: geometryType === "MultiPolygon"
      ? { type: "MultiPolygon", coordinates: [polygon] }
      : geometryType === "Polygon"
        ? { type: "Polygon", coordinates: polygon }
        : { type: geometryType, coordinates: [37.61, 55.75] },
  };
}

function zoneSnapshot({ cursor = "903", version = "2.0.0", facts = [zoneFact()] } = {}) {
  return {
    schemaVersion: "nodedc.data-product.snapshot/v1",
    dataProduct: { id: zoneV2Product.id, version },
    generatedAt: "2026-07-21T10:00:02.000Z",
    cursor,
    facts,
  };
}

function zonePatch(cursor, previousCursor, operations, version = "2.0.0") {
  return {
    schemaVersion: "nodedc.data-product.patch/v1",
    dataProduct: { id: zoneV2Product.id, version },
    cursor,
    previousCursor,
    emittedAt: "2026-07-21T10:00:03.000Z",
    operations,
  };
}

test("zone v2 consumer pins provider-neutral contract and commits Polygon/MultiPolygon patches by stable subject identity", async () => {
  const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-zone-v2-"));
  const currentTarget = zoneV2Target();
  const streamCounters = { open: 0, closed: 0 };
  const manager = createFoundryDataProductConsumerManager({
    stateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => structuredClone(currentTarget),
    readReaderToken: async () => "ndc_edprb_zone-reader-capability",
    inspectReaderGrant: async () => ({ product: zoneV2Product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
    resolvePolicy: () => zoneV2Policy,
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
    fetchImpl: async () => Response.json(zoneSnapshot()),
    openStream: async ({ signal }) => sseResponse(
      zonePatch("904", "903", [{ op: "upsert", fact: zoneFact({ geometryType: "MultiPolygon", maxSpeedKph: 15 }) }]),
      signal,
      streamCounters,
    ),
    now: () => Date.parse("2026-07-21T10:00:10.000Z"),
    idleStopMs: 10,
    reconnectMinMs: 10,
    reconnectMaxMs: 20,
  });
  try {
    const input = { applicationId: currentTarget.application.id, pageId: currentTarget.page.id, bindingId: currentTarget.binding.id };
    const plan = await manager.plan(input);
    assert.equal(plan.configuration.product.version, "2.0.0");
    assert.deepEqual(plan.configuration.policy.consumerContract.semanticTypes, ["map.zone"]);
    assert.deepEqual(plan.configuration.policy.consumerContract.fieldProjection, zoneV2Projection);
    assert.deepEqual(plan.configuration.policy.consumerContract.geometryTypes, ["Polygon", "MultiPolygon"]);
    assert.equal(JSON.stringify(plan).includes("http://edp.test"), false);
    assert.equal(JSON.stringify(plan).includes("ndc_edprb_"), false);

    const applied = await manager.apply({ ...input, planId: plan.planId });
    assert.equal(applied.consumer.cursor, "903");
    assert.equal(applied.consumer.subjectCount, 1);
    assert.equal(applied.consumer.subjects[0].subjectId, "depttrans-zone-001");
    assert.equal(applied.consumer.subjects[0].status, "active");
    assert.equal(applied.consumer.metrics.staleTransitions, 0);

    const lease = await manager.subscribe({ ...currentTarget, after: "903" }, () => undefined);
    await waitUntil(async () => (await manager.status(input)).consumer.cursor === "904", "zone_patch_cursor_904");
    const patched = await manager.snapshot(currentTarget);
    assert.equal(patched.facts.length, 1);
    assert.equal(patched.facts[0].sourceId, "depttrans-zone-001");
    assert.equal(patched.facts[0].geometry.type, "MultiPolygon");
    assert.equal(patched.facts[0].attributes.max_speed_kph, 15);
    assert.equal((await manager.status(input)).consumer.upstreamStreamCount, 1);
    lease.release();
    await waitUntil(() => streamCounters.closed === 1, "zone_stream_closed");
    await waitUntil(async () => (await manager.status(input)).consumer.runtimeState === "idle", "zone_stream_idle");
  } finally {
    await manager.shutdown();
    await rm(stateDir, { recursive: true, force: true, maxRetries: 5, retryDelay: 20 });
  }
});

test("zone v2 consumer fails closed on projection, product, envelope and geometry contract drift", async () => {
  const cases = [
    {
      name: "projection",
      target: zoneV2Target(zoneV2Projection.slice(0, -1)),
      product: zoneV2Product,
      policy: zoneV2Policy,
      snapshot: zoneSnapshot(),
      planError: /data_product_consumer_contract_mismatch/,
    },
    {
      name: "field-type",
      target: zoneV2Target(),
      product: { ...zoneV2Product, fieldContracts: { ...zoneV2Product.fieldContracts, max_speed_kph: { type: "string" } } },
      policy: zoneV2Policy,
      snapshot: zoneSnapshot(),
      planError: /data_product_consumer_contract_mismatch/,
    },
    {
      name: "product-version",
      target: zoneV2Target(),
      product: { ...zoneV2Product, version: "2.0.1" },
      policy: zoneV2Policy,
      snapshot: zoneSnapshot({ version: "2.0.1" }),
      planError: /data_product_consumer_policy_not_found/,
    },
    {
      name: "envelope-version",
      target: zoneV2Target(),
      product: zoneV2Product,
      policy: zoneV2Policy,
      snapshot: zoneSnapshot({ version: "2.0.1" }),
      applyError: /data_product_snapshot_product_mismatch/,
    },
    {
      name: "point-geometry",
      target: zoneV2Target(),
      product: zoneV2Product,
      policy: zoneV2Policy,
      snapshot: zoneSnapshot({ facts: [zoneFact({ geometryType: "Point" })] }),
      applyError: /data_product_consumer_fact_contract_invalid/,
    },
  ];

  for (const variant of cases) {
    const stateDir = await mkdtemp(join(tmpdir(), `foundry-consumer-zone-v2-${variant.name}-`));
    const manager = createFoundryDataProductConsumerManager({
      stateDir,
      dataPlaneUrl: "http://edp.test",
      resolveTarget: async () => structuredClone(variant.target),
      readReaderToken: async () => "ndc_edprb_zone-reader-capability",
      inspectReaderGrant: async () => ({ product: variant.product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
      resolvePolicy: () => variant.policy,
      sanitizeSnapshot: (value) => value,
      sanitizePatch: (value) => value,
      fetchImpl: async () => Response.json(variant.snapshot),
    });
    const input = { applicationId: variant.target.application.id, pageId: variant.target.page.id, bindingId: variant.target.binding.id };
    try {
      if (variant.planError) {
        await assert.rejects(manager.plan(input), variant.planError);
        continue;
      }
      const plan = await manager.plan(input);
      await assert.rejects(manager.apply({ ...input, planId: plan.planId }), variant.applyError);
      const failed = await manager.status(input);
      assert.equal(failed.consumer.subjectCount, 0);
      assert.equal(failed.consumer.runtimeState, "error");
    } finally {
      await manager.shutdown();
      await rm(stateDir, { recursive: true, force: true });
    }
  }
});

const unitProfileV1Projection = [
  "corrected_engine_hours_factor",
  "corrected_mileage_factor",
  "display_name",
  "filter_by_satellite_count_enabled",
  "filter_by_satellite_count_value",
  "filter_emissions",
  "hardware_manufacturer_name",
  "hardware_port",
  "hardware_type_class",
  "hardware_type_name",
  "limit_acceleration",
  "lost_connection_enabled",
  "lost_connection_time_value",
  "maximum_permissible_speed",
  "maximum_valid_height",
  "maximum_valid_speed",
  "mileage_by_ignition",
  "minimum_movement_speed",
  "minimum_movement_time",
  "minimum_parking_time",
  "minimum_stop_time",
  "minimum_trip_distance",
  "minimum_valid_height",
  "speed_parameter",
  "trip_detection_type",
  "unit_type_class",
  "unit_type_name",
  "use_odometer",
];

const unitProfileV1FieldTypes = {
  corrected_engine_hours_factor: "number",
  corrected_mileage_factor: "number",
  display_name: "string",
  filter_by_satellite_count_enabled: "boolean",
  filter_by_satellite_count_value: "number",
  filter_emissions: "number",
  hardware_manufacturer_name: "string",
  hardware_port: "number",
  hardware_type_class: "string",
  hardware_type_name: "string",
  limit_acceleration: "number",
  lost_connection_enabled: "boolean",
  lost_connection_time_value: "number",
  maximum_permissible_speed: "number",
  maximum_valid_height: "number",
  maximum_valid_speed: "number",
  mileage_by_ignition: "boolean",
  minimum_movement_speed: "number",
  minimum_movement_time: "number",
  minimum_parking_time: "number",
  minimum_stop_time: "number",
  minimum_trip_distance: "number",
  minimum_valid_height: "number",
  speed_parameter: "string",
  trip_detection_type: "string",
  unit_type_class: "string",
  unit_type_name: "string",
  use_odometer: "boolean",
};

const unitProfileV1Policy = {
  id: "map-moving-object-unit-profile-current-v1",
  version: "1.0.0",
  dataProductId: "fleet.units.profile.current.v1",
  productVersion: "1.0.0",
  freshness: "none",
  staleAfterMs: null,
  terminalStatuses: [],
  consumerContract: {
    ontologyRevision: "ontology.map.moving_object.v3",
    deliveryMode: "snapshot+patch",
    semanticTypes: ["map.moving_object"],
    fieldProjection: unitProfileV1Projection,
    fieldTypes: unitProfileV1FieldTypes,
    geometryTypes: [],
    subjectIdentity: "semantic-type+source-id",
    snapshotMode: "atomic-replace",
    patchMode: "atomic",
  },
  removeMode: "canonical-tombstone-or-snapshot-rebase",
};

const unitProfileV1Product = {
  id: "fleet.units.profile.current.v1",
  version: "1.0.0",
  ontologyRevision: "ontology.map.moving_object.v3",
  deliveryMode: "snapshot+patch",
  semanticTypes: ["map.moving_object"],
  fields: [...unitProfileV1Projection],
  fieldContracts: Object.fromEntries(unitProfileV1Projection.map((field) => [
    field,
    { type: unitProfileV1FieldTypes[field], required: field === "display_name" },
  ])),
  active: true,
};

function unitProfileV1Target(fieldProjection = unitProfileV1Projection) {
  return {
    application: { id: "66666666-6666-4666-8666-666666666666" },
    page: { id: "map" },
    binding: {
      id: "trike-unit-profile",
      dataProductId: unitProfileV1Product.id,
      slotId: "points",
      delivery: "snapshot+patch",
      semanticTypes: ["map.moving_object"],
      fieldProjection: [...fieldProjection],
    },
  };
}

function unitProfileV1Fact({ geometry } = {}) {
  return {
    sourceId: "gelios-unit-501",
    semanticType: "map.moving_object",
    observedAt: "2026-07-23T06:28:52.000Z",
    receivedAt: "2026-07-23T06:28:53.000Z",
    attributes: {
      display_name: "501 B2",
      hardware_manufacturer_name: "Gelios",
      hardware_type_name: "Trike tracker",
      maximum_permissible_speed: 25,
      minimum_movement_speed: 3,
      use_odometer: true,
    },
    ...(geometry === undefined ? {} : { geometry }),
  };
}

function unitProfileV1Snapshot(facts = [unitProfileV1Fact()]) {
  return {
    schemaVersion: "nodedc.data-product.snapshot/v1",
    dataProduct: { id: unitProfileV1Product.id, version: unitProfileV1Product.version },
    generatedAt: "2026-07-23T06:28:54.000Z",
    cursor: "107",
    facts,
  };
}

test("non-geometric unit profile aspect is strictly typed and joins by stable subject identity", async () => {
  const stateDir = await mkdtemp(join(tmpdir(), "foundry-consumer-unit-profile-v1-"));
  const currentTarget = unitProfileV1Target();
  const manager = createFoundryDataProductConsumerManager({
    stateDir,
    dataPlaneUrl: "http://edp.test",
    resolveTarget: async () => structuredClone(currentTarget),
    readReaderToken: async () => "ndc_edprb_profile-reader-capability",
    inspectReaderGrant: async () => ({ product: unitProfileV1Product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
    resolvePolicy: () => unitProfileV1Policy,
    sanitizeSnapshot: (value) => value,
    sanitizePatch: (value) => value,
    fetchImpl: async () => Response.json(unitProfileV1Snapshot()),
  });
  const input = { applicationId: currentTarget.application.id, pageId: currentTarget.page.id, bindingId: currentTarget.binding.id };
  try {
    const plan = await manager.plan(input);
    assert.deepEqual(plan.configuration.policy.consumerContract.geometryTypes, []);
    assert.deepEqual(plan.configuration.policy.consumerContract.fieldProjection, unitProfileV1Projection);
    const applied = await manager.apply({ ...input, planId: plan.planId });
    assert.equal(applied.consumer.subjectCount, 1);
    assert.equal(applied.consumer.subjects[0].subjectId, "gelios-unit-501");
    assert.equal(applied.consumer.subjects[0].status, "active");
    const persisted = await manager.snapshot(currentTarget);
    assert.equal(persisted.facts[0].geometry, undefined);
    assert.equal(persisted.facts[0].attributes.hardware_type_name, "Trike tracker");
  } finally {
    await manager.shutdown();
    await rm(stateDir, { recursive: true, force: true });
  }
});

test("non-geometric unit profile aspect fails closed on projection drift and injected geometry", async () => {
  const variants = [
    {
      name: "projection",
      target: unitProfileV1Target(unitProfileV1Projection.slice(1)),
      snapshot: unitProfileV1Snapshot(),
      planError: /data_product_consumer_contract_mismatch/,
    },
    {
      name: "geometry",
      target: unitProfileV1Target(),
      snapshot: unitProfileV1Snapshot([unitProfileV1Fact({ geometry: { type: "Point", coordinates: [37.61, 55.75] } })]),
      applyError: /data_product_consumer_fact_contract_invalid/,
    },
  ];
  for (const variant of variants) {
    const stateDir = await mkdtemp(join(tmpdir(), `foundry-consumer-unit-profile-v1-${variant.name}-`));
    const manager = createFoundryDataProductConsumerManager({
      stateDir,
      dataPlaneUrl: "http://edp.test",
      resolveTarget: async () => structuredClone(variant.target),
      readReaderToken: async () => "ndc_edprb_profile-reader-capability",
      inspectReaderGrant: async () => ({ product: unitProfileV1Product, readerGrantAction: "reuse", readerGrantGeneration: 1 }),
      resolvePolicy: () => unitProfileV1Policy,
      sanitizeSnapshot: (value) => value,
      sanitizePatch: (value) => value,
      fetchImpl: async () => Response.json(variant.snapshot),
    });
    const input = { applicationId: variant.target.application.id, pageId: variant.target.page.id, bindingId: variant.target.binding.id };
    try {
      if (variant.planError) {
        await assert.rejects(manager.plan(input), variant.planError);
      } else {
        const plan = await manager.plan(input);
        await assert.rejects(manager.apply({ ...input, planId: plan.planId }), variant.applyError);
      }
    } finally {
      await manager.shutdown();
      await rm(stateDir, { recursive: true, force: true });
    }
  }
});

test("restricted aspect consumers require the exact declared binding data class", async () => {
  for (const [bindingDataClass, expected] of [
    ["restricted", "ok"],
    ["operational", "data_product_consumer_data_class_mismatch"],
  ]) {
    const stateDir = await mkdtemp(join(tmpdir(), `foundry-consumer-data-class-${bindingDataClass}-`));
    const currentTarget = unitProfileV1Target();
    currentTarget.binding.dataClass = bindingDataClass;
    const manager = createFoundryDataProductConsumerManager({
      stateDir,
      dataPlaneUrl: "http://edp.test",
      resolveTarget: async () => structuredClone(currentTarget),
      readReaderToken: async () => "ndc_edprb_profile-reader-capability",
      inspectReaderGrant: async () => ({
        product: unitProfileV1Product,
        readerGrantAction: "reuse",
        readerGrantGeneration: 1,
      }),
      resolvePolicy: () => ({ ...unitProfileV1Policy, dataClass: "restricted" }),
      sanitizeSnapshot: (value) => value,
      sanitizePatch: (value) => value,
      fetchImpl: async () => Response.json(unitProfileV1Snapshot()),
    });
    const input = {
      applicationId: currentTarget.application.id,
      pageId: currentTarget.page.id,
      bindingId: currentTarget.binding.id,
    };
    try {
      if (expected === "ok") {
        const plan = await manager.plan(input);
        assert.equal(plan.configuration.policy.dataClass, "restricted");
        assert.equal(plan.configuration.target.dataClass, "restricted");
      } else {
        await assert.rejects(manager.plan(input), new RegExp(expected));
      }
    } finally {
      await manager.shutdown();
      await rm(stateDir, { recursive: true, force: true });
    }
  }
});
