import { PassThrough } from "node:stream" import { afterEach, expect, test, vi } from "vitest" import { Pipeline } from "#Source/orchestration/index.ts" afterEach(() => { vi.useRealTimers() vi.restoreAllMocks() }) test("include, occurTimes and sequencialOccur evaluate stream chunks across calls", () => { const includesReady = Pipeline.include("ready") const doneTwice = Pipeline.occurTimes("done", 2) const sequenceMatched = Pipeline.sequencialOccur(["step-1", "step-2"]) expect(includesReady("pending")).toBe(false) expect(includesReady("ready now")).toBe(true) expect(doneTwice("done")).toBe(false) expect(doneTwice("ignored")).toBe(false) expect(doneTwice("done again")).toBe(true) expect(sequenceMatched("step-2")).toBe(false) expect(sequenceMatched("step-1")).toBe(false) expect(sequenceMatched("step-2 completed")).toBe(true) }) test("defineTask uses the default resolver and logs task boundaries", async () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined) const runner = vi.fn(() => "runner-result") const task = Pipeline.defineTask({ name: "default task", runner, }) await expect(task()).resolves.toBeUndefined() expect(runner).toHaveBeenCalledTimes(1) expect(logSpy).toHaveBeenNthCalledWith(1, "\n\n[Pipeline] start default task...\n\n") expect(logSpy).toHaveBeenNthCalledWith(2, "\n\n[Pipeline] end default task...\n\n") }) test("defineTask passes runner output to a custom resolver and waits for resolve", async () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined) const runner = vi.fn(() => 42) let settled = false let resolveTask: (() => void) | undefined const task = Pipeline.defineTask({ name: "custom task", runner, resolver: ({ runnerReturn, resolve }) => { expect(runnerReturn).toBe(42) resolveTask = () => { resolve() } }, }) const taskPromise = (async () => { await task() settled = true })() await Promise.resolve() expect(settled).toBe(false) expect(resolveTask).toBeTypeOf("function") resolveTask?.() await taskPromise expect(runner).toHaveBeenCalledTimes(1) expect(logSpy).toHaveBeenNthCalledWith(1, "\n\n[Pipeline] start custom task...\n\n") expect(logSpy).toHaveBeenNthCalledWith(2, "\n\n[Pipeline] end custom task...\n\n") }) test("defineStreamTask resolves after all success conditions are satisfied", async () => { const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined) const stdout = new PassThrough() const task = Pipeline.defineStreamTask({ name: "stream task", runner: () => ({ stdout }), itSuccessWhen: [ Pipeline.include("ready"), Pipeline.occurTimes("done", 2), Pipeline.sequencialOccur(["phase-1", "phase-2"]), ], }) let settled = false const taskPromise = (async () => { await task() settled = true })() stdout.write("ready phase-1\n") await Promise.resolve() expect(settled).toBe(false) stdout.write("done\n") await Promise.resolve() expect(settled).toBe(false) stdout.write("phase-2 done\n") await taskPromise stdout.end() expect(logSpy).toHaveBeenCalledWith("ready phase-1\n") expect(logSpy).toHaveBeenCalledWith("done\n") expect(logSpy).toHaveBeenCalledWith("phase-2 done\n") }) test("defineSleepTask resolves only after the configured duration", async () => { vi.useFakeTimers() const logSpy = vi.spyOn(console, "log").mockImplementation(() => undefined) const task = Pipeline.defineSleepTask({ name: "sleep task", duration: 50, }) let settled = false const taskPromise = (async () => { await task() settled = true })() await Promise.resolve() expect(settled).toBe(false) await vi.advanceTimersByTimeAsync(49) expect(settled).toBe(false) await vi.advanceTimersByTimeAsync(1) await taskPromise expect(logSpy).toHaveBeenNthCalledWith(1, "\n\n[Pipeline] start sleep task...\n\n") expect(logSpy).toHaveBeenNthCalledWith(2, "\n\n[Pipeline] end sleep task...\n\n") })