← Files Biological Sequence & Alignment ViewerARCHIVED FILE

src/server-workbench-upload.test.ts

35.4 KB · Sep 30, 2026 · 23:01 UTC

↓ Download file

import { createHash, randomUUID } from "node:crypto";
import {
  mkdir,
  mkdtemp,
  readFile,
  readdir,
  rm,
  writeFile,
} from "node:fs/promises";
import os from "node:os";
import path from "node:path";
import { pathToFileURL } from "node:url";
import { gzipSync } from "node:zlib";

import { afterEach, describe, expect, it, vi } from "vitest";

import { SEQUENCE_VIEWER_LIMITS } from "./runtime-contract";
import { buildAlignedFasta } from "./msa/exports";
import { createServerWorkbenchStore } from "./server-workbench-store";
import { SequenceWorkbenchUploadStore } from "./server-workbench-upload";
import type { SequenceWorkbenchPayloadDeclaration } from "./workbench-persistence-protocol";
import { SequenceWorkspaceExportPublisher } from "./workspace-export-publisher";

const temporaryDirectories: Array<string> = [];
const stores: Array<SequenceWorkbenchUploadStore> = [];

// Genuine RF04178 sequence and structure from Biopython commit
// c9489604d1d9607602ca9199a3852c1219ed330f, Tests/Stockholm/rfam1.seed.txt.
// Original fixture SHA-256:
// a77480898aab5cc85b2c3eb332b05242c77aef0b9e4a29f5177f7d54adf6c035.
const publicRfamSequence = [
  "GUAAGUAAAAGUGUAACAGGAAGAAAGUUGCAGCAUAUAUGCGGUGAAUUAUGCGGUGUCAUAGGAAUUG",
  "AGGAUUUAUGUAAGAUGCUGAUAAUGAGUAAGGAACCUUAAAGUUAAUCGUUCCCUGUCUCUCCGCAGAA",
  "CCUACUGGACAAAACAGGACAGUAAGUGGACAAAAACCUACAAAUCAGC-GAUUUGUAGGUUUUUU",
].join("");
const publicRfamSecondaryStructure = [
  ":::::::::::<<<<<<_________>>>>>>,,,,,,,,((((,,,<<<<<-<<<<<<<----<<<___",
  "____>>>------>>>>>>>>>>>><<<<<-<<<<_______________>>>>->>->>>,))))----",
  "-----------------------------------<<<<<<<<<<<____>>>>>>>>>>>:::::",
].join("");
const publicRfamStockholm = [
  "# STOCKHOLM 1.0",
  "#=GF AC RF04178",
  "#=GF ID BTnc005",
  `AE015928.1/72774-72978 ${publicRfamSequence}`,
  `#=GC SS_cons ${publicRfamSecondaryStructure}`,
  "//",
].join("\n");

afterEach(async () => {
  await Promise.allSettled(stores.splice(0).map((store) => store.dispose()));
  await Promise.all(
    temporaryDirectories
      .splice(0)
      .map((directory) => rm(directory, { force: true, recursive: true })),
  );
});

describe("SequenceWorkbenchUploadStore", () => {
  it("persists a proxy-safe one-shot artifact idempotently", async () => {
    const { store, workbench } = await createStore();
    const content = ">demo\nACGT\n";
    const input = oneShotInput(artifactDeclaration(content), content);

    const first = await store.persistOneShot(input);
    const replayed = await store.persistOneShot(input);

    expect(replayed).toEqual(first);
    expect(first).toMatchObject({
      format: "fasta",
      kind: "artifact",
      name: "demo.fasta",
      sha256: sha256(content),
      size: Buffer.byteLength(content),
    });
    if (first.kind !== "artifact" || !("id" in first)) {
      throw new Error("Expected private artifact result.");
    }
    await expect(workbench.readArtifact(first.id)).resolves.toMatchObject({
      content,
    });
  });

  it.each([
    [
      "a3m",
      "text/x-a3m",
      "alignment.a3m",
      ">alpha\nACgtGT\n>beta\nA-GT\n",
    ],
    [
      "clustal",
      "text/x-clustal",
      "alignment.aln",
      "CLUSTAL W multiple sequence alignment\n\nalpha  ACGT\nbeta   A-GT\n",
    ],
    [
      "gtf",
      "text/x-gtf",
      "annotations.gtf",
      'AC007323.5\tGenBank\tCDS\t3698\t3978\t.\t+\t2\tgene_id "AC007323.5"; transcript_id "AT1G01010";\n',
    ],
    [
      "pdf",
      "application/pdf",
      "alignment.pdf",
      "%PDF-1.4\n1 0 obj\n<<>>\nendobj\nstartxref\n9\n%%EOF\n",
    ],
    [
      "stockholm",
      "text/x-stockholm",
      "alignment.sto",
      publicRfamStockholm,
    ],
  ] as const)(
    "persists advertised %s exports as retrievable private artifacts",
    async (format, mediaType, name, content) => {
      const { store, workbench } = await createStore();
      const declaration = artifactDeclaration(content, {
        format,
        mediaType,
        name,
      });

      const result = await store.persistOneShot(
        oneShotInput(declaration, content),
      );

      expect(result).toMatchObject({
        format,
        kind: "artifact",
        mediaType,
        name,
        resourceUri: expect.stringMatching(
          /^viewer-artifact:\/\/sequence-viewer\/generated\//u,
        ),
        sha256: sha256(content),
        size: Buffer.byteLength(content),
      });
      if (result.kind !== "artifact" || !("id" in result)) {
        throw new Error("Expected a retrievable private artifact.");
      }
      await expect(workbench.readArtifact(result.id)).resolves.toMatchObject({
        content,
        metadata: { format, mediaType, name },
      });
    },
  );

  it.each([
    ["a3m", "text/x-a3m", "alignment.a3m", ">alpha\n"],
    ["clustal", "text/x-clustal", "alignment.aln", ">alpha\nACGT\n"],
    [
      "gtf",
      "text/x-gtf",
      "annotations.gtf",
      "chr1\tviewer\tgene\t1\t4\t.\t+\t.\tname x;\n",
    ],
    ["pdf", "application/pdf", "alignment.pdf", "%PDF-1.4\n1 0 obj\n"],
    [
      "stockholm",
      "text/x-stockholm",
      "alignment.sto",
      "# STOCKHOLM 1.0\nalpha ACGU\n",
    ],
  ] as const)(
    "rejects structurally invalid private %s artifacts",
    async (format, mediaType, name, content) => {
      const { store } = await createStore();
      const declaration = artifactDeclaration(content, {
        format,
        mediaType,
        name,
      });

      await expect(
        store.persistOneShot(oneShotInput(declaration, content)),
      ).rejects.toThrow();
    },
  );

  it.each([
    ["a3m", "alignment.a3m", ">alpha\nACGT\n"],
    [
      "clustal",
      "alignment.aln",
      "CLUSTAL W multiple sequence alignment\n\nalpha ACGT\n",
    ],
    [
      "gtf",
      "annotations.gtf",
      'chr1\tviewer\tgene\t1\t4\t.\t+\t.\tgene_id "gene-1";\n',
    ],
    [
      "pdf",
      "alignment.pdf",
      "%PDF-1.4\n1 0 obj\n<<>>\nendobj\nstartxref\n9\n%%EOF\n",
    ],
    [
      "stockholm",
      "alignment.sto",
      "# STOCKHOLM 1.0\nalpha ACGU\n//\n",
    ],
  ] as const)(
    "rejects a mismatched media type for private %s artifacts",
    async (format, name, content) => {
      const { store } = await createStore();
      const declaration = artifactDeclaration(content, {
        format,
        mediaType: "text/plain",
        name,
      });

      await expect(
        store.persistOneShot(oneShotInput(declaration, content)),
      ).rejects.toThrow("media type");
    },
  );

  it("preserves a valid empty BED export", async () => {
    const { store } = await createStore();
    const declaration = artifactDeclaration("", {
      format: "bed",
      mediaType: "text/x-bed",
      name: "empty.bed",
    });
    await expect(
      store.persistOneShot(oneShotInput(declaration, "")),
    ).resolves.toMatchObject({
      format: "bed",
      kind: "artifact",
      size: 0,
    });
  });

  it("resumes multi-chunk uploads and accepts only identical retries", async () => {
    const { store } = await createStore();
    const content = `>${"a".repeat(
      SEQUENCE_VIEWER_LIMITS.persistence.maxChunkBytes * 2 + 17,
    )}`;
    const declaration = artifactDeclaration(content);
    const bytes = Buffer.from(content);
    const first = bytes.subarray(
      0,
      SEQUENCE_VIEWER_LIMITS.persistence.maxChunkBytes,
    );

    await store.begin(declaration);
    await store.append(chunkInput(declaration, 0, first));
    await expect(
      store.append(chunkInput(declaration, 0, first)),
    ).resolves.toMatchObject({ receivedBytes: first.byteLength });
    await expect(
      store.append(
        chunkInput(declaration, 0, Buffer.alloc(first.byteLength, 7)),
      ),
    ).rejects.toThrow("did not match");
    await expect(
      store.append(
        chunkInput(
          declaration,
          first.byteLength + 1,
          bytes.subarray(first.byteLength, first.byteLength * 2),
        ),
      ),
    ).rejects.toThrow("Expected workbench payload offset");

    await expect(store.begin(declaration)).resolves.toMatchObject({
      receivedBytes: first.byteLength,
    });
    for (let offset = first.byteLength; offset < bytes.byteLength; ) {
      const end = Math.min(
        offset + SEQUENCE_VIEWER_LIMITS.persistence.maxChunkBytes,
        bytes.byteLength,
      );
      await store.append(
        chunkInput(declaration, offset, bytes.subarray(offset, end)),
      );
      offset = end;
    }
    const result = await store.finish(identity(declaration));
    expect(result).toMatchObject({
      kind: "artifact",
      sha256: declaration.sha256,
      size: bytes.byteLength,
    });
    await expect(store.finish(identity(declaration))).resolves.toEqual(result);
  });

  it("rejects incomplete, oversized, and digest-mismatched declarations", async () => {
    const { store } = await createStore();
    const content = ">demo\nACGT\n";
    const incomplete = artifactDeclaration(content);
    await store.begin(incomplete);
    await expect(store.finish(identity(incomplete))).rejects.toThrow(
      "incomplete",
    );
    await store.abort(identity(incomplete));

    const badDigest = artifactDeclaration(content, {
      sha256: "0".repeat(64),
    });
    await store.begin(badDigest);
    await store.append(chunkInput(badDigest, 0, Buffer.from(content)));
    await expect(store.finish(identity(badDigest))).rejects.toThrow("SHA-256");
    await store.abort(identity(badDigest));

    await expect(
      store.begin({
        ...artifactDeclaration(content),
        byteLength: SEQUENCE_VIEWER_LIMITS.session.maxArtifactBytes + 1,
      }),
    ).rejects.toThrow();
  });

  it("binds uploads to the exact session, command, caller, and declaration", async () => {
    const { store } = await createStore();
    const content = ">demo\nACGT\n";
    const declaration = artifactDeclaration(content);
    await store.begin(declaration);

    for (const changed of [
      { callerId: randomUUID() },
      { commandId: randomUUID() },
      { sessionId: randomUUID() },
    ]) {
      await expect(
        store.append({
          ...chunkInput(declaration, 0, Buffer.from(content)),
          ...changed,
        }),
      ).rejects.toThrow(/another (caller|viewer command|viewer session)/u);
    }
    await expect(
      store.begin({ ...declaration, name: "different.fasta" }),
    ).rejects.toThrow("different data");
    await store.abort(identity(declaration));
  });

  it("bounds concurrent upload count and declared active bytes", async () => {
    const { store } = await createStore();
    const countDeclarations = Array.from(
      { length: SEQUENCE_VIEWER_LIMITS.persistence.maxActiveUploads },
      () => artifactDeclaration(">A"),
    );
    for (const declaration of countDeclarations) {
      await store.begin(declaration);
    }
    await expect(store.begin(artifactDeclaration(">B"))).rejects.toThrow(
      "Too many",
    );
    await Promise.all(
      countDeclarations.map((declaration) =>
        store.abort(identity(declaration)),
      ),
    );

    const byteDeclarations = Array.from({ length: 4 }, () =>
      artifactDeclaration("", {
        byteLength: SEQUENCE_VIEWER_LIMITS.session.maxArtifactBytes,
        sha256: "0".repeat(64),
      }),
    );
    for (const declaration of byteDeclarations) {
      await store.begin(declaration);
    }
    await expect(
      store.begin(
        artifactDeclaration("", {
          byteLength: 1,
          sha256: "0".repeat(64),
        }),
      ),
    ).rejects.toThrow("byte budget");
    await Promise.all(
      byteDeclarations.map((declaration) => store.abort(identity(declaration))),
    );
  });

  it("validates sessions and returns compact saved-session metadata", async () => {
    const { store, workbench } = await createStore();
    const content = validSession();
    const declaration = sessionDeclaration(content);
    const result = await store.persistOneShot(
      oneShotInput(declaration, content),
    );
    expect(result).toEqual({
      kind: "session",
      name: declaration.name,
      savedSessionId: expect.any(String),
      sha256: declaration.sha256,
      size: declaration.byteLength,
    });
    if (result.kind !== "session" || !("savedSessionId" in result)) {
      throw new Error("Expected private session result.");
    }
    await expect(workbench.readSession(result.savedSessionId)).resolves.toBe(
      content,
    );

    const invalid = '{"schemaVersion":1}';
    const invalidDeclaration = sessionDeclaration(invalid);
    await store.begin(invalidDeclaration);
    await store.append(chunkInput(invalidDeclaration, 0, Buffer.from(invalid)));
    await expect(store.finish(identity(invalidDeclaration))).rejects.toThrow();
    await store.abort(identity(invalidDeclaration));
  });

  it("cleans up cancellation and expiry without publishing partial content", async () => {
    let now = 1_000;
    const { stagingDirectory, store } = await createStore({ now: () => now });
    const content = ">demo\nACGT\n";
    const cancelled = artifactDeclaration(content);
    await store.begin(cancelled);
    await store.append(chunkInput(cancelled, 0, Buffer.from(content)));
    await expect(store.abort(identity(cancelled))).resolves.toEqual({
      aborted: true,
      uploadId: cancelled.uploadId,
    });
    expect(store.activeUploads).toBe(0);

    const expired = artifactDeclaration(content);
    await store.begin(expired);
    now += SEQUENCE_VIEWER_LIMITS.persistence.uploadTtlMs + 1;
    await store.cleanupExpired();
    expect(store.activeUploads).toBe(0);
    await expect(store.finish(identity(expired))).rejects.toThrow(
      "not found or has expired",
    );
    expect(await readdir(stagingDirectory)).toEqual([]);
  });

  it("releases a pending begin when abort wins staging creation", async () => {
    const directory = await temporaryDirectory();
    const stagingDirectory = await temporaryDirectory();
    const workbench = createServerWorkbenchStore({ stateDirectory: directory });
    let releaseStagingDirectory: (() => void) | undefined;
    const stagingDirectoryGate = new Promise<void>((resolve) => {
      releaseStagingDirectory = resolve;
    });
    const stagingDirectoryRequested = vi.fn();
    const store = new SequenceWorkbenchUploadStore(workbench, {
      createStagingDirectory: async () => {
        stagingDirectoryRequested();
        await stagingDirectoryGate;
        return stagingDirectory;
      },
    });
    stores.push(store);
    const declaration = artifactDeclaration(">demo\nACGT\n");

    const beginning = store.begin(declaration);
    await vi.waitFor(() =>
      expect(stagingDirectoryRequested).toHaveBeenCalledOnce(),
    );
    expect(store.activeUploads).toBe(1);
    const aborting = store.abort(identity(declaration));
    releaseStagingDirectory?.();

    await expect(beginning).rejects.toMatchObject({ name: "AbortError" });
    await expect(aborting).resolves.toEqual({
      aborted: true,
      uploadId: declaration.uploadId,
    });
    expect(store.activeUploads).toBe(0);
    expect(await readdir(stagingDirectory)).toEqual([]);
  });

  it("reports a committed result when cancellation races with publication", async () => {
    const directory = await temporaryDirectory();
    const workbench = createServerWorkbenchStore({ stateDirectory: directory });
    const originalPersist = workbench.persistArtifact.bind(workbench);
    let releaseCommit: (() => void) | undefined;
    const commitGate = new Promise<void>((resolve) => {
      releaseCommit = resolve;
    });
    const commitStarted = vi.fn();
    workbench.persistArtifact = vi.fn(async (input) => {
      commitStarted();
      await commitGate;
      return await originalPersist(input);
    });
    const stagingDirectory = await temporaryDirectory();
    const store = new SequenceWorkbenchUploadStore(workbench, {
      createStagingDirectory: async () => stagingDirectory,
    });
    stores.push(store);
    const content = ">demo\nACGT\n";
    const declaration = artifactDeclaration(content);
    await store.begin(declaration);
    await store.append(chunkInput(declaration, 0, Buffer.from(content)));
    const finishing = store.finish(identity(declaration));
    await vi.waitFor(() => expect(commitStarted).toHaveBeenCalledOnce());
    const aborting = store.abort(identity(declaration));
    releaseCommit?.();
    const [result, aborted] = await Promise.all([finishing, aborting]);
    expect(aborted).toEqual({
      aborted: false,
      result,
      uploadId: declaration.uploadId,
    });
  });

  it("publishes every supported export format through the existing one-shot transport", async () => {
    const fixture = await createWorkspaceStore();
    const exports = {
      "aligned-fasta": [">a\nA-\n>b\nAC\n", "visible.afa", "text/x-fasta"],
      bed: ["", "features.bed", "text/x-bed"],
      csv: ["name,value\na,1\n", "results.csv", "text/csv"],
      embl: ["ID   demo;\nSQ   Sequence 1 BP;\n     a\n//\n", "record.embl", "text/x-embl"],
      fasta: [">demo\nACGT\n", "record.fasta", "text/x-fasta"],
      fastq: ["@demo\nACGT\n+\nIIII\n", "reads.fastq", "text/x-fastq"],
      genbank: ["LOCUS       DEMO 1 bp\nORIGIN\n        1 a\n//\n", "record.gbk", "text/x-genbank"],
      gff3: ["##gff-version 3\nchr1\tdemo\tgene\t1\t1\t.\t+\t.\tID=g1\n", "features.gff3", "text/x-gff3"],
      json: ["{\"records\":[]}", "viewer.json", "application/json"],
      newick: ["(a:1,b:1);\n", "tree.nwk", "text/x-newick"],
      svg: ["<svg xmlns=\"http://www.w3.org/2000/svg\"></svg>\n", "range.svg", "image/svg+xml"],
      tsv: ["name\tvalue\na\t1\n", "hits.tsv", "text/tab-separated-values"],
      vcf: ["##fileformat=VCFv4.3\n#CHROM\tPOS\tID\tREF\tALT\tQUAL\tFILTER\tINFO\n", "variants.vcf", "text/x-vcf"],
    } as const;

    for (const [format, [content, name, mediaType]] of Object.entries(exports)) {
      const declaration = artifactDeclaration(content, {
        destination: {
          base: "opened-source",
          kind: "workspace",
          relativePath: `exports/${name}`,
        },
        format: format as SequenceWorkbenchPayloadDeclaration["format"],
        mediaType,
        name,
        provenance: {
          engine: "sequence-viewer-export-v1",
          parameters: { format },
          sourceRevision: 1,
        },
        sessionId: fixture.sessionId,
      });
      const result = await fixture.store.persistOneShot(
        oneShotInput(declaration, content),
      );
      expect(result).toMatchObject({
        destination: { base: "opened-source", kind: "workspace" },
        format,
        kind: "artifact",
        mediaType,
        name,
        sha256: declaration.sha256,
        size: Buffer.byteLength(content),
      });
      await expect(
        readFile(path.join(fixture.sourceDirectory, "exports", name), "utf8"),
      ).resolves.toBe(content);
    }
    await expect(readFile(fixture.sourcePath, "utf8")).resolves.toBe(
      fixture.sourceContent,
    );
  });

  it("publishes server-authored workspace session manifests through the existing transport", async () => {
    const fixture = await createWorkspaceStore();
    const content = validSession();
    const declaration: SequenceWorkbenchPayloadDeclaration = {
      ...sessionDeclaration(content),
      destination: {
        base: "opened-source",
        collisionPolicy: "next-version",
        kind: "workspace",
        relativePath: "source.sequence-viewer.session.json",
      },
      name: "source.sequence-viewer.session.json",
      sessionId: fixture.sessionId,
    };
    const first = await fixture.store.persistOneShot(
      oneShotInput(declaration, content),
    );
    const secondDeclaration: SequenceWorkbenchPayloadDeclaration = {
      ...declaration,
      callerId: randomUUID(),
      commandId: randomUUID(),
      uploadId: randomUUID(),
    };
    const second = await fixture.store.persistOneShot(
      oneShotInput(secondDeclaration, content),
    );

    expect(first).toMatchObject({
      destination: { base: "opened-source", kind: "workspace" },
      kind: "session",
      name: "source.sequence-viewer.session.json",
      outputWorkspacePath: "data/source.sequence-viewer.session.json",
      payloadSha256: declaration.sha256,
      payloadSize: declaration.byteLength,
      version: 1,
    });
    expect(second).toMatchObject({
      kind: "session",
      name: "source.sequence-viewer.session-2.json",
      outputWorkspacePath: "data/source.sequence-viewer.session-2.json",
    });
    expect(first).not.toHaveProperty("savedSessionId");
    const manifestText = await readFile(
      path.join(
        fixture.sourceDirectory,
        "source.sequence-viewer.session.json",
      ),
      "utf8",
    );
    const manifest = JSON.parse(manifestText) as Record<string, unknown>;
    expect(manifest).toMatchObject({
      mode: "sequence",
      payload: content,
      payloadSha256: declaration.sha256,
      schemaVersion: 1,
      source: {
        sha256: sha256(fixture.sourceContent),
        size: Buffer.byteLength(fixture.sourceContent),
        workspacePath: "data/source.fasta",
      },
      version: 1,
    });
    expect(manifestText).not.toContain(fixture.sourcePath);
    expect(manifestText).not.toContain(declaration.commandId);
    expect(manifestText).not.toContain(declaration.uploadId);
  });

  it("resumes and idempotently finishes a multi-megabyte workspace export", async () => {
    const fixture = await createWorkspaceStore();
    const content = `>large\n${"ACGT".repeat(600 * 1_024)}\n`;
    const declaration = artifactDeclaration(content, {
      destination: {
        base: "opened-source",
        kind: "workspace",
        relativePath: "exports/large.fasta",
      },
      name: "large.fasta",
      provenance: {
        engine: "sequence-viewer-export-v1",
        parameters: { scope: "all" },
        sourceRevision: 2,
      },
      sessionId: fixture.sessionId,
    });
    const bytes = Buffer.from(content);
    await fixture.store.begin(declaration);
    const firstEnd = SEQUENCE_VIEWER_LIMITS.persistence.maxChunkBytes;
    await fixture.store.append(
      chunkInput(declaration, 0, bytes.subarray(0, firstEnd)),
    );
    await fixture.store.append(
      chunkInput(declaration, 0, bytes.subarray(0, firstEnd)),
    );
    await expect(fixture.store.begin(declaration)).resolves.toMatchObject({
      receivedBytes: firstEnd,
    });
    for (let offset = firstEnd; offset < bytes.byteLength; ) {
      const end = Math.min(
        offset + SEQUENCE_VIEWER_LIMITS.persistence.maxChunkBytes,
        bytes.byteLength,
      );
      await fixture.store.append(
        chunkInput(declaration, offset, bytes.subarray(offset, end)),
      );
      offset = end;
    }
    const result = await fixture.store.finish(identity(declaration));
    expect(result).toMatchObject({
      metrics: {
        acceptedBytes: bytes.byteLength,
        committedBytes: bytes.byteLength,
        mode: "browser-streamed",
        producedBytes: bytes.byteLength,
        retryCount: 1,
      },
    });
    await expect(fixture.store.finish(identity(declaration))).resolves.toEqual(
      result,
    );
    await expect(
      readFile(path.join(fixture.sourceDirectory, "exports/large.fasta")),
    ).resolves.toEqual(bytes);
  }, 20_000);

  it("generates an Alignment export directly from the trusted source idempotently", async () => {
    const fixture = await createWorkspaceStore({
      sourceContent: ">a\nAC-\n>b\nA--",
      sourceName: "source.afa",
    });
    const input = {
      callerId: randomUUID(),
      commandId: randomUUID(),
      destination: {
        base: "opened-source" as const,
        kind: "workspace" as const,
        relativePath: "exports/generated.afa",
      },
      format: "aligned-fasta" as const,
      mediaType: "text/x-fasta" as const,
      name: "generated.afa",
      operationId: randomUUID(),
      provenance: {
        engine: "sequence-viewer-alignment-server-export-v1",
        parameters: { scope: "all" },
        sourceRevision: 0,
      },
      sessionId: fixture.sessionId,
      source: { compression: "auto" as const, kind: "opened-source" as const },
    };

    const result = await fixture.store.generateWorkspaceExport(input);
    expect(fixture.sourceContent).toBe(
      buildAlignedFasta([
        { alignedSequence: "AC-", id: "a", label: "a", ungappedLength: 2 },
        { alignedSequence: "A--", id: "b", label: "b", ungappedLength: 1 },
      ]),
    );
    await expect(fixture.store.generateWorkspaceExport(input)).resolves.toEqual(
      result,
    );
    expect(result).toMatchObject({
      kind: "artifact",
      metrics: {
        acceptedBytes: Buffer.byteLength(fixture.sourceContent),
        committedBytes: Buffer.byteLength(fixture.sourceContent),
        mode: "server-generated",
        peakRetainedBytes: expect.any(Number),
        producedBytes: Buffer.byteLength(fixture.sourceContent),
      },
      name: "generated.afa",
      size: Buffer.byteLength(fixture.sourceContent),
    });
    await expect(
      readFile(
        path.join(fixture.sourceDirectory, "exports/generated.afa"),
        "utf8",
      ),
    ).resolves.toBe(fixture.sourceContent);
  });

  it("generates a gzip-backed Sequence export without retaining the decoded output", async () => {
    const sourceContent = `>gzip\n${"ACGT".repeat(300_000)}\n`;
    const fixture = await createWorkspaceStore({
      sourceBytes: gzipSync(sourceContent),
      sourceContent,
      sourceName: "source.fasta.gz",
    });
    const result = await fixture.store.generateWorkspaceExport({
      callerId: randomUUID(),
      commandId: randomUUID(),
      destination: {
        base: "opened-source",
        kind: "workspace",
        relativePath: "exports/decoded.fasta",
      },
      format: "fasta",
      mediaType: "text/x-fasta",
      name: "decoded.fasta",
      operationId: randomUUID(),
      provenance: {
        engine: "sequence-viewer-server-export-v1",
        parameters: { compression: "gzip" },
        sourceRevision: 0,
      },
      sessionId: fixture.sessionId,
      source: { compression: "auto", kind: "opened-source" },
    });

    expect(result).toMatchObject({
      kind: "artifact",
      metrics: {
        mode: "server-generated",
        peakRetainedBytes: expect.any(Number),
      },
      sha256: sha256(sourceContent),
      size: Buffer.byteLength(sourceContent),
    });
    if (result.kind !== "artifact" || !("metrics" in result)) {
      throw new Error("Expected generated workspace artifact metrics.");
    }
    expect(result.metrics?.peakRetainedBytes).toBeLessThanOrEqual(4 * 1_024 * 1_024);
    await expect(
      readFile(path.join(fixture.sourceDirectory, "exports/decoded.fasta"), "utf8"),
    ).resolves.toBe(sourceContent);
  }, 20_000);

  it("enforces workspace-output and disk quotas without reserving declared bytes", async () => {
    const fixture = await createWorkspaceStore({
      getAvailableWorkspaceBytes: async () => 0,
    });
    const tooLarge = artifactDeclaration("", {
      byteLength: SEQUENCE_VIEWER_LIMITS.workspace.maxArtifactBytes + 1,
      destination: {
        base: "opened-source",
        kind: "workspace",
        relativePath: "exports/too-large.fasta",
      },
      name: "too-large.fasta",
      provenance: {
        engine: "test",
        parameters: {},
        sourceRevision: 0,
      },
      sessionId: fixture.sessionId,
      sha256: "0".repeat(64),
    });
    await expect(fixture.store.begin(tooLarge)).rejects.toThrow(
      "workspace-output quota",
    );

    const declarations = ["first", "second"].map((stem) =>
      artifactDeclaration("", {
        byteLength: 1_024 * 1_024 * 1_024,
        destination: {
          base: "opened-source",
          kind: "workspace",
          relativePath: `exports/${stem}.fasta`,
        },
        name: `${stem}.fasta`,
        provenance: {
          engine: "test",
          parameters: {},
          sourceRevision: 0,
        },
        sessionId: fixture.sessionId,
        sha256: "0".repeat(64),
      }),
    );
    for (const declaration of declarations) {
      await expect(fixture.store.begin(declaration)).resolves.toMatchObject({
        receivedBytes: 0,
      });
    }
    expect(fixture.store.workspaceStagingUsage).toBe(0);
    await Promise.all(
      declarations.map((declaration) =>
        fixture.store.abort(identity(declaration)),
      ),
    );

    const content = ">disk\nACGT\n";
    const diskFailure = artifactDeclaration(content, {
      destination: {
        base: "opened-source",
        kind: "workspace",
        relativePath: "exports/disk.fasta",
      },
      name: "disk.fasta",
      provenance: {
        engine: "test",
        parameters: {},
        sourceRevision: 0,
      },
      sessionId: fixture.sessionId,
    });
    await fixture.store.begin(diskFailure);
    await expect(
      fixture.store.append(
        chunkInput(diskFailure, 0, Buffer.from(content)),
      ),
    ).rejects.toThrow("free disk space");
    expect(fixture.store.activeUploads).toBe(0);
    expect(fixture.store.workspaceStagingUsage).toBe(0);
    expect(
      (await readdir(path.join(fixture.sourceDirectory, "exports"))).some(
        (name) => name.endsWith(".tmp"),
      ),
    ).toBe(false);
    await expect(
      readFile(path.join(fixture.sourceDirectory, "exports/disk.fasta")),
    ).rejects.toMatchObject({ code: "ENOENT" });
  });

  it("removes destination-local staging after incremental validation fails", async () => {
    const fixture = await createWorkspaceStore();
    const content = '{"value":}';
    const declaration = artifactDeclaration(content, {
      destination: {
        base: "opened-source",
        kind: "workspace",
        relativePath: "exports/invalid.json",
      },
      format: "json",
      mediaType: "application/json",
      name: "invalid.json",
      provenance: {
        engine: "test",
        parameters: {},
        sourceRevision: 0,
      },
      sessionId: fixture.sessionId,
    });
    await fixture.store.begin(declaration);
    await expect(
      fixture.store.append(
        chunkInput(declaration, 0, Buffer.from(content)),
      ),
    ).rejects.toThrow("JSON");
    expect(fixture.store.activeUploads).toBe(0);
    expect(fixture.store.workspaceStagingUsage).toBe(0);
    await expect(
      readFile(path.join(fixture.sourceDirectory, "exports/invalid.json")),
    ).rejects.toMatchObject({ code: "ENOENT" });
  });

  it("returns the actual deterministic name for a next-version workspace upload", async () => {
    const fixture = await createWorkspaceStore();
    await writeFile(
      path.join(fixture.sourceDirectory, "exports/versioned.fasta"),
      "occupied",
    );
    const content = ">versioned\nAC\n";
    const declaration = artifactDeclaration(content, {
      destination: {
        base: "opened-source",
        collisionPolicy: "next-version",
        kind: "workspace",
        relativePath: "exports/versioned.fasta",
      },
      name: "versioned.fasta",
      provenance: {
        engine: "sequence-viewer-browser-export-v1",
        parameters: {},
        sourceRevision: 1,
      },
      sessionId: fixture.sessionId,
    });

    const result = await fixture.store.persistOneShot(
      oneShotInput(declaration, content),
    );

    expect(result).toMatchObject({
      kind: "artifact",
      name: "versioned-2.fasta",
      outputWorkspacePath: "data/exports/versioned-2.fasta",
    });
    await expect(
      readFile(
        path.join(fixture.sourceDirectory, "exports/versioned-2.fasta"),
        "utf8",
      ),
    ).resolves.toBe(content);
  });
});

async function createWorkspaceStore({
  getAvailableWorkspaceBytes,
  sourceContent = ">source\nACGT\n",
  sourceBytes,
  sourceName = "source.fasta",
}: {
  getAvailableWorkspaceBytes?: (directory: string) => Promise<number>;
  sourceContent?: string;
  sourceBytes?: Uint8Array;
  sourceName?: string;
} = {}) {
  const directory = await temporaryDirectory();
  const stateDirectory = await temporaryDirectory();
  const stagingDirectory = await temporaryDirectory();
  const sourceDirectory = path.join(directory, "data");
  await mkdir(path.join(sourceDirectory, "exports"), { recursive: true });
  const sourcePath = path.join(sourceDirectory, sourceName);
  await writeFile(sourcePath, sourceBytes ?? sourceContent);
  const sessionId = randomUUID();
  const workspacePublisher = new SequenceWorkspaceExportPublisher();
  await workspacePublisher.bindSession(sessionId, sourcePath, {
    async sendRequest() {
      return { roots: [{ uri: pathToFileURL(directory).href }] };
    },
  } as never);
  const workbench = createServerWorkbenchStore({ stateDirectory });
  const store = new SequenceWorkbenchUploadStore(workbench, {
    createStagingDirectory: async () => stagingDirectory,
    getAvailableWorkspaceBytes,
    workspacePublisher,
  });
  stores.push(store);
  return {
    sessionId,
    sourceContent,
    sourceDirectory,
    sourcePath,
    store,
  };
}

async function createStore({ now }: { now?: () => number } = {}) {
  const directory = await temporaryDirectory();
  const stagingDirectory = await temporaryDirectory();
  const workbench = createServerWorkbenchStore({ stateDirectory: directory });
  const store = new SequenceWorkbenchUploadStore(workbench, {
    createStagingDirectory: async () => stagingDirectory,
    now,
  });
  stores.push(store);
  return { stagingDirectory, store, workbench };
}

function artifactDeclaration(
  content: string,
  overrides: Partial<SequenceWorkbenchPayloadDeclaration> = {},
): SequenceWorkbenchPayloadDeclaration {
  const { destination = { kind: "private" }, ...rest } = overrides;
  return {
    byteLength: Buffer.byteLength(content),
    callerId: randomUUID(),
    commandId: randomUUID(),
    destination,
    format: "fasta",
    kind: "artifact",
    mediaType: "text/x-fasta",
    name: "demo.fasta",
    sessionId: randomUUID(),
    sha256: sha256(content),
    uploadId: randomUUID(),
    ...rest,
  };
}

function sessionDeclaration(
  content: string,
): SequenceWorkbenchPayloadDeclaration {
  return {
    byteLength: Buffer.byteLength(content),
    callerId: randomUUID(),
    commandId: randomUUID(),
    destination: { kind: "private" },
    kind: "session",
    name: "demo.sequence-session.json",
    sessionId: randomUUID(),
    sha256: sha256(content),
    uploadId: randomUUID(),
  };
}

function oneShotInput(
  declaration: SequenceWorkbenchPayloadDeclaration,
  content: string,
) {
  return {
    ...declaration,
    dataBase64: Buffer.from(content).toString("base64"),
  };
}

function chunkInput(
  declaration: SequenceWorkbenchPayloadDeclaration,
  offset: number,
  bytes: Uint8Array,
) {
  return {
    ...identity(declaration),
    dataBase64: Buffer.from(bytes).toString("base64"),
    offset,
  };
}

function identity(declaration: SequenceWorkbenchPayloadDeclaration) {
  return {
    callerId: declaration.callerId,
    commandId: declaration.commandId,
    sessionId: declaration.sessionId,
    uploadId: declaration.uploadId,
  };
}

function sha256(value: string): string {
  return createHash("sha256").update(value).digest("hex");
}

function validSession(): string {
  return JSON.stringify({
    artifacts: [],
    createdAt: 1,
    dirty: false,
    jobs: [],
    revision: 0,
    schemaVersion: 1,
    source: { fileName: "demo.fasta", format: "fasta" },
    tracks: [],
    view: {
      mode: "sequence",
      sequence: {
        geneticCodeId: 1,
        layout: "linear",
        orientation: "forward",
        paletteId: "neutral",
        selectedFeatureId: null,
        selectedRecordId: "record-1",
        selection: null,
        showFeatures: true,
        showQuality: true,
        showTranslation: true,
        synchronizedViews: true,
        viewport: null,
        wrapWidth: 60,
      },
    },
  });
}

async function temporaryDirectory(): Promise<string> {
  const directory = await mkdtemp(
    path.join(os.tmpdir(), "sequence-workbench-upload-test-"),
  );
  temporaryDirectories.push(directory);
  return directory;
}

SHA-256: c30f3938a5c97c070adbdd284e76574fdd4c5e6915977a99b0ce6c2daba1c635