test: share gateway config onboard harness

This commit is contained in:
Peter Steinberger
2026-04-19 00:22:42 +01:00
parent aaad2468c8
commit 158ebbb2ed

View File

@@ -33,6 +33,29 @@ const SAMPLE_SECRET_REF = {
id: "OPENCLAW_GATEWAY_TOKEN_REF",
};
function createTokenConfig(token: unknown): OpenClawConfig {
return {
gateway: { auth: { mode: "token", token } },
} as unknown as OpenClawConfig;
}
function applyGatewayConfig({
nextConfig = {} as OpenClawConfig,
opts = baseOpts,
runtime = createRuntime(),
}: {
nextConfig?: OpenClawConfig;
opts?: OnboardOptions;
runtime?: ReturnType<typeof createRuntime>;
} = {}) {
return applyNonInteractiveGatewayConfig({
nextConfig,
opts,
runtime: runtime as never,
defaultPort: 18789,
});
}
describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
const originalEnvToken = process.env.OPENCLAW_GATEWAY_TOKEN;
const originalRefValue = process.env[SAMPLE_SECRET_REF.id];
@@ -59,16 +82,9 @@ describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
// --- Plaintext preservation (the original regression) ---
it("preserves existing plaintext gateway.auth.token when no flag or env override is provided", () => {
const nextConfig = {
gateway: { auth: { mode: "token", token: "existing-user-token" } },
} as OpenClawConfig;
const nextConfig = createTokenConfig("existing-user-token");
const result = applyNonInteractiveGatewayConfig({
nextConfig,
opts: baseOpts,
runtime: createRuntime() as never,
defaultPort: 18789,
});
const result = applyGatewayConfig({ nextConfig });
expect(result?.nextConfig.gateway?.auth?.token).toBe("existing-user-token");
expect(randomToken).not.toHaveBeenCalled();
@@ -78,31 +94,20 @@ describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
// A stale shell/launchd OPENCLAW_GATEWAY_TOKEN must not rotate a
// persisted token — that would break already-paired clients.
process.env.OPENCLAW_GATEWAY_TOKEN = "stale-env-token";
const nextConfig = {
gateway: { auth: { mode: "token", token: "existing-user-token" } },
} as OpenClawConfig;
const nextConfig = createTokenConfig("existing-user-token");
const result = applyNonInteractiveGatewayConfig({
nextConfig,
opts: baseOpts,
runtime: createRuntime() as never,
defaultPort: 18789,
});
const result = applyGatewayConfig({ nextConfig });
expect(result?.nextConfig.gateway?.auth?.token).toBe("existing-user-token");
expect(randomToken).not.toHaveBeenCalled();
});
it("prefers --gateway-token flag over existing plaintext token", () => {
const nextConfig = {
gateway: { auth: { mode: "token", token: "existing-user-token" } },
} as OpenClawConfig;
const nextConfig = createTokenConfig("existing-user-token");
const result = applyNonInteractiveGatewayConfig({
const result = applyGatewayConfig({
nextConfig,
opts: { gatewayToken: "flag-token" } as OnboardOptions,
runtime: createRuntime() as never,
defaultPort: 18789,
});
expect(result?.nextConfig.gateway?.auth?.token).toBe("flag-token");
@@ -112,24 +117,14 @@ describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
it("uses OPENCLAW_GATEWAY_TOKEN to fill an empty config on first-run", () => {
process.env.OPENCLAW_GATEWAY_TOKEN = "env-token";
const result = applyNonInteractiveGatewayConfig({
nextConfig: {} as OpenClawConfig,
opts: baseOpts,
runtime: createRuntime() as never,
defaultPort: 18789,
});
const result = applyGatewayConfig();
expect(result?.nextConfig.gateway?.auth?.token).toBe("env-token");
expect(randomToken).not.toHaveBeenCalled();
});
it("generates a random token only when flag, env, and existing config are all empty", () => {
const result = applyNonInteractiveGatewayConfig({
nextConfig: {} as OpenClawConfig,
opts: baseOpts,
runtime: createRuntime() as never,
defaultPort: 18789,
});
const result = applyGatewayConfig();
expect(randomToken).toHaveBeenCalledOnce();
expect(result?.nextConfig.gateway?.auth?.token).toBe("generated-random-token");
@@ -138,16 +133,9 @@ describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
// --- SecretRef preservation ---
it("preserves an existing SecretRef when no flag or env override is provided", () => {
const nextConfig = {
gateway: { auth: { mode: "token", token: SAMPLE_SECRET_REF } },
} as unknown as OpenClawConfig;
const nextConfig = createTokenConfig(SAMPLE_SECRET_REF);
const result = applyNonInteractiveGatewayConfig({
nextConfig,
opts: baseOpts,
runtime: createRuntime() as never,
defaultPort: 18789,
});
const result = applyGatewayConfig({ nextConfig });
expect(result?.nextConfig.gateway?.auth?.token).toEqual(SAMPLE_SECRET_REF);
expect(randomToken).not.toHaveBeenCalled();
@@ -156,16 +144,9 @@ describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
it("preserves an existing SecretRef even when ambient OPENCLAW_GATEWAY_TOKEN is set", () => {
// A stale ambient env must not declassify a configured SecretRef.
process.env.OPENCLAW_GATEWAY_TOKEN = "stale-env-token";
const nextConfig = {
gateway: { auth: { mode: "token", token: SAMPLE_SECRET_REF } },
} as unknown as OpenClawConfig;
const nextConfig = createTokenConfig(SAMPLE_SECRET_REF);
const result = applyNonInteractiveGatewayConfig({
nextConfig,
opts: baseOpts,
runtime: createRuntime() as never,
defaultPort: 18789,
});
const result = applyGatewayConfig({ nextConfig });
expect(result?.nextConfig.gateway?.auth?.token).toEqual(SAMPLE_SECRET_REF);
expect(randomToken).not.toHaveBeenCalled();
@@ -173,31 +154,20 @@ describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
it("leaves env-source SecretRef resolution to the health probe path", () => {
process.env[SAMPLE_SECRET_REF.id] = "resolved-secret-value";
const nextConfig = {
gateway: { auth: { mode: "token", token: SAMPLE_SECRET_REF } },
} as unknown as OpenClawConfig;
const nextConfig = createTokenConfig(SAMPLE_SECRET_REF);
const result = applyNonInteractiveGatewayConfig({
nextConfig,
opts: baseOpts,
runtime: createRuntime() as never,
defaultPort: 18789,
});
const result = applyGatewayConfig({ nextConfig });
expect(result?.nextConfig.gateway?.auth?.token).toEqual(SAMPLE_SECRET_REF);
expect(randomToken).not.toHaveBeenCalled();
});
it("overrides an existing SecretRef when --gateway-token flag is provided", () => {
const nextConfig = {
gateway: { auth: { mode: "token", token: SAMPLE_SECRET_REF } },
} as unknown as OpenClawConfig;
const nextConfig = createTokenConfig(SAMPLE_SECRET_REF);
const result = applyNonInteractiveGatewayConfig({
const result = applyGatewayConfig({
nextConfig,
opts: { gatewayToken: "flag-token" } as OnboardOptions,
runtime: createRuntime() as never,
defaultPort: 18789,
});
expect(result?.nextConfig.gateway?.auth?.token).toBe("flag-token");
@@ -208,15 +178,11 @@ describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
const newRefId = "OPENCLAW_GATEWAY_TOKEN_NEW_REF";
process.env[newRefId] = "resolved-new-ref-value";
try {
const nextConfig = {
gateway: { auth: { mode: "token", token: SAMPLE_SECRET_REF } },
} as unknown as OpenClawConfig;
const nextConfig = createTokenConfig(SAMPLE_SECRET_REF);
const result = applyNonInteractiveGatewayConfig({
const result = applyGatewayConfig({
nextConfig,
opts: { gatewayTokenRefEnv: newRefId } as OnboardOptions,
runtime: createRuntime() as never,
defaultPort: 18789,
});
const newToken = result?.nextConfig.gateway?.auth?.token;
@@ -231,11 +197,9 @@ describe("applyNonInteractiveGatewayConfig token resolution chain", () => {
it("fails when --gateway-token-ref-env points to a missing env var", () => {
const runtime = createRuntime();
const result = applyNonInteractiveGatewayConfig({
nextConfig: {} as OpenClawConfig,
const result = applyGatewayConfig({
opts: { gatewayTokenRefEnv: "MISSING_GATEWAY_TOKEN_ENV" } as OnboardOptions,
runtime: runtime as never,
defaultPort: 18789,
runtime,
});
expect(result).toBeNull();