| import { describe, expect, it } from "vitest"; |
| import { encodePairingSetupCode } from "../../pairing/setup-code.js"; |
| import { resolveNodeGatewayOptions, resolveNodePairGatewayOptions } from "./gateway-options.js"; |
|
|
| const TLS_FINGERPRINT = "ab".repeat(32); |
|
|
| describe("node gateway options", () => { |
| it("preserves ordered pairing endpoint candidates and pins only the direct endpoint", () => { |
| const pair = resolveNodePairGatewayOptions( |
| encodePairingSetupCode({ |
| url: "wss://192.168.1.20:8443/openclaw-gw", |
| urls: ["wss://192.168.1.20:8443/openclaw-gw", "wss://gateway.tailnet.example/tailnet-gw"], |
| bootstrapToken: "bootstrap-123", |
| tlsFingerprint: `sha256:${TLS_FINGERPRINT.toUpperCase()}`, |
| }), |
| ); |
|
|
| expect(resolveNodeGatewayOptions({}, null, pair).gatewayCandidates).toEqual([ |
| { |
| host: "192.168.1.20", |
| port: 8443, |
| contextPath: "/openclaw-gw", |
| tls: true, |
| tlsFingerprint: TLS_FINGERPRINT, |
| }, |
| { |
| host: "gateway.tailnet.example", |
| port: 443, |
| contextPath: "/tailnet-gw", |
| tls: true, |
| }, |
| ]); |
| expect(resolveNodeGatewayOptions({}, null, pair).contextPath).toBe("/openclaw-gw"); |
| }); |
|
|
| it("keeps origin-only pairing endpoints pathless", () => { |
| const pair = resolveNodePairGatewayOptions( |
| encodePairingSetupCode({ |
| url: "wss://gateway.example", |
| bootstrapToken: "bootstrap-123", |
| }), |
| ); |
|
|
| expect(resolveNodeGatewayOptions({}, null, pair)).toMatchObject({ |
| contextPath: undefined, |
| gatewayCandidates: [{ host: "gateway.example", port: 443, tls: true }], |
| }); |
| }); |
|
|
| it("collapses pairing candidates when an endpoint flag is explicit", () => { |
| const pair = resolveNodePairGatewayOptions( |
| encodePairingSetupCode({ |
| url: "ws://192.168.1.20:18789", |
| urls: ["ws://192.168.1.20:18789", "wss://gateway.tailnet.example"], |
| bootstrapToken: "bootstrap-123", |
| }), |
| ); |
|
|
| expect(resolveNodeGatewayOptions({ host: "manual.example" }, null, pair)).toMatchObject({ |
| host: "manual.example", |
| gatewayCandidates: undefined, |
| }); |
| }); |
|
|
| it("canonicalizes explicit TLS pins and rejects invalid values", () => { |
| const colonFingerprint = (TLS_FINGERPRINT.match(/.{2}/gu)?.join(":") ?? "").toUpperCase(); |
| expect( |
| resolveNodeGatewayOptions({ tlsFingerprint: `SHA256:${colonFingerprint}` }, null), |
| ).toMatchObject({ tls: true, tlsFingerprint: TLS_FINGERPRINT }); |
| expect(() => resolveNodeGatewayOptions({ tlsFingerprint: "abc123" }, null)).toThrow( |
| "Invalid TLS fingerprint", |
| ); |
| }); |
| }); |
|
|