144 lines · 4.9 KB · TypeScript
RawDownload
import { assert, describe, it } from "@effect/vitest";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Sink from "effect/Sink";import * as Stream from "effect/Stream";import { ChildProcessSpawner } from "effect/unstable/process"; import {  buildTailscaleHttpsBaseUrl,  disableTailscaleServe,  ensureTailscaleServe,  isTailscaleIpv4Address,  parseTailscaleMagicDnsName,  parseTailscaleStatus,  readTailscaleStatus,} from "./tailscale.ts"; const encoder = new TextEncoder();const tailscaleStatusJson = `{"Self":{"DNSName":"desktop.tail.ts.net.","TailscaleIPs":["100.100.100.100","fd7a:115c:a1e0::1","192.168.1.20"]}}`;const tailscaleStatusWithSingleIpJson = `{"Self":{"DNSName":"desktop.tail.ts.net.","TailscaleIPs":["100.90.1.2"]}}`; function mockHandle(result: { stdout?: string; stderr?: string; code?: number }) {  return ChildProcessSpawner.makeHandle({    pid: ChildProcessSpawner.ProcessId(1),    exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(result.code ?? 0)),    isRunning: Effect.succeed(false),    kill: () => Effect.void,    unref: Effect.succeed(Effect.void),    stdin: Sink.drain,    stdout: Stream.make(encoder.encode(result.stdout ?? "")),    stderr: Stream.make(encoder.encode(result.stderr ?? "")),    all: Stream.empty,    getInputFd: () => Sink.drain,    getOutputFd: () => Stream.empty,  });} function mockSpawnerLayer(  handler: (    command: string,    args: ReadonlyArray<string>,  ) => { stdout?: string; stderr?: string; code?: number },) {  return Layer.mock(ChildProcessSpawner.ChildProcessSpawner, {    spawn: (command) => {      const childProcess = command as unknown as {        readonly command: string;        readonly args: ReadonlyArray<string>;      };      return Effect.succeed(mockHandle(handler(childProcess.command, childProcess.args)));    },  });} describe("tailscale", () => {  it.effect("detects Tailnet IPv4 addresses", () =>    Effect.sync(() => {      assert.equal(isTailscaleIpv4Address("100.64.0.1"), true);      assert.equal(isTailscaleIpv4Address("100.127.255.254"), true);      assert.equal(isTailscaleIpv4Address("100.128.0.1"), false);      assert.equal(isTailscaleIpv4Address("192.168.1.44"), false);    }),  );   it.effect("parses MagicDNS names from tailscale status", () =>    Effect.gen(function* () {      const dnsName = yield* parseTailscaleMagicDnsName(tailscaleStatusJson);      assert.deepEqual(dnsName, Option.some("desktop.tail.ts.net"));      assert.deepEqual(yield* parseTailscaleMagicDnsName("{}"), Option.none());    }),  );   it.effect("parses status facts", () =>    Effect.gen(function* () {      const status = yield* parseTailscaleStatus(tailscaleStatusJson);      assert.deepEqual(status, {        magicDnsName: Option.some("desktop.tail.ts.net"),        tailnetIpv4Addresses: ["100.100.100.100"],      });    }),  );   it.effect("builds clean HTTPS base URLs", () =>    Effect.sync(() => {      assert.equal(        buildTailscaleHttpsBaseUrl({ magicDnsName: "desktop.tail.ts.net" }),        "https://desktop.tail.ts.net/",      );      assert.equal(        buildTailscaleHttpsBaseUrl({ magicDnsName: "desktop.tail.ts.net", servePort: 8443 }),        "https://desktop.tail.ts.net:8443/",      );    }),  );   it.effect("reads tailscale status through the process spawner service", () => {    const layer = mockSpawnerLayer((command, args) => {      assert.equal(command, "tailscale");      assert.deepEqual(args, ["status", "--json"]);      return {        stdout: tailscaleStatusWithSingleIpJson,      };    });     return Effect.gen(function* () {      const status = yield* readTailscaleStatus.pipe(Effect.provide(layer));      assert.deepEqual(status, {        magicDnsName: Option.some("desktop.tail.ts.net"),        tailnetIpv4Addresses: ["100.90.1.2"],      });    });  });   it.effect("configures tailscale serve through the process spawner service", () => {    const layer = mockSpawnerLayer((command, args) => {      assert.equal(command, "tailscale");      assert.deepEqual(args, ["serve", "--bg", "--https=8443", "http://127.0.0.1:13773"]);      return {};    });     return ensureTailscaleServe({ localPort: 13773, servePort: 8443 }).pipe(Effect.provide(layer));  });   it.effect("disables tailscale serve through the process spawner service", () => {    const commands: {      readonly command: string;      readonly args: ReadonlyArray<string>;    }[] = [];    const layer = mockSpawnerLayer((command, args) => {      commands.push({ command, args });      assert.equal(command, "tailscale");      assert.deepEqual(args, ["serve", "--https=8443", "off"]);      return {};    });     return Effect.gen(function* () {      yield* disableTailscaleServe({ servePort: 8443 }).pipe(Effect.provide(layer));      assert.deepEqual(commands, [        { command: "tailscale", args: ["serve", "--https=8443", "off"] },      ]);    });  });});