|
1 | | -import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs" |
2 | | -import { tmpdir } from "node:os" |
3 | | -import path from "node:path" |
4 | | - |
5 | 1 | import { NodeContext as BrowserFrontendTestNodeContext } from "@effect/platform-node" |
| 2 | +import type { PlatformError } from "@effect/platform/Error" |
| 3 | +import * as FileSystem from "@effect/platform/FileSystem" |
| 4 | +import * as Path from "@effect/platform/Path" |
6 | 5 | import { describe, expect, it } from "@effect/vitest" |
7 | 6 | import { Effect } from "effect" |
8 | 7 | import { afterEach, beforeEach, vi } from "vitest" |
@@ -67,39 +66,72 @@ const requireEnvValue = ( |
67 | 66 | const writeWebStateFile = ( |
68 | 67 | statePath: string, |
69 | 68 | state: Readonly<Record<string, string | number>> |
70 | | -): void => { |
71 | | - mkdirSync(path.dirname(statePath), { recursive: true }) |
72 | | - writeFileSync(statePath, `${JSON.stringify(state, null, 2)}\n`) |
73 | | -} |
| 69 | +): Effect.Effect<void, PlatformError> => |
| 70 | + Effect.gen(function*(_) { |
| 71 | + const fs = yield* _(FileSystem.FileSystem) |
| 72 | + const path = yield* _(Path.Path) |
| 73 | + yield* _(fs.makeDirectory(path.dirname(statePath), { recursive: true })) |
| 74 | + yield* _(fs.writeFileString(statePath, `${JSON.stringify(state, null, 2)}\n`)) |
| 75 | + }).pipe(Effect.provide(BrowserFrontendTestNodeContext.layer)) |
| 76 | + |
| 77 | +const makeProjectsRoot = (): Effect.Effect<string, PlatformError> => |
| 78 | + Effect.gen(function*(_) { |
| 79 | + const fs = yield* _(FileSystem.FileSystem) |
| 80 | + return yield* _(fs.makeTempDirectory({ prefix: "docker-git-browser-test-" })) |
| 81 | + }).pipe(Effect.provide(BrowserFrontendTestNodeContext.layer)) |
| 82 | + |
| 83 | +const removeProjectsRoot = (root: string): Effect.Effect<void, PlatformError> => |
| 84 | + Effect.gen(function*(_) { |
| 85 | + const fs = yield* _(FileSystem.FileSystem) |
| 86 | + yield* _(fs.remove(root, { force: true, recursive: true })) |
| 87 | + }).pipe(Effect.provide(BrowserFrontendTestNodeContext.layer)) |
74 | 88 |
|
75 | 89 | describe("browser frontend command", () => { |
76 | 90 | let projectsRoot: string | null = null |
77 | 91 |
|
78 | | - beforeEach(() => { |
79 | | - vi.resetModules() |
80 | | - projectsRoot = mkdtempSync(path.join(tmpdir(), "docker-git-browser-test-")) |
81 | | - process.env["DOCKER_GIT_PROJECTS_ROOT"] = projectsRoot |
82 | | - makeNonInteractive() |
83 | | - ensureControllerReadyMock.mockReset() |
84 | | - ensureControllerReadyMock.mockImplementation(() => Effect.void) |
85 | | - runCommandCaptureMock.mockReset() |
86 | | - runCommandCaptureMock.mockImplementation(() => Effect.succeed("")) |
87 | | - runCommandExitCodeMock.mockReset() |
88 | | - runCommandExitCodeMock.mockImplementation(() => Effect.succeed(0)) |
89 | | - runCommandExitCodeStreamingMock.mockReset() |
90 | | - runCommandExitCodeStreamingMock.mockImplementation(() => Effect.succeed(0)) |
91 | | - }) |
92 | | - |
93 | | - afterEach(() => { |
94 | | - restoreTty() |
95 | | - delete process.env["DOCKER_GIT_WEB_PORT"] |
96 | | - delete process.env["DOCKER_GIT_WEB_HOST"] |
97 | | - delete process.env["DOCKER_GIT_PROJECTS_ROOT"] |
98 | | - if (projectsRoot !== null) { |
99 | | - rmSync(projectsRoot, { force: true, recursive: true }) |
100 | | - projectsRoot = null |
101 | | - } |
102 | | - }) |
| 92 | + beforeEach(() => |
| 93 | + Effect.runPromise( |
| 94 | + makeProjectsRoot().pipe( |
| 95 | + Effect.tap((root) => |
| 96 | + Effect.sync(() => { |
| 97 | + vi.resetModules() |
| 98 | + projectsRoot = root |
| 99 | + process.env["DOCKER_GIT_PROJECTS_ROOT"] = root |
| 100 | + makeNonInteractive() |
| 101 | + ensureControllerReadyMock.mockReset() |
| 102 | + ensureControllerReadyMock.mockImplementation(() => Effect.void) |
| 103 | + runCommandCaptureMock.mockReset() |
| 104 | + runCommandCaptureMock.mockImplementation(() => Effect.succeed("")) |
| 105 | + runCommandExitCodeMock.mockReset() |
| 106 | + runCommandExitCodeMock.mockImplementation(() => Effect.succeed(0)) |
| 107 | + runCommandExitCodeStreamingMock.mockReset() |
| 108 | + runCommandExitCodeStreamingMock.mockImplementation(() => Effect.succeed(0)) |
| 109 | + }) |
| 110 | + ), |
| 111 | + Effect.asVoid |
| 112 | + ) |
| 113 | + ) |
| 114 | + ) |
| 115 | + |
| 116 | + afterEach(() => |
| 117 | + Effect.runPromise( |
| 118 | + Effect.gen(function*(_) { |
| 119 | + const root = projectsRoot |
| 120 | + yield* _( |
| 121 | + Effect.sync(() => { |
| 122 | + restoreTty() |
| 123 | + delete process.env["DOCKER_GIT_WEB_PORT"] |
| 124 | + delete process.env["DOCKER_GIT_WEB_HOST"] |
| 125 | + delete process.env["DOCKER_GIT_PROJECTS_ROOT"] |
| 126 | + projectsRoot = null |
| 127 | + }) |
| 128 | + ) |
| 129 | + if (root !== null) { |
| 130 | + yield* _(removeProjectsRoot(root)) |
| 131 | + } |
| 132 | + }) |
| 133 | + ) |
| 134 | + ) |
103 | 135 |
|
104 | 136 | it.effect("starts controller and web when nothing is running", () => |
105 | 137 | Effect.gen(function*(_) { |
@@ -172,15 +204,15 @@ describe("browser frontend command", () => { |
172 | 204 | const revision = requireEnvValue(serveEnv, "DOCKER_GIT_WEB_REVISION") |
173 | 205 | const statePath = requireEnvValue(serveEnv, "DOCKER_GIT_WEB_STATE_PATH") |
174 | 206 |
|
175 | | - writeWebStateFile(statePath, { |
| 207 | + yield* _(writeWebStateFile(statePath, { |
176 | 208 | schemaVersion: 1, |
177 | 209 | revision, |
178 | 210 | pid: "123", |
179 | 211 | host: "0.0.0.0", |
180 | 212 | port: "4174", |
181 | 213 | apiBaseUrl: "http://127.0.0.1:3334", |
182 | 214 | startedAtIso: "2026-04-21T00:00:00.000Z" |
183 | | - }) |
| 215 | + })) |
184 | 216 |
|
185 | 217 | ensureControllerReadyMock.mockClear() |
186 | 218 | runCommandExitCodeMock.mockClear() |
|
0 commit comments