138 lines · 4.4 KB · TypeScript
RawDownload
import * as NodeServices from "@effect/platform-node/NodeServices";import { describe, expect, it } from "@effect/vitest";import * as Duration from "effect/Duration";import * as Effect from "effect/Effect";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import { TestClock } from "effect/testing"; import { VcsProcessExitError, VcsProcessTimeoutError } from "@t3tools/contracts";import * as VcsProcess from "./VcsProcess.ts"; const run = (input: VcsProcess.VcsProcessInput) =>  Effect.gen(function* () {    const process = yield* VcsProcess.VcsProcess;    return yield* process.run(input);  }); const liveLayer = VcsProcess.layer.pipe(Layer.provide(NodeServices.layer)); const provideLive = <A, E, R>(effect: Effect.Effect<A, E, R | VcsProcess.VcsProcess>) =>  effect.pipe(Effect.provide(liveLayer)); describe("VcsProcess.run", () => {  it.effect("collects stdout", () =>    Effect.gen(function* () {      const result = yield* run({        operation: "test.stdout",        command: "node",        args: ["-e", "process.stdout.write('hello')"],        cwd: process.cwd(),      });       expect(result.stdout).toBe("hello");      expect(result.stderr).toBe("");      expect(result.stdoutTruncated).toBe(false);      expect(result.stderrTruncated).toBe(false);    }).pipe(provideLive),  );   it.effect("writes stdin before waiting for exit", () =>    Effect.gen(function* () {      const result = yield* run({        operation: "test.stdin",        command: "node",        args: [          "-e",          [            "process.stdin.setEncoding('utf8');",            "let data='';",            "process.stdin.on('data', chunk => { data += chunk; });",            "process.stdin.on('end', () => { process.stdout.write(data); });",          ].join(""),        ],        cwd: process.cwd(),        stdin: "stdin payload",      });       expect(result.stdout).toBe("stdin payload");    }).pipe(provideLive),  );   it.effect("fails with VcsProcessExitError for non-zero exits by default", () =>    Effect.gen(function* () {      const error = yield* run({        operation: "test.exit",        command: "node",        args: ["-e", "process.stderr.write('boom'); process.exit(2)"],        cwd: process.cwd(),      }).pipe(Effect.flip);       expect(error).toBeInstanceOf(VcsProcessExitError);    }).pipe(provideLive),  );   it.effect("returns output when non-zero exits are allowed", () =>    Effect.gen(function* () {      const result = yield* run({        operation: "test.allowed-exit",        command: "node",        args: ["-e", "process.stderr.write('boom'); process.exit(2)"],        cwd: process.cwd(),        allowNonZeroExit: true,      });       expect(result.exitCode).toBe(2);      expect(result.stderr).toBe("boom");    }).pipe(provideLive),  );   it.effect("truncates output and appends the marker when requested", () =>    Effect.gen(function* () {      const result = yield* run({        operation: "test.truncate-marker",        command: "node",        args: ["-e", "process.stdout.write('x'.repeat(2048))"],        cwd: process.cwd(),        maxOutputBytes: 128,        appendTruncationMarker: true,      });       expect(result.stdoutTruncated).toBe(true);      expect(result.stdout).toContain("[truncated]");      expect(result.stderrTruncated).toBe(false);    }).pipe(provideLive),  );   it.effect("truncates without the marker when truncation markers are disabled", () =>    Effect.gen(function* () {      const result = yield* run({        operation: "test.truncate-silent",        command: "node",        args: ["-e", "process.stdout.write('x'.repeat(2048))"],        cwd: process.cwd(),        maxOutputBytes: 128,      });       expect(result.stdoutTruncated).toBe(true);      expect(result.stdout).not.toContain("[truncated]");    }).pipe(provideLive),  );   it.effect("fails with VcsProcessTimeoutError on timeout", () =>    Effect.gen(function* () {      const errorFiber = yield* run({        operation: "test.timeout",        command: "node",        args: ["-e", "setTimeout(() => {}, 5000)"],        cwd: process.cwd(),        timeoutMs: 50,      }).pipe(Effect.flip, Effect.forkScoped);      yield* Effect.yieldNow;      yield* TestClock.adjust(Duration.millis(50));      const error = yield* Fiber.join(errorFiber);       expect(error).toBeInstanceOf(VcsProcessTimeoutError);    }).pipe(provideLive),  );});