test(cli): add tests for trusted-proxy auth configuration

- Add tests for buildGatewayAuthConfig with trusted-proxy mode
- Test all trusted-proxy options (userHeader, requiredHeaders, allowUsers)
- Test minimal trusted-proxy config (userHeader only)
- Test preserving allowTailscale when switching to trusted-proxy
- Test error when trustedProxy config missing
- Test dropping token/password when switching to trusted-proxy
- Add integration tests for interactive gateway prompting flow
- Test trusted-proxy with all options and with minimal options

Test coverage:
- 5 new tests in configure.gateway-auth.test.ts
- 2 new tests in configure.gateway.test.ts

All tests verify proper handling of the new trusted-proxy auth mode.
This commit is contained in:
Nick Taylor
2026-02-13 14:48:58 +00:00
committed by Peter Steinberger
parent e1ce11c4b7
commit 1fc97ebe23
2 changed files with 158 additions and 0 deletions

View File

@@ -117,4 +117,94 @@ describe("buildGatewayAuthConfig", () => {
expect(typeof result?.token).toBe("string");
expect(result?.token?.length).toBeGreaterThan(0);
});
it("builds trusted-proxy config with all options", () => {
const result = buildGatewayAuthConfig({
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"],
allowUsers: ["nick@example.com", "admin@company.com"],
},
});
expect(result).toEqual({
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"],
allowUsers: ["nick@example.com", "admin@company.com"],
},
});
});
it("builds trusted-proxy config with only userHeader", () => {
const result = buildGatewayAuthConfig({
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-remote-user",
},
});
expect(result).toEqual({
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-remote-user",
},
});
});
it("preserves allowTailscale when switching to trusted-proxy", () => {
const result = buildGatewayAuthConfig({
existing: {
mode: "token",
token: "abc",
allowTailscale: true,
},
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
},
});
expect(result).toEqual({
mode: "trusted-proxy",
allowTailscale: true,
trustedProxy: {
userHeader: "x-forwarded-user",
},
});
});
it("throws error when trusted-proxy mode lacks trustedProxy config", () => {
expect(() => {
buildGatewayAuthConfig({
mode: "trusted-proxy",
// missing trustedProxy
});
}).toThrow("trustedProxy config is required when mode is trusted-proxy");
});
it("drops token and password when switching to trusted-proxy", () => {
const result = buildGatewayAuthConfig({
existing: {
mode: "token",
token: "abc",
password: "secret",
},
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
},
});
expect(result).toEqual({
mode: "trusted-proxy",
trustedProxy: {
userHeader: "x-forwarded-user",
},
});
expect(result).not.toHaveProperty("token");
expect(result).not.toHaveProperty("password");
});
});

View File

@@ -97,4 +97,72 @@ describe("promptGatewayConfig", () => {
expect(call?.password).not.toBe("undefined");
expect(call?.password).toBe("");
});
it("prompts for trusted-proxy configuration when trusted-proxy mode selected", async () => {
vi.clearAllMocks();
mocks.resolveGatewayPort.mockReturnValue(18789);
// Flow: loopback bind → trusted-proxy auth → tailscale off
const selectQueue = ["loopback", "trusted-proxy", "off"];
mocks.select.mockImplementation(async () => selectQueue.shift());
// Port prompt, userHeader, requiredHeaders, allowUsers, trustedProxies
const textQueue = [
"18789",
"x-forwarded-user",
"x-forwarded-proto,x-forwarded-host",
"nick@example.com",
"10.0.1.10,192.168.1.5",
];
mocks.text.mockImplementation(async () => textQueue.shift());
mocks.buildGatewayAuthConfig.mockImplementation(({ mode, trustedProxy }) => ({
mode,
trustedProxy,
}));
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
};
const result = await promptGatewayConfig({}, runtime);
const call = mocks.buildGatewayAuthConfig.mock.calls[0]?.[0];
expect(call?.mode).toBe("trusted-proxy");
expect(call?.trustedProxy).toEqual({
userHeader: "x-forwarded-user",
requiredHeaders: ["x-forwarded-proto", "x-forwarded-host"],
allowUsers: ["nick@example.com"],
});
expect(result.config.gateway?.trustedProxies).toEqual(["10.0.1.10", "192.168.1.5"]);
});
it("handles trusted-proxy with no optional fields", async () => {
vi.clearAllMocks();
mocks.resolveGatewayPort.mockReturnValue(18789);
const selectQueue = ["loopback", "trusted-proxy", "off"];
mocks.select.mockImplementation(async () => selectQueue.shift());
// Port prompt, userHeader (only required), empty requiredHeaders, empty allowUsers, trustedProxies
const textQueue = ["18789", "x-remote-user", "", "", "10.0.0.1"];
mocks.text.mockImplementation(async () => textQueue.shift());
mocks.buildGatewayAuthConfig.mockImplementation(({ mode, trustedProxy }) => ({
mode,
trustedProxy,
}));
const runtime: RuntimeEnv = {
log: vi.fn(),
error: vi.fn(),
exit: vi.fn(),
};
const result = await promptGatewayConfig({}, runtime);
const call = mocks.buildGatewayAuthConfig.mock.calls[0]?.[0];
expect(call?.mode).toBe("trusted-proxy");
expect(call?.trustedProxy).toEqual({
userHeader: "x-remote-user",
// requiredHeaders and allowUsers should be undefined when empty
});
expect(result.config.gateway?.trustedProxies).toEqual(["10.0.0.1"]);
});
});