cloudaxe-opencode/packages/opencode/test/testing/simulation/spawner.test.ts
James Long 27de1ee982 wip
2026-05-17 14:57:41 -04:00

78 lines
3.2 KiB
TypeScript

import { describe, expect } from "bun:test"
import { Effect, Layer, Stream } from "effect"
import { ChildProcess } from "effect/unstable/process"
import { ChildProcessSpawner } from "effect/unstable/process/ChildProcessSpawner"
import { InMemoryFs } from "just-bash"
import { SimulationFileSystem } from "../../../src/testing/simulation/filesystem"
import { SimulationSpawner } from "../../../src/testing/simulation/spawner"
import { testEffect } from "../../lib/effect"
const root = "/opencode"
const fs = new InMemoryFs()
const it = testEffect(
Layer.mergeAll(SimulationFileSystem.layer({ root, fs }), SimulationSpawner.layer({ root, fs })),
)
describe("SimulationSpawner", () => {
it.effect("runs shell commands through just-bash", () =>
Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner
const handle = yield* spawner.spawn(
ChildProcess.make("printf 'hello' > file.txt && cat file.txt", [], { cwd: root, shell: "/bin/bash" }),
).pipe(Effect.scoped)
expect(yield* Stream.mkString(Stream.decodeText(handle.stdout))).toBe("hello")
expect(Number(yield* handle.exitCode)).toBe(0)
expect(yield* Effect.promise(() => fs.readFile("/opencode/file.txt"))).toBe("hello")
}),
)
it.effect("extracts shell command text from -lc", () =>
Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner
const handle = yield* spawner.spawn(ChildProcess.make("bash", ["-lc", "printf ok"], { cwd: root })).pipe(Effect.scoped)
expect(yield* Stream.mkString(Stream.decodeText(handle.stdout))).toBe("ok")
expect(Number(yield* handle.exitCode)).toBe(0)
}),
)
it.effect("fakes git discovery commands", () =>
Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner
const [common, topLevel, revision] = yield* Effect.all(
[
spawner.spawn(ChildProcess.make("git", ["rev-parse", "--git-common-dir"], { cwd: root })).pipe(Effect.scoped),
spawner.spawn(ChildProcess.make("git", ["rev-parse", "--show-toplevel"], { cwd: root })).pipe(Effect.scoped),
spawner.spawn(ChildProcess.make("git", ["rev-list", "--max-parents=0", "HEAD"], { cwd: root })).pipe(Effect.scoped),
],
{ concurrency: 3 },
)
expect(yield* Stream.mkString(Stream.decodeText(common.stdout))).toBe(".git\n")
expect(yield* Stream.mkString(Stream.decodeText(topLevel.stdout))).toBe("/opencode\n")
expect(yield* Stream.mkString(Stream.decodeText(revision.stdout))).toBe("0000000000000000000000000000000000000000\n")
}),
)
it.effect("rejects unsupported non-shell commands", () =>
Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner
const exit = yield* spawner.spawn(ChildProcess.make("git", ["status"], { cwd: root })).pipe(Effect.scoped, Effect.exit)
expect(exit._tag).toBe("Failure")
}),
)
it.effect("rejects shell commands outside the simulation root", () =>
Effect.gen(function* () {
const spawner = yield* ChildProcessSpawner
const exit = yield* spawner.spawn(ChildProcess.make("printf blocked", [], { cwd: "/tmp", shell: "/bin/bash" })).pipe(
Effect.scoped,
Effect.exit,
)
expect(exit._tag).toBe("Failure")
}),
)
})