Skip to content

Commit 7d4496e

Browse files
test(core): stabilize shell progress test (#37643)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
1 parent 1f2de53 commit 7d4496e

1 file changed

Lines changed: 46 additions & 30 deletions

File tree

packages/core/test/tool-shell.test.ts

Lines changed: 46 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import fs from "fs/promises"
22
import { realpathSync } from "node:fs"
33
import path from "path"
44
import { describe, expect, test } from "bun:test"
5-
import { DateTime, Duration, Effect, Fiber, Layer, Scope, Stream } from "effect"
5+
import { DateTime, Deferred, Duration, Effect, Fiber, Layer, Scope, Stream } from "effect"
66
import { Money } from "@opencode-ai/schema/money"
77
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
88
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
@@ -166,10 +166,10 @@ const overflowCommand = (bytes: number) =>
166166
isWindows
167167
? `[Console]::Out.Write(('x' * ${bytes})); Start-Sleep -Milliseconds 100`
168168
: `head -c ${bytes} /dev/zero | tr '\\0' 'x'`
169-
const progressOverflowCommand = (bytes: number) =>
169+
const progressOverflowCommand = (bytes: number, release: string) =>
170170
isWindows
171-
? `[Console]::Out.Write(('x' * ${bytes})); Start-Sleep -Milliseconds 1500`
172-
: `head -c ${bytes} /dev/zero | tr '\\0' 'x'; sleep 1.5`
171+
? `[Console]::Out.Write(('x' * ${bytes})); while (!(Test-Path -LiteralPath '${release}')) { Start-Sleep -Milliseconds 50 }`
172+
: `head -c ${bytes} /dev/zero | tr '\\0' 'x'; while [ ! -e '${release}' ]; do sleep 0.05; done`
173173

174174
const withSession = <A, E, R>(directory: string, body: (registry: ToolRegistry.Interface) => Effect.Effect<A, E, R>) =>
175175
Effect.gen(function* () {
@@ -417,33 +417,49 @@ describe("ShellTool", () => {
417417
),
418418
)
419419

420-
it.live("reports bounded output progress for a running command", () =>
421-
Effect.acquireUseRelease(
422-
Effect.promise(() => tmpdir()),
423-
(tmp) => {
424-
reset()
425-
const bytes = ShellTool.MAX_CAPTURE_BYTES + 1024
426-
return withSession(tmp.path, (registry) =>
427-
Effect.gen(function* () {
428-
const progress: ToolRegistry.Progress[] = []
429-
yield* settleTool(registry, {
430-
...call({ command: progressOverflowCommand(bytes) }, "call-progress"),
431-
progress: (update) => Effect.sync(() => progress.push(update)),
432-
})
420+
it.live(
421+
"reports bounded output progress for a running command",
422+
() =>
423+
Effect.acquireUseRelease(
424+
Effect.promise(() => tmpdir()),
425+
(tmp) => {
426+
reset()
427+
const release = "shell-progress-release"
428+
const releasePath = path.join(tmp.path, release)
429+
return withSession(tmp.path, (registry) =>
430+
Effect.gen(function* () {
431+
const observed = yield* Deferred.make<ToolRegistry.Progress>()
432+
yield* settleTool(registry, {
433+
...call(
434+
{ command: progressOverflowCommand(ShellTool.MAX_CAPTURE_BYTES + 1024, release) },
435+
"call-progress",
436+
),
437+
progress: (update) =>
438+
Effect.gen(function* () {
439+
if (update.structured.truncated !== true) return
440+
const content = update.content[0]
441+
if (content?.type !== "text") return
442+
if (content.text.indexOf("\n\n[output truncated; full output saved to:") !== ShellTool.MAX_CAPTURE_BYTES)
443+
return
444+
yield* Deferred.succeed(observed, update)
445+
yield* Effect.promise(() => fs.writeFile(releasePath, ""))
446+
}),
447+
})
433448

434-
expect(progress).toHaveLength(1)
435-
expect(progress[0]?.structured).toEqual({ truncated: true })
436-
const content = progress[0]?.content[0]
437-
expect(content?.type).toBe("text")
438-
if (content?.type !== "text") return
439-
expect(content.text.indexOf("\n\n[output truncated; full output saved to:")).toBe(
440-
ShellTool.MAX_CAPTURE_BYTES,
441-
)
442-
}),
443-
)
444-
},
445-
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
446-
),
449+
const progress = yield* Deferred.await(observed)
450+
expect(progress.structured).toEqual({ truncated: true })
451+
const content = progress.content[0]
452+
expect(content?.type).toBe("text")
453+
if (content?.type !== "text") return
454+
expect(content.text.indexOf("\n\n[output truncated; full output saved to:")).toBe(
455+
ShellTool.MAX_CAPTURE_BYTES,
456+
)
457+
}).pipe(Effect.ensuring(Effect.promise(() => fs.writeFile(releasePath, "")).pipe(Effect.ignore))),
458+
)
459+
},
460+
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
461+
),
462+
{ timeout: 15_000 },
447463
)
448464

449465
it.live("returns a useful timeout settlement", () =>

0 commit comments

Comments
 (0)