440 lines · 13.8 KB · TypeScript
RawDownload
import { assert, describe, it } from "@effect/vitest";import { vi } from "vite-plus/test";import * as Deferred from "effect/Deferred";import * as Effect from "effect/Effect";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as PlatformError from "effect/PlatformError";import * as Sink from "effect/Sink";import * as Stream from "effect/Stream";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process";import * as RelayClient from "@t3tools/shared/relayClient"; import * as ServerSecretStore from "../auth/ServerSecretStore.ts";import * as ManagedEndpointRuntime from "./ManagedEndpointRuntime.ts"; const relayClientAvailableLayer = Layer.succeed(  RelayClient.RelayClient,  RelayClient.RelayClient.of({    resolve: Effect.succeed({      status: "available",      executablePath: "cloudflared",      source: "path",      version: RelayClient.CLOUDFLARED_VERSION,    }),    install: Effect.die("unused"),    installWithProgress: () => Effect.die("unused"),  }),); const runtimeDependencies = (  spawner: ReturnType<typeof ChildProcessSpawner.make>,  relayClientLayer = relayClientAvailableLayer,) =>  Layer.mergeAll(    Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner),    relayClientLayer,    Layer.mock(ServerSecretStore.ServerSecretStore)({      get: () => Effect.succeed(Option.none()),    }),  ); const buildCloudManagedEndpointRuntime = (  spawner: ReturnType<typeof ChildProcessSpawner.make>,  relayClientLayer = relayClientAvailableLayer,) =>  Effect.gen(function* () {    const context = yield* Layer.build(      ManagedEndpointRuntime.layer.pipe(        Layer.provide(runtimeDependencies(spawner, relayClientLayer)),      ),    );    return yield* Effect.service(ManagedEndpointRuntime.CloudManagedEndpointRuntime).pipe(      Effect.provide(context),    );  }); function makeHandle(input: {  readonly pid: number;  readonly onKill: () => void;  readonly isRunning?: () => boolean;  readonly exitCode?: Effect.Effect<ChildProcessSpawner.ExitCode>;}) {  return ChildProcessSpawner.makeHandle({    pid: ChildProcessSpawner.ProcessId(input.pid),    exitCode: input.exitCode ?? Effect.never,    isRunning: Effect.sync(() => input.isRunning?.() ?? true),    kill: () =>      Effect.sync(() => {        input.onKill();      }),    unref: Effect.succeed(Effect.void),    stdin: Sink.drain,    stdout: Stream.empty,    stderr: Stream.empty,    all: Stream.empty,    getInputFd: () => Sink.drain,    getOutputFd: () => Stream.empty,  });} describe("CloudManagedEndpointRuntime", () => {  it("classifies Cloudflare connection and warning output", () => {    assert.equal(      ManagedEndpointRuntime.classifyRelayClientOutput(        "2026-06-17T02:00:00Z INF Registered tunnel connection connIndex=0",      ),      "connected",    );    assert.equal(      ManagedEndpointRuntime.classifyRelayClientOutput(        "2026-06-17T02:00:00Z ERR Failed to serve tunnel connection",      ),      "warning",    );    assert.equal(      ManagedEndpointRuntime.classifyRelayClientOutput(        "2026-06-17T02:00:00Z INF Starting metrics server",      ),      "debug",    );  });   it.effect("starts, deduplicates, rotates, and stops the Cloudflare connector", () =>    Effect.gen(function* () {      const spawned: Array<ChildProcess.StandardCommand> = [];      const killed: Array<number> = [];      let nextPid = 100;      const spawner = ChildProcessSpawner.make((command) =>        Effect.gen(function* () {          if (!ChildProcess.isStandardCommand(command)) {            throw new Error("Expected standard command.");          }          spawned.push(command);          const pid = nextPid;          nextPid += 1;          const handle = makeHandle({            pid,            onKill: () => {              killed.push(pid);            },          });          yield* Effect.addFinalizer(() => handle.kill().pipe(Effect.ignore));          return handle;        }),      );      const runtime = yield* buildCloudManagedEndpointRuntime(spawner);       yield* runtime.applyConfig(        Option.some({          providerKind: "cloudflare_tunnel",          connectorToken: "token-1",          tunnelId: "tunnel-1",          tunnelName: "t3-code-env-1",        }),      );      yield* runtime.applyConfig(        Option.some({          providerKind: "cloudflare_tunnel",          connectorToken: "token-1",          tunnelId: "tunnel-1",          tunnelName: "t3-code-env-1",        }),      );      yield* runtime.applyConfig(        Option.some({          providerKind: "cloudflare_tunnel",          connectorToken: "token-2",          tunnelId: "tunnel-1",          tunnelName: "t3-code-env-1",        }),      );      const stopped = yield* runtime.applyConfig(Option.none());       assert.deepEqual(        spawned.map((command) => command.command),        ["cloudflared", "cloudflared"],      );      assert.deepEqual(        spawned.map((command) => command.args),        [          ["tunnel", "run"],          ["tunnel", "run"],        ],      );      assert.deepEqual(        spawned.map((command) => command.options.env?.TUNNEL_TOKEN),        ["token-1", "token-2"],      );      assert.deepEqual(        spawned.map((command) => command.options.stdout),        ["pipe", "pipe"],      );      assert.deepEqual(        spawned.map((command) => command.options.stderr),        ["pipe", "pipe"],      );      assert.deepEqual(        spawned.map((command) => command.options.detached),        [false, false],      );      assert.deepEqual(        spawned.map((command) => command.options.shell),        [false, false],      );      assert.deepEqual(killed, [100, 101]);      assert.deepEqual(stopped, { status: "disabled" });    }),  );   it.effect("stops an active connector when a non-Cloudflare runtime config is applied", () =>    Effect.gen(function* () {      const killed: Array<number> = [];      const spawner = ChildProcessSpawner.make(() =>        Effect.gen(function* () {          const handle = makeHandle({            pid: 200,            onKill: () => {              killed.push(200);            },          });          yield* Effect.addFinalizer(() => handle.kill().pipe(Effect.ignore));          return handle;        }),      );      const runtime = yield* buildCloudManagedEndpointRuntime(spawner);       const started = yield* runtime.applyConfig(        Option.some({          providerKind: "cloudflare_tunnel",          connectorToken: "token",        }),      );      const unsupported = yield* runtime.applyConfig(        Option.some({          providerKind: "manual",          connectorToken: "manual-token",        }),      );       assert.equal(started.status, "running");      assert.deepEqual(unsupported, { status: "unsupported", providerKind: "manual" });      assert.deepEqual(killed, [200]);    }),  );   it.effect("restarts the connector when the active process has exited", () =>    Effect.gen(function* () {      const spawned: Array<number> = [];      const killed: Array<number> = [];      let firstRunning = true;      const spawner = ChildProcessSpawner.make(() =>        Effect.gen(function* () {          const pid = spawned.length === 0 ? 300 : 301;          spawned.push(pid);          const handle = makeHandle({            pid,            isRunning: () => (pid === 300 ? firstRunning : true),            onKill: () => {              killed.push(pid);            },          });          yield* Effect.addFinalizer(() => handle.kill().pipe(Effect.ignore));          return handle;        }),      );      const runtime = yield* buildCloudManagedEndpointRuntime(spawner);      const config = {        providerKind: "cloudflare_tunnel" as const,        connectorToken: "token",        tunnelId: "tunnel-1",      };       const first = yield* runtime.applyConfig(Option.some(config));      firstRunning = false;      const second = yield* runtime.applyConfig(Option.some(config));       if (first.status !== "running") {        assert.fail(`Expected first connector to be running, got ${first.status}`);      }      assert.equal(first.pid, 300);      if (second.status !== "running") {        assert.fail(`Expected second connector to be running, got ${second.status}`);      }      assert.equal(second.pid, 301);      assert.deepEqual(spawned, [300, 301]);      assert.deepEqual(killed, [300]);    }),  );   it.effect("supervises the active connector and restarts it after process exit", () =>    Effect.gen(function* () {      const spawned: Array<number> = [];      const killed: Array<number> = [];      const firstExit = yield* Deferred.make<ChildProcessSpawner.ExitCode>();      const secondSpawned = yield* Deferred.make<void>();      const spawner = ChildProcessSpawner.make(() =>        Effect.gen(function* () {          const pid = spawned.length === 0 ? 400 : 401;          spawned.push(pid);          if (pid === 401) {            yield* Deferred.succeed(secondSpawned, undefined);          }          const handle = makeHandle({            pid,            exitCode:              pid === 400                ? Deferred.await(firstExit)                : (Effect.never as Effect.Effect<ChildProcessSpawner.ExitCode>),            onKill: () => {              killed.push(pid);            },          });          yield* Effect.addFinalizer(() => handle.kill().pipe(Effect.ignore));          return handle;        }),      );      const runtime = yield* buildCloudManagedEndpointRuntime(spawner);       const started = yield* runtime.applyConfig(        Option.some({          providerKind: "cloudflare_tunnel",          connectorToken: "token",          tunnelId: "tunnel-1",        }),      );      yield* Deferred.succeed(firstExit, ChildProcessSpawner.ExitCode(1));      yield* Deferred.await(secondSpawned);       if (started.status !== "running") {        assert.fail(`Expected connector to be running, got ${started.status}`);      }      assert.equal(started.pid, 400);      assert.deepEqual(spawned, [400, 401]);      assert.deepEqual(killed, [400]);    }),  );   it.effect("serializes concurrent connector config changes", () =>    Effect.gen(function* () {      const spawned: Array<number> = [];      const killed: Array<number> = [];      const firstSpawnEntered = yield* Deferred.make<void>();      const releaseFirstSpawn = yield* Deferred.make<void>();      const spawner = ChildProcessSpawner.make(() =>        Effect.gen(function* () {          const pid = 500 + spawned.length;          spawned.push(pid);          if (pid === 500) {            yield* Deferred.succeed(firstSpawnEntered, undefined);            yield* Deferred.await(releaseFirstSpawn);          }          const handle = makeHandle({            pid,            onKill: () => {              killed.push(pid);            },          });          yield* Effect.addFinalizer(() => handle.kill().pipe(Effect.ignore));          return handle;        }),      );      const runtime = yield* buildCloudManagedEndpointRuntime(spawner);       const first = yield* runtime        .applyConfig(          Option.some({            providerKind: "cloudflare_tunnel",            connectorToken: "token-1",          }),        )        .pipe(Effect.forkChild);      yield* Deferred.await(firstSpawnEntered);      const second = yield* runtime        .applyConfig(          Option.some({            providerKind: "cloudflare_tunnel",            connectorToken: "token-2",          }),        )        .pipe(Effect.forkChild);      yield* Deferred.succeed(releaseFirstSpawn, undefined);       yield* Fiber.join(first);      const status = yield* Fiber.join(second);       if (status.status !== "running") {        assert.fail(`Expected connector to be running, got ${status.status}`);      }      assert.equal(status.pid, 501);      assert.deepEqual(spawned, [500, 501]);      assert.deepEqual(killed, [500]);    }),  );   it.effect("reports connector spawn failures", () =>    Effect.gen(function* () {      const spawner = ChildProcessSpawner.make(() =>        Effect.fail(          PlatformError.systemError({            _tag: "NotFound",            module: "ChildProcess",            method: "spawn",            description: "cloudflared missing",          }),        ),      );      const runtime = yield* buildCloudManagedEndpointRuntime(spawner);       const status = yield* runtime.applyConfig(        Option.some({          providerKind: "cloudflare_tunnel",          connectorToken: "token",          tunnelId: "tunnel-1",        }),      );       if (status.status !== "failed") {        assert.fail(`Expected connector spawn to fail, got ${status.status}`);      }      assert.equal(status.providerKind, "cloudflare_tunnel");      assert.equal(status.tunnelId, "tunnel-1");    }),  );   it.effect("reports a missing relay client executable without spawning", () =>    Effect.gen(function* () {      const spawn = vi.fn();      const spawner = ChildProcessSpawner.make(spawn);      const runtime = yield* buildCloudManagedEndpointRuntime(        spawner,        Layer.succeed(          RelayClient.RelayClient,          RelayClient.RelayClient.of({            resolve: Effect.succeed({              status: "missing",              version: RelayClient.CLOUDFLARED_VERSION,            }),            install: Effect.die("unused"),            installWithProgress: () => Effect.die("unused"),          }),        ),      );       const status = yield* runtime.applyConfig(        Option.some({          providerKind: "cloudflare_tunnel",          connectorToken: "token",        }),      );       assert.deepEqual(status, {        status: "failed",        providerKind: "cloudflare_tunnel",        reason: "The relay client is not installed.",      });      assert.equal(spawn.mock.calls.length, 0);    }),  );});