| 1 | import { assert, describe, it } from "@effect/vitest"; |
| 2 | import * as Effect from "effect/Effect"; |
| 3 | import * as Layer from "effect/Layer"; |
| 4 | import { HttpClient } from "effect/http"; |
| 5 | import { ChildProcessSpawner } from "effect/process"; |
| 6 | |
| 7 | import { |
| 8 | parseTailscaleMagicDnsName, |
| 9 | resolveTailscaleAdvertisedEndpoints, |
| 10 | } from "./tailscaleEndpointProvider.ts"; |
| 11 | |
| 12 | const layerUnusedTailscaleExternalServices = Layer.mergeAll( |
| 13 | Layer.succeed( |
| 14 | HttpClient.HttpClient, |
| 15 | HttpClient.make(() => Effect.die("unexpected Tailscale HTTPS probe")), |
| 16 | ), |
| 17 | Layer.succeed( |
| 18 | ChildProcessSpawner.ChildProcessSpawner, |
| 19 | ChildProcessSpawner.make(() => Effect.die("unexpected tailscale status process")), |
| 20 | ), |
| 21 | ); |
| 22 | |
| 23 | describe("tailscale endpoint provider", () => { |
| 24 | it.effect("parses MagicDNS names from tailscale status", () => |
| 25 | Effect.gen(function* () { |
| 26 | const dnsName = yield* parseTailscaleMagicDnsName( |
| 27 | `{"Self":{"DNSName":"desktop.tail.ts.net."}}`, |
| 28 | ); |
| 29 | assert.equal(dnsName, "desktop.tail.ts.net"); |
| 30 | assert.equal(yield* parseTailscaleMagicDnsName("{}"), null); |
| 31 | const malformed = yield* Effect.result(parseTailscaleMagicDnsName("not-json")); |
| 32 | assert.isTrue(malformed._tag === "Failure"); |
| 33 | }), |
| 34 | ); |
| 35 | |
| 36 | it.effect("resolves Tailscale endpoints as add-on advertised endpoints", () => |
| 37 | Effect.gen(function* () { |
| 38 | const endpoints = yield* resolveTailscaleAdvertisedEndpoints({ |
| 39 | port: 3773, |
| 40 | networkInterfaces: { |
| 41 | tailscale0: [ |
| 42 | { |
| 43 | address: "100.100.100.100", |
| 44 | family: "IPv4", |
| 45 | internal: false, |
| 46 | netmask: "255.192.0.0", |
| 47 | cidr: "100.100.100.100/10", |
| 48 | mac: "00:00:00:00:00:00", |
| 49 | }, |
| 50 | ], |
| 51 | }, |
| 52 | statusJson: `{"Self":{"DNSName":"desktop.tail.ts.net."}}`, |
| 53 | }); |
| 54 | assert.deepEqual(endpoints, [ |
| 55 | { |
| 56 | id: "tailscale-ip:http://100.100.100.100:3773", |
| 57 | label: "Tailscale IP", |
| 58 | provider: { |
| 59 | id: "tailscale", |
| 60 | label: "Tailscale", |
| 61 | kind: "private-network", |
| 62 | isAddon: true, |
| 63 | }, |
| 64 | httpBaseUrl: "http://100.100.100.100:3773/", |
| 65 | wsBaseUrl: "ws://100.100.100.100:3773/", |
| 66 | reachability: "private-network", |
| 67 | compatibility: { |
| 68 | hostedHttpsApp: "mixed-content-blocked", |
| 69 | desktopApp: "compatible", |
| 70 | }, |
| 71 | source: "desktop-addon", |
| 72 | status: "available", |
| 73 | description: "Reachable from devices on the same Tailnet.", |
| 74 | }, |
| 75 | { |
| 76 | id: "tailscale-magicdns:https://desktop.tail.ts.net/", |
| 77 | label: "Tailscale HTTPS", |
| 78 | provider: { |
| 79 | id: "tailscale", |
| 80 | label: "Tailscale", |
| 81 | kind: "private-network", |
| 82 | isAddon: true, |
| 83 | }, |
| 84 | httpBaseUrl: "https://desktop.tail.ts.net/", |
| 85 | wsBaseUrl: "wss://desktop.tail.ts.net/", |
| 86 | reachability: "private-network", |
| 87 | compatibility: { |
| 88 | hostedHttpsApp: "requires-configuration", |
| 89 | desktopApp: "compatible", |
| 90 | }, |
| 91 | source: "desktop-addon", |
| 92 | status: "unavailable", |
| 93 | description: "MagicDNS hostname. Configure Tailscale Serve for HTTPS access.", |
| 94 | }, |
| 95 | ]); |
| 96 | }).pipe(Effect.provide(layerUnusedTailscaleExternalServices)), |
| 97 | ); |
| 98 | |
| 99 | it.effect("uses an injected magic DNS name reader instead of spawning tailscale", () => |
| 100 | Effect.gen(function* () { |
| 101 | let readerCalls = 0; |
| 102 | const endpoints = yield* resolveTailscaleAdvertisedEndpoints({ |
| 103 | port: 3773, |
| 104 | networkInterfaces: {}, |
| 105 | readMagicDnsName: Effect.sync(() => { |
| 106 | readerCalls += 1; |
| 107 | return "desktop.tail.ts.net"; |
| 108 | }), |
| 109 | }); |
| 110 | assert.equal(readerCalls, 1); |
| 111 | assert.deepEqual( |
| 112 | endpoints.map((endpoint) => endpoint.httpBaseUrl), |
| 113 | ["https://desktop.tail.ts.net/"], |
| 114 | ); |
| 115 | }).pipe(Effect.provide(layerUnusedTailscaleExternalServices)), |
| 116 | ); |
| 117 | |
| 118 | it.effect( |
| 119 | "marks the Tailscale HTTPS endpoint available after Serve is enabled and reachable", |
| 120 | () => |
| 121 | Effect.gen(function* () { |
| 122 | const endpoints = yield* resolveTailscaleAdvertisedEndpoints({ |
| 123 | port: 3773, |
| 124 | networkInterfaces: {}, |
| 125 | statusJson: `{"Self":{"DNSName":"desktop.tail.ts.net."}}`, |
| 126 | serveEnabled: true, |
| 127 | probe: () => Effect.succeed(true), |
| 128 | }); |
| 129 | assert.deepEqual(endpoints, [ |
| 130 | { |
| 131 | id: "tailscale-magicdns:https://desktop.tail.ts.net/", |
| 132 | label: "Tailscale HTTPS", |
| 133 | provider: { |
| 134 | id: "tailscale", |
| 135 | label: "Tailscale", |
| 136 | kind: "private-network", |
| 137 | isAddon: true, |
| 138 | }, |
| 139 | httpBaseUrl: "https://desktop.tail.ts.net/", |
| 140 | wsBaseUrl: "wss://desktop.tail.ts.net/", |
| 141 | reachability: "private-network", |
| 142 | compatibility: { |
| 143 | hostedHttpsApp: "compatible", |
| 144 | desktopApp: "compatible", |
| 145 | }, |
| 146 | source: "desktop-addon", |
| 147 | status: "available", |
| 148 | description: "HTTPS endpoint served by Tailscale Serve.", |
| 149 | }, |
| 150 | ]); |
| 151 | }).pipe(Effect.provide(layerUnusedTailscaleExternalServices)), |
| 152 | ); |
| 153 | }); |