import test from "node:test"; import assert from "node:assert/strict"; import { ASSET_TYPES, WORKFLOW_RUN_STATUSES, WORKSPACE_TYPES, } from "../../../packages/contracts/src/domain.ts"; import { buildCustomNodeEnvelopePreview, formatCustomNodeValidationIssue, validateCustomNodeDefinition, } from "../../../packages/contracts/src/custom-node.ts"; import { normalizeWorkflowInputBindings, splitWorkflowInputBindings, } from "../../../packages/contracts/src/workflow-input.ts"; import { createMongoConnectionUri } from "../src/common/mongo/mongo.module.ts"; import { ASSET_COLLECTION_NAME, PROJECT_COLLECTION_NAME, WORKFLOW_DEFINITION_COLLECTION_NAME, WORKSPACE_COLLECTION_NAME, } from "../src/common/mongo/schemas/index.ts"; test("workspace types include personal and team", () => { assert.deepEqual(WORKSPACE_TYPES, ["personal", "team"]); }); test("asset types include raw and dataset-oriented sources", () => { assert.equal(ASSET_TYPES.includes("raw_file"), true); assert.equal(ASSET_TYPES.includes("standard_dataset"), true); assert.equal(ASSET_TYPES.includes("rosbag"), true); }); test("workflow run statuses include the documented execution states", () => { assert.equal(WORKFLOW_RUN_STATUSES.includes("pending"), true); assert.equal(WORKFLOW_RUN_STATUSES.includes("running"), true); assert.equal(WORKFLOW_RUN_STATUSES.includes("success"), true); assert.equal(WORKFLOW_RUN_STATUSES.includes("failed"), true); }); test("mongo connection uri builder uses environment-style config", () => { assert.equal( createMongoConnectionUri({ username: "emboflow", password: "emboflow", host: "localhost", port: 27017, database: "emboflow", }), "mongodb://emboflow:emboflow@localhost:27017/emboflow?authSource=admin", ); }); test("schema collection names match the core domain objects", () => { assert.equal(WORKSPACE_COLLECTION_NAME, "workspaces"); assert.equal(PROJECT_COLLECTION_NAME, "projects"); assert.equal(ASSET_COLLECTION_NAME, "assets"); assert.equal(WORKFLOW_DEFINITION_COLLECTION_NAME, "workflow_definitions"); }); test("custom node validation accepts a valid docker image utility node", () => { const issues = validateCustomNodeDefinition({ name: "Merge Assets", category: "Utility", source: { kind: "image", image: "python:3.11-alpine", command: ["python3", "-c", "print('merge')"], }, contract: { inputMode: "multi_asset_set", outputMode: "asset_set", artifactType: "json", }, }); assert.deepEqual(issues, []); }); test("custom node validation rejects invalid dockerfile and impossible source contract combinations", () => { const issues = validateCustomNodeDefinition({ name: "Bad Source", category: "Source", source: { kind: "dockerfile", dockerfileContent: "CMD [\"python3\"]", }, contract: { inputMode: "multi_asset_set", outputMode: "report", artifactType: "json", }, }); assert.deepEqual(issues, ["source_cannot_be_multi_input", "dockerfile_missing_from"]); assert.equal( formatCustomNodeValidationIssue("dockerfile_missing_from"), "custom node dockerfile must include a FROM instruction", ); }); test("custom node envelope preview reflects the declared input and output contract", () => { const preview = buildCustomNodeEnvelopePreview({ inputMode: "multi_asset_set", outputMode: "asset_set_with_report", artifactType: "json", }); assert.deepEqual(preview.input.context.assetIds, ["asset-123"]); assert.deepEqual(preview.input.context.upstreamResults[0]?.result?.assetIds, ["asset-123"]); assert.deepEqual(preview.output.result.assetIds, ["asset-123"]); assert.equal(preview.output.result.artifactType, "json"); assert.equal(typeof preview.output.result.report, "object"); }); test("workflow input bindings normalize legacy asset and dataset inputs into one contract", () => { const bindings = normalizeWorkflowInputBindings({ assetIds: ["asset-1", "asset-1", ""], datasetIds: ["dataset-1", "dataset-2", "dataset-1"], }); assert.deepEqual(bindings, [ { kind: "asset", id: "asset-1" }, { kind: "dataset", id: "dataset-1" }, { kind: "dataset", id: "dataset-2" }, ]); assert.deepEqual(splitWorkflowInputBindings(bindings), { assetIds: ["asset-1"], datasetIds: ["dataset-1", "dataset-2"], }); }); test("workflow input bindings prefer explicit bindings over legacy fallback arrays", () => { const bindings = normalizeWorkflowInputBindings({ inputBindings: [ { kind: "dataset", id: "dataset-9" }, { kind: "dataset", id: "dataset-9" }, { kind: "asset", id: "asset-2" }, ], assetIds: ["asset-legacy"], }); assert.deepEqual(bindings, [ { kind: "dataset", id: "dataset-9" }, { kind: "asset", id: "asset-2" }, ]); });