import test from "node:test"; import assert from "node:assert/strict"; import { existsSync, readFileSync } from "node:fs"; import path from "node:path"; import { fileURLToPath } from "node:url"; const testDirectory = path.dirname(fileURLToPath(import.meta.url)); const repoRoot = path.resolve(testDirectory, "../../.."); function readRequiredSource(...pathSegments) { const sourcePath = path.resolve(repoRoot, ...pathSegments); assert.equal(existsSync(sourcePath), true, `Expected config fixture to exist: ${sourcePath}`); return readFileSync(sourcePath, "utf8"); } const baseComposeSource = readRequiredSource("docker-compose.yml"); const exampleComposeSource = readRequiredSource("docker-compose.example.yml"); const standaloneProdComposeSource = readRequiredSource("docker-compose.prod.standalone.yml"); const traefikSource = [ readRequiredSource("services", "traefik", "traefik.yml"), readRequiredSource("services", "traefik", "traefik.prod.yml"), ].join("\n"); function readComposeServiceBlock(composeSource, serviceName) { const escapedServiceName = serviceName.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"); const servicePattern = new RegExp( `^[ ]{2}${escapedServiceName}:\\r?\\n([\\s\\S]*?)(?=^[ ]{2}[A-Za-z0-9_-]+:|^volumes:|^networks:|(?![\\s\\S]))`, "m" ); const match = composeSource.match(servicePattern); assert.ok(match, `Expected docker compose service block for ${serviceName}`); return match[0]; } test("traefik does not expose a dedicated public edge broker port", () => { assert.doesNotMatch(traefikSource, /edge-broker:\s*\n\s*address:\s*":4300"/); }); test("base docker compose routes edge broker traffic through traefik", () => { const serviceBlock = readComposeServiceBlock(baseComposeSource, "edge-broker"); assert.doesNotMatch(serviceBlock, /\n\s+ports:\s*\n[\s\S]*?\n\s+- "4300:4300"/); assert.match(serviceBlock, /EDGE_AUTH_MODE:\s*\$\{EDGE_AUTH_MODE:-manager\}/); assert.match(serviceBlock, /EDGE_MANAGER_URL:\s*\$\{EDGE_MANAGER_URL:-http:\/\/caddy\}/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.priority=200/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.priority=200/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.rule=Host\(`api\.truckwash\.dk`\) && PathPrefix\(`\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api-io\.rule=Host\(`api\.truckwash\.io`\) && PathPrefix\(`\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api-v2\.rule=Host\(`api-v2\.truckwash\.io`\) && PathPrefix\(`\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.rule=Host\(`localhost`\) && PathPrefix\(`\/api\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.middlewares\.edge-broker-strip\.stripPrefix\.prefixes=\/edge-broker/); assert.match(serviceBlock, /traefik\.http\.middlewares\.edge-broker-strip-local\.stripPrefix\.prefixes=\/api\/edge-broker/); assert.match(serviceBlock, /traefik\.http\.services\.edge-broker\.loadbalancer\.server\.port=4300/); }); test("example docker compose routes edge broker traffic through traefik", () => { const serviceBlock = readComposeServiceBlock(exampleComposeSource, "edge-broker"); assert.doesNotMatch(serviceBlock, /\n\s+ports:\s*\n[\s\S]*?\n\s+- "4300:4300"/); assert.match(serviceBlock, /EDGE_AUTH_MODE:\s*\$\{EDGE_AUTH_MODE:-manager\}/); assert.match(serviceBlock, /EDGE_MANAGER_URL:\s*\$\{EDGE_MANAGER_URL:-http:\/\/caddy\}/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.rule=Host\(`api\.example\.com`\) && PathPrefix\(`\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.rule=Host\(`localhost`\) && PathPrefix\(`\/api\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.services\.edge-broker\.loadbalancer\.server\.port=4300/); }); test("standalone production compose routes edge broker traffic through traefik", () => { const serviceBlock = readComposeServiceBlock(standaloneProdComposeSource, "edge-broker"); assert.doesNotMatch(serviceBlock, /\n\s+ports:\s*\n[\s\S]*?\n\s+- "4300:4300"/); assert.match(serviceBlock, /EDGE_AUTH_MODE:\s*\$\{EDGE_AUTH_MODE:-manager\}/); assert.match(serviceBlock, /EDGE_MANAGER_URL:\s*\$\{EDGE_MANAGER_URL:-http:\/\/caddy\}/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.priority=200/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.priority=200/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api\.rule=Host\(`api\.truckwash\.dk`\) && PathPrefix\(`\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api-io\.rule=Host\(`api\.truckwash\.io`\) && PathPrefix\(`\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-api-v2\.rule=Host\(`api-v2\.truckwash\.io`\) && PathPrefix\(`\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.routers\.edge-broker-local\.rule=Host\(`localhost`\) && PathPrefix\(`\/api\/edge-broker`\)/); assert.match(serviceBlock, /traefik\.http\.services\.edge-broker\.loadbalancer\.server\.port=4300/); }); test("php services receive broker websocket environment defaults", () => { for (const composeSource of [baseComposeSource, exampleComposeSource]) { assert.match(composeSource, /EDGE_BROKER_URL:\s*\$\{EDGE_BROKER_URL:-http:\/\/edge-broker:4300\}/); assert.match(composeSource, /EDGE_BROKER_SHARED_SECRET:\s*\$\{EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev\}/); } }); test("base docker compose wires the broker into each php worker", () => { for (const serviceName of ["php1", "php2", "php3", "php4", "php5", "php-staging", "php-cron"]) { const serviceBlock = readComposeServiceBlock(baseComposeSource, serviceName); assert.match(serviceBlock, /\n\s+depends_on:\s*\n[\s\S]*?\n\s+- edge-broker/); assert.match(serviceBlock, /EDGE_BROKER_URL:\s*\$\{EDGE_BROKER_URL:-http:\/\/edge-broker:4300\}/); assert.match(serviceBlock, /EDGE_BROKER_SHARED_SECRET:\s*\$\{EDGE_BROKER_SHARED_SECRET:-truckwash-edge-dev\}/); } });