diff --git a/lib/BasePipelineSolver.ts b/lib/BasePipelineSolver.ts index 83219bd..408dc6a 100644 --- a/lib/BasePipelineSolver.ts +++ b/lib/BasePipelineSolver.ts @@ -225,7 +225,10 @@ export abstract class BasePipelineSolver extends BaseSolver { * Get the output from a specific pipeline stage */ getStageOutput(stageOutput: string): T | undefined { - return this.pipelineOutputs[stageOutput] + if (!Object.hasOwn(this.pipelineOutputs, stageOutput)) { + return undefined + } + return this.pipelineOutputs[stageOutput] as T } /** @@ -239,7 +242,7 @@ export abstract class BasePipelineSolver extends BaseSolver { * Check if a step has completed and produced output */ hasStageOutput(stageName: string): boolean { - return stageName in this.pipelineOutputs + return Object.hasOwn(this.pipelineOutputs, stageName) } /** diff --git a/tests/pipeline-output-lookup.test.ts b/tests/pipeline-output-lookup.test.ts new file mode 100644 index 0000000..1601ff2 --- /dev/null +++ b/tests/pipeline-output-lookup.test.ts @@ -0,0 +1,51 @@ +import { expect, test } from "bun:test" +import { BasePipelineSolver } from "../lib/BasePipelineSolver" +import { BaseSolver } from "../lib/BaseSolver" + +class EmptyPipeline extends BasePipelineSolver> { + pipelineDef = [] +} + +class ZeroOutputSolver extends BaseSolver { + override _step() { + this.solved = true + } + + override getOutput() { + return 0 + } +} + +class ZeroOutputPipeline extends BasePipelineSolver> { + zeroSolver?: ZeroOutputSolver + + pipelineDef = [ + { + solverName: "zeroSolver", + solverClass: ZeroOutputSolver, + getConstructorParams: () => [], + }, + ] +} + +for (const name of ["constructor", "toString", "__proto__"]) { + test(`no output exists for inherited Object property ${name}`, () => { + const pipeline = new EmptyPipeline({}) + expect(Object.keys(pipeline.getAllOutputs())).toEqual([]) + expect(pipeline.hasStageOutput(name)).toBe(false) + expect(pipeline.getStageOutput(name)).toBeUndefined() + }) +} + +test("missing ordinary stage names still report no output", () => { + const pipeline = new EmptyPipeline({}) + expect(pipeline.hasStageOutput("zeroSolver")).toBe(false) + expect(pipeline.getStageOutput("zeroSolver")).toBeUndefined() +}) + +test("zero-valued completed outputs remain valid", () => { + const pipeline = new ZeroOutputPipeline({}) + pipeline.solve() + expect(pipeline.hasStageOutput("zeroSolver")).toBe(true) + expect(pipeline.getStageOutput("zeroSolver")).toEqual(0) +})