workspace-modal.test.ts 2.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
  1. import fs from "node:fs"
  2. import os from "node:os"
  3. import path from "node:path"
  4. import { expect, test } from "bun:test"
  5. import { AppNodeBuilder } from "@opencode-ai/core/effect/app-node-builder"
  6. import { makeFiles } from "@opencode-ai/core/environment"
  7. import { Workspace } from "@opencode-ai/core/workspace"
  8. import { WorkspaceDriver } from "@opencode-ai/core/workspace/driver"
  9. import { Effect, Layer } from "effect"
  10. import { TestClock } from "effect/testing"
  11. import { modalWorkspaceDriver, provider } from "../src/workspace/modal-workspace"
  12. const enabled =
  13. !!process.env.OPENCODE_TEST_MODAL &&
  14. ((!!process.env.MODAL_TOKEN_ID && !!process.env.MODAL_TOKEN_SECRET) ||
  15. fs.existsSync(path.join(os.homedir(), ".modal.toml")))
  16. const testLayer = Layer.provideMerge(
  17. AppNodeBuilder.build(Workspace.configured({ idleThreshold: "1 minute", pollInterval: "1 minute" }), [
  18. [
  19. WorkspaceDriver.node,
  20. WorkspaceDriver.registryNode({ [provider]: modalWorkspaceDriver({ app: "opencode-workspace-tests" }) }),
  21. ],
  22. ]),
  23. TestClock.layer(),
  24. )
  25. const modalTest = enabled ? test : test.skip
  26. modalTest(
  27. "wakes a workspace from its filesystem snapshot",
  28. () =>
  29. Effect.runPromise(
  30. Effect.gen(function* () {
  31. const workspace = yield* Workspace.Service
  32. yield* Effect.acquireUseRelease(
  33. workspace.create(provider),
  34. (created) =>
  35. Effect.gen(function* () {
  36. const environment = yield* workspace.connect(created.id)
  37. const files = makeFiles(environment)
  38. const file = `/tmp/opencode-workspace-${crypto.randomUUID()}.txt`
  39. yield* files.write(file, new TextEncoder().encode("survived snapshot"))
  40. yield* TestClock.adjust("2 minutes")
  41. const restored = yield* files.read(file)
  42. expect(new TextDecoder().decode(restored.bytes)).toBe("survived snapshot")
  43. }),
  44. (created) => workspace.destroy(created.id).pipe(Effect.ignore),
  45. )
  46. }).pipe(Effect.scoped, Effect.provide(testLayer)),
  47. ),
  48. 180_000,
  49. )