import { describe, expect, test } from "bun:test"; import type { BUN_SQLITE_WGUI_CLIENT_ROUTES, BUN_SQLITE_WGUI_TLS_CERTIFICATES, } from "@/db/types/db"; import buildCaddyfile from "./build-caddyfile"; const base = { https_port: 8443, http_port: 8081, admin_socket: `/run/wgui-caddy/admin.sock`, storage_dir: `/var/lib/wgui/caddy/data`, }; const certificate = { hostname: "media.example.com", cert_path: "/var/lib/wgui/tls/media.example.com/fullchain.pem", key_path: "/var/lib/wgui/tls/media.example.com/privkey.pem", status: "active", } as BUN_SQLITE_WGUI_TLS_CERTIFICATES; function route( values: Partial, ): BUN_SQLITE_WGUI_CLIENT_ROUTES { return { hostname: "media.example.com", path_prefix: "/", upstream_url: "http://10.5.0.20:8080", enabled: 1, ...values, }; } describe("buildCaddyfile", () => { test("uses a high port and never 80/443", () => { const res = buildCaddyfile({ ...base, routes: [], certificates: [] }); expect(res.config).toContain("https_port 8443"); expect(res.config).toContain("http_port 8081"); expect(res.config).not.toContain("auto_https disable_redirects"); expect(res.config).not.toContain("https_port 443"); expect(res.config).not.toContain("http_port 80\n"); }); test("can disable automatic HTTPS redirects", () => { const res = buildCaddyfile({ ...base, redirect: false, routes: [], certificates: [], }); expect(res.config).toContain("auto_https disable_redirects"); }); test("orders longer prefixes before the catch-all", () => { const res = buildCaddyfile({ ...base, certificates: [certificate], routes: [ route({ name: "root", path_prefix: "/" }), route({ name: "app", path_prefix: "/app", strip_prefix: 1, upstream_url: "http://10.5.0.30:9000", }), ], }); const app_index = res.config?.indexOf("@route_1 {") || 0; const root_index = res.config?.indexOf("handle {") || 0; expect(app_index).toBeGreaterThan(0); expect(root_index).toBeGreaterThan(app_index); expect(res.config).toContain("path /app /app/*"); expect(res.config).toContain("uri strip_prefix /app"); }); test("skips routes without an active certificate", () => { const res = buildCaddyfile({ ...base, certificates: [], routes: [route({ name: "media" })], }); expect(res.config).not.toContain("media.example.com {"); expect(res.warnings[0]).toContain("certificate"); }); test("scopes a route to its host's WireGuard subnet", () => { const res = buildCaddyfile({ ...base, certificates: [certificate], hosts: [{ id: 0, wg_ip_address: "10.5.0.1" }], routes: [route({ host_id: 0, path_prefix: "/app" })], }); expect(res.config).toContain("remote_ip 10.5.0.0/24"); }); test("appends route allowed_ips to the remote_ip matcher", () => { const res = buildCaddyfile({ ...base, certificates: [certificate], hosts: [{ id: 0, wg_ip_address: "10.5.0.1" }], routes: [ route({ host_id: 0, path_prefix: "/app", allowed_ips: "203.0.113.0/24, 198.51.100.0/24", }), ], }); expect(res.config).toContain( "remote_ip 10.5.0.0/24 127.0.0.1/32 ::1/128 203.0.113.0/24 198.51.100.0/24", ); }); test("maps insecure upstream TLS to Caddy", () => { const res = buildCaddyfile({ ...base, certificates: [certificate], routes: [ route({ upstream_url: "https://10.5.0.20:8443", upstream_tls_verify: 0, }), ], }); expect(res.config).toContain("tls_insecure_skip_verify"); }); test("allows a catch-all route with allowed_ips to have a matcher", () => { const res = buildCaddyfile({ ...base, certificates: [certificate], hosts: [{ id: 0, wg_ip_address: "10.5.0.1" }], routes: [ route({ host_id: 0, path_prefix: "/", allowed_ips: "203.0.113.0/24", }), ], }); expect(res.config).toContain("@route_1 remote_ip"); expect(res.config).toContain("203.0.113.0/24"); }); });