test(core): stabilize shell progress test (#37643)
Co-authored-by: Aiden Cline <rekram1-node@users.noreply.github.com>
This commit is contained in:
parent
1f2de535aa
commit
7d4496eafc
1 changed files with 46 additions and 30 deletions
|
|
@ -2,7 +2,7 @@ import fs from "fs/promises"
|
||||||
import { realpathSync } from "node:fs"
|
import { realpathSync } from "node:fs"
|
||||||
import path from "path"
|
import path from "path"
|
||||||
import { describe, expect, test } from "bun:test"
|
import { describe, expect, test } from "bun:test"
|
||||||
import { DateTime, Duration, Effect, Fiber, Layer, Scope, Stream } from "effect"
|
import { DateTime, Deferred, Duration, Effect, Fiber, Layer, Scope, Stream } from "effect"
|
||||||
import { Money } from "@opencode-ai/schema/money"
|
import { Money } from "@opencode-ai/schema/money"
|
||||||
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
|
||||||
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
import { LayerNode } from "@opencode-ai/core/effect/layer-node"
|
||||||
|
|
@ -166,10 +166,10 @@ const overflowCommand = (bytes: number) =>
|
||||||
isWindows
|
isWindows
|
||||||
? `[Console]::Out.Write(('x' * ${bytes})); Start-Sleep -Milliseconds 100`
|
? `[Console]::Out.Write(('x' * ${bytes})); Start-Sleep -Milliseconds 100`
|
||||||
: `head -c ${bytes} /dev/zero | tr '\\0' 'x'`
|
: `head -c ${bytes} /dev/zero | tr '\\0' 'x'`
|
||||||
const progressOverflowCommand = (bytes: number) =>
|
const progressOverflowCommand = (bytes: number, release: string) =>
|
||||||
isWindows
|
isWindows
|
||||||
? `[Console]::Out.Write(('x' * ${bytes})); Start-Sleep -Milliseconds 1500`
|
? `[Console]::Out.Write(('x' * ${bytes})); while (!(Test-Path -LiteralPath '${release}')) { Start-Sleep -Milliseconds 50 }`
|
||||||
: `head -c ${bytes} /dev/zero | tr '\\0' 'x'; sleep 1.5`
|
: `head -c ${bytes} /dev/zero | tr '\\0' 'x'; while [ ! -e '${release}' ]; do sleep 0.05; done`
|
||||||
|
|
||||||
const withSession = <A, E, R>(directory: string, body: (registry: ToolRegistry.Interface) => Effect.Effect<A, E, R>) =>
|
const withSession = <A, E, R>(directory: string, body: (registry: ToolRegistry.Interface) => Effect.Effect<A, E, R>) =>
|
||||||
Effect.gen(function* () {
|
Effect.gen(function* () {
|
||||||
|
|
@ -417,33 +417,49 @@ describe("ShellTool", () => {
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
it.live("reports bounded output progress for a running command", () =>
|
it.live(
|
||||||
Effect.acquireUseRelease(
|
"reports bounded output progress for a running command",
|
||||||
Effect.promise(() => tmpdir()),
|
() =>
|
||||||
(tmp) => {
|
Effect.acquireUseRelease(
|
||||||
reset()
|
Effect.promise(() => tmpdir()),
|
||||||
const bytes = ShellTool.MAX_CAPTURE_BYTES + 1024
|
(tmp) => {
|
||||||
return withSession(tmp.path, (registry) =>
|
reset()
|
||||||
Effect.gen(function* () {
|
const release = "shell-progress-release"
|
||||||
const progress: ToolRegistry.Progress[] = []
|
const releasePath = path.join(tmp.path, release)
|
||||||
yield* settleTool(registry, {
|
return withSession(tmp.path, (registry) =>
|
||||||
...call({ command: progressOverflowCommand(bytes) }, "call-progress"),
|
Effect.gen(function* () {
|
||||||
progress: (update) => Effect.sync(() => progress.push(update)),
|
const observed = yield* Deferred.make<ToolRegistry.Progress>()
|
||||||
})
|
yield* settleTool(registry, {
|
||||||
|
...call(
|
||||||
|
{ command: progressOverflowCommand(ShellTool.MAX_CAPTURE_BYTES + 1024, release) },
|
||||||
|
"call-progress",
|
||||||
|
),
|
||||||
|
progress: (update) =>
|
||||||
|
Effect.gen(function* () {
|
||||||
|
if (update.structured.truncated !== true) return
|
||||||
|
const content = update.content[0]
|
||||||
|
if (content?.type !== "text") return
|
||||||
|
if (content.text.indexOf("\n\n[output truncated; full output saved to:") !== ShellTool.MAX_CAPTURE_BYTES)
|
||||||
|
return
|
||||||
|
yield* Deferred.succeed(observed, update)
|
||||||
|
yield* Effect.promise(() => fs.writeFile(releasePath, ""))
|
||||||
|
}),
|
||||||
|
})
|
||||||
|
|
||||||
expect(progress).toHaveLength(1)
|
const progress = yield* Deferred.await(observed)
|
||||||
expect(progress[0]?.structured).toEqual({ truncated: true })
|
expect(progress.structured).toEqual({ truncated: true })
|
||||||
const content = progress[0]?.content[0]
|
const content = progress.content[0]
|
||||||
expect(content?.type).toBe("text")
|
expect(content?.type).toBe("text")
|
||||||
if (content?.type !== "text") return
|
if (content?.type !== "text") return
|
||||||
expect(content.text.indexOf("\n\n[output truncated; full output saved to:")).toBe(
|
expect(content.text.indexOf("\n\n[output truncated; full output saved to:")).toBe(
|
||||||
ShellTool.MAX_CAPTURE_BYTES,
|
ShellTool.MAX_CAPTURE_BYTES,
|
||||||
)
|
)
|
||||||
}),
|
}).pipe(Effect.ensuring(Effect.promise(() => fs.writeFile(releasePath, "")).pipe(Effect.ignore))),
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
|
(tmp) => Effect.promise(() => tmp[Symbol.asyncDispose]().then(() => undefined)),
|
||||||
),
|
),
|
||||||
|
{ timeout: 15_000 },
|
||||||
)
|
)
|
||||||
|
|
||||||
it.live("returns a useful timeout settlement", () =>
|
it.live("returns a useful timeout settlement", () =>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue