← Files Biological Sequence & Alignment ViewerARCHIVED FILE
src/viewer-operations.test.ts
11.6 KB · Sep 30, 2026 · 23:01 UTC
import { describe, expect, it } from "vitest";
import { queuedSequenceViewerCommandSchema } from "./viewer-commands";
import {
isSafeWorkspaceExportRelativePath,
isWorkspaceExportNameForFormat,
sequenceViewerAnalysisInputSchema,
sequenceViewerAnalysisRequestSchema,
sequenceViewerAlignInputSchema,
sequenceViewerEditInputSchema,
sequenceViewerEditRequestSchema,
sequenceViewerExportInputSchema,
sequenceViewerLoadTrackInputSchema,
sequenceViewerOperationCommandSchema,
sequenceViewerQueryInputSchema,
sequenceViewerQueryRequestSchema,
sequenceViewerSaveSessionInputSchema,
} from "./viewer-operations";
const sessionId = "11111111-1111-4111-8111-111111111111";
describe("workbench operation protocol", () => {
it.each([
["a3m", ["family.a3m"], ["family.aln", "family.a3m.exe"]],
["clustal", ["family.aln", "family.clustal", "family.clw"], ["family.sto"]],
["gtf", ["annotations.gtf", "ANNOTATIONS.GTF"], ["annotations.gff3"]],
["pdf", ["report.pdf", "REPORT.PDF"], ["report.svg", "report.pdf.exe"]],
[
"stockholm",
["family.sto", "family.stk", "family.stockholm"],
["family.a3m"],
],
] as const)(
"exposes real %s exports with exact safe format extensions",
(format, validNames, invalidNames) => {
expect(
sequenceViewerExportInputSchema.parse({
format,
sessionId,
}).format,
).toBe(format);
for (const name of validNames) {
expect(isWorkspaceExportNameForFormat(format, name)).toBe(true);
}
for (const name of invalidNames) {
expect(isWorkspaceExportNameForFormat(format, name)).toBe(false);
}
},
);
it("defaults exports to private persistence and accepts only safe relative workspace syntax", () => {
expect(
sequenceViewerExportInputSchema.parse({
format: "fasta",
sessionId,
}),
).toMatchObject({ destination: { kind: "private" } });
expect(
sequenceViewerExportInputSchema.parse({
destination: {
base: "opened-source",
kind: "workspace",
relativePath: "derived.fasta",
},
format: "fasta",
sessionId,
}).destination,
).not.toHaveProperty("collisionPolicy");
expect(
sequenceViewerExportInputSchema.safeParse({
destination: {
base: "opened-source",
collisionPolicy: "next-version",
kind: "workspace",
relativePath: "../results/derived.fasta",
},
format: "fasta",
sessionId,
}).success,
).toBe(false);
for (const relativePath of [
"",
"/tmp/derived.fasta",
"C:/tmp/derived.fasta",
"\\\\server\\share\\derived.fasta",
"file:///tmp/derived.fasta",
"exports/derived.fasta:stream",
"exports/CON.fasta",
"exports/derived.fasta.",
"exports\\derived.fasta",
"exports//derived.fasta",
"./derived.fasta",
"derived.fasta\nignored",
]) {
expect(
sequenceViewerExportInputSchema.safeParse({
destination: {
base: "opened-source",
kind: "workspace",
relativePath,
},
format: "fasta",
sessionId,
}).success,
).toBe(false);
}
});
it("rejects Windows drives, UNC paths, device names and aliasing whitespace", () => {
for (const relativePath of [
"C:/Windows/derived.fasta",
"C:\\Windows\\derived.fasta",
"\\\\server\\share\\derived.fasta",
"\\\\?\\C:\\Windows\\derived.fasta",
"exports/derived.fasta:hidden",
"exports/CON.fasta",
"exports/AUX.fasta",
"exports/COM1.fasta",
"exports/LPT9.fasta",
"exports/derived.fasta ",
" exports/derived.fasta",
"exports/trailing./derived.fasta",
]) {
expect(isSafeWorkspaceExportRelativePath(relativePath)).toBe(false);
}
expect(isSafeWorkspaceExportRelativePath("../results/derived.fasta")).toBe(
true,
);
});
it("validates paged query targets and rejects target-specific field drift", () => {
expect(
sequenceViewerQueryInputSchema.parse({
limit: 25,
sessionId,
target: "records",
}),
).toMatchObject({ limit: 25, target: "records" });
expect(
sequenceViewerQueryRequestSchema.safeParse({
end: 20,
limit: 100,
reference: "chr1",
start: 1,
target: "variants",
}).success,
).toBe(true);
expect(
sequenceViewerQueryRequestSchema.safeParse({
row: "not-valid-for-records",
limit: 100,
target: "records",
}).success,
).toBe(false);
const unpagedInput = sequenceViewerQueryInputSchema.parse({
end: 4,
sessionId,
start: 1,
target: "sequence-range",
});
expect(unpagedInput).not.toHaveProperty("limit");
const { sessionId: _sessionId, ...unpagedRequest } = unpagedInput;
expect(sequenceViewerQueryRequestSchema.parse(unpagedRequest)).toEqual({
end: 4,
start: 1,
target: "sequence-range",
});
expect(
sequenceViewerQueryRequestSchema.parse({ target: "records" }),
).toMatchObject({ limit: 100, target: "records" });
});
it("uses discriminated analysis and edit request contracts", () => {
expect(
sequenceViewerAnalysisRequestSchema.parse({
analysis: "find-orfs",
}),
).toMatchObject({
includePartial: false,
minAminoAcids: 30,
strands: "both",
});
expect(
sequenceViewerEditRequestSchema.safeParse({
operation: "replace-sequence-range",
end: 20,
sequence: "ACGT",
start: 10,
}).success,
).toBe(true);
expect(
sequenceViewerEditRequestSchema.safeParse({
operation: "replace-sequence-range",
end: 20,
start: 10,
}).success,
).toBe(false);
expect(
sequenceViewerEditRequestSchema.safeParse({
group: "responders",
operation: "assign-alignment-row-group",
rowIds: ["row-a", "row-b"],
}).success,
).toBe(true);
});
it("keeps model-facing tool objects strict", () => {
expect(
sequenceViewerAnalysisInputSchema.safeParse({
analysis: "statistics",
command: "legacy",
sessionId,
}).success,
).toBe(false);
expect(
sequenceViewerEditInputSchema.safeParse({
operation: "undo",
sessionId,
unexpected: true,
}).success,
).toBe(false);
expect(
sequenceViewerLoadTrackInputSchema.safeParse({
format: "cram",
path: "/workspace/evidence.cram",
sessionId,
}).success,
).toBe(true);
expect(
sequenceViewerLoadTrackInputSchema.safeParse({
end: 40_000,
format: "bam",
indexPath: "/workspace/evidence.bam.csi",
path: "/workspace/evidence.bam",
reference: "chr1",
sessionId,
start: 1,
}).success,
).toBe(true);
expect(
sequenceViewerLoadTrackInputSchema.safeParse({
format: "bed",
indexPath: "/workspace/evidence.bed.tbi",
path: "/workspace/evidence.bed",
sessionId,
}).success,
).toBe(false);
expect(
sequenceViewerLoadTrackInputSchema.safeParse({
end: 100_001,
format: "bam",
path: "/workspace/evidence.bam",
sessionId,
start: 1,
}).success,
).toBe(false);
});
it("keeps alignment engine selection unambiguous", () => {
expect(
sequenceViewerAlignInputSchema.safeParse({
algorithm: "builtin-pairwise",
recordIds: ["a", "b"],
sessionId,
}).success,
).toBe(true);
expect(
sequenceViewerAlignInputSchema.safeParse({
algorithm: "builtin-pairwise",
recordIds: ["a", "b", "c"],
sessionId,
}).success,
).toBe(false);
expect(
sequenceViewerAlignInputSchema.safeParse({
recordIds: ["a", "b"],
rowIds: ["row-a", "row-b"],
sessionId,
}).success,
).toBe(false);
});
it("requires saved-session names to be basenames", () => {
expect(
sequenceViewerSaveSessionInputSchema.parse({
name: "analysis.session.json",
sessionId,
}),
).toMatchObject({ name: "analysis.session.json" });
for (const name of [
"results/session.json",
"results\\session.json",
".",
"..",
]) {
expect(
sequenceViewerSaveSessionInputSchema.safeParse({ name, sessionId })
.success,
).toBe(false);
}
});
it("includes operations in the same versioned queued-command transport", () => {
const command = {
action: "query_viewer" as const,
commandId: "22222222-2222-4222-8222-222222222222",
request: { limit: 10, target: "records" as const },
revision: 4,
};
expect(
sequenceViewerOperationCommandSchema.safeParse(command).success,
).toBe(false);
expect(queuedSequenceViewerCommandSchema.parse(command)).toEqual(command);
});
it.each([
{ target: "workbench-panels", group: "sequence-tools" },
{ target: "workbench-disclosures", mode: "sequence", limit: 10 },
{ target: "workbench-feedback", mode: "sequence", limit: 10 },
{ target: "sequence-ui-state" },
{ target: "read-pileup-state" },
{ target: "quality-report" },
{ target: "chromatogram", start: 1, end: 100, limit: 500 },
{
target: "read-detail",
trackId: "track-a",
sourceReadIndex: 0,
start: 10,
end: 80,
limit: 25,
},
])(
"exposes bounded structured $target queries through both schema layers",
(request) => {
const { sessionId: _session, ...modelRequest } =
sequenceViewerQueryInputSchema.parse({ ...request, sessionId });
expect(
sequenceViewerQueryRequestSchema.parse(modelRequest),
).toMatchObject(request);
},
);
it.each([
{ target: "workbench-panels", group: "unknown" },
{ target: "workbench-disclosures", mode: "both" },
{ target: "workbench-disclosures", limit: 101 },
{ target: "workbench-feedback", limit: 101 },
{ target: "quality-report", record: "individual-read" },
{ target: "chromatogram", start: 1, end: 101 },
{ target: "chromatogram", start: 10, end: 2 },
{ target: "chromatogram", start: 1, end: 10, limit: 501 },
{ target: "read-detail", sourceReadIndex: 0 },
{ target: "read-detail", trackId: "track-a", sourceReadIndex: -1 },
{
target: "read-detail",
trackId: "track-a",
sourceReadIndex: 0,
cursor: "x".repeat(1025),
},
{ target: "sequence-ui-state", includeSourceBytes: true },
])("rejects ambiguous or unbounded $target queries", (request) => {
expect(sequenceViewerQueryRequestSchema.safeParse(request).success).toBe(
false,
);
});
it("exposes document-scope quality reports and a bounded explicit adapter screen", () => {
for (const adapterSequence of [
undefined,
"ACGTACGT",
"acgtacgt",
"A".repeat(64),
]) {
const { sessionId: _session, ...request } =
sequenceViewerAnalysisInputSchema.parse({
analysis: "quality-report",
adapterSequence,
sessionId,
});
expect(sequenceViewerAnalysisRequestSchema.parse(request)).toEqual({
analysis: "quality-report",
adapterSequence,
});
}
for (const request of [
{ analysis: "quality-report", adapterSequence: "ACGT" },
{ analysis: "quality-report", adapterSequence: "A".repeat(65) },
{ analysis: "quality-report", adapterSequence: "ACGTNCGT" },
{ analysis: "quality-report", record: "one-read" },
]) {
expect(
sequenceViewerAnalysisRequestSchema.safeParse(request).success,
).toBe(false);
}
});
});
SHA-256: bb508ad88022c4e9e210a62ba52d6ecc962874a158a11302975a5ac82e0b015d