test: refine gateway auth helper coverage

This commit is contained in:
Peter Steinberger 2026-03-13 17:58:28 +00:00
parent 91f1894372
commit e25fa446e8
2 changed files with 104 additions and 64 deletions

View File

@ -1,29 +1,69 @@
import { describe, expect, it } from "vitest";
import { buildDeviceAuthPayloadV3, normalizeDeviceMetadataForAuth } from "./device-auth.js";
import {
buildDeviceAuthPayload,
buildDeviceAuthPayloadV3,
normalizeDeviceMetadataForAuth,
} from "./device-auth.js";
describe("device-auth payload vectors", () => {
it("builds canonical v3 payload", () => {
const payload = buildDeviceAuthPayloadV3({
deviceId: "dev-1",
clientId: "openclaw-macos",
clientMode: "ui",
role: "operator",
scopes: ["operator.admin", "operator.read"],
signedAtMs: 1_700_000_000_000,
token: "tok-123",
nonce: "nonce-abc",
platform: " IOS ",
deviceFamily: " iPhone ",
});
expect(payload).toBe(
"v3|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1700000000000|tok-123|nonce-abc|ios|iphone",
);
it.each([
{
name: "builds canonical v2 payloads",
build: () =>
buildDeviceAuthPayload({
deviceId: "dev-1",
clientId: "openclaw-macos",
clientMode: "ui",
role: "operator",
scopes: ["operator.admin", "operator.read"],
signedAtMs: 1_700_000_000_000,
token: null,
nonce: "nonce-abc",
}),
expected:
"v2|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1700000000000||nonce-abc",
},
{
name: "builds canonical v3 payloads",
build: () =>
buildDeviceAuthPayloadV3({
deviceId: "dev-1",
clientId: "openclaw-macos",
clientMode: "ui",
role: "operator",
scopes: ["operator.admin", "operator.read"],
signedAtMs: 1_700_000_000_000,
token: "tok-123",
nonce: "nonce-abc",
platform: " IOS ",
deviceFamily: " iPhone ",
}),
expected:
"v3|dev-1|openclaw-macos|ui|operator|operator.admin,operator.read|1700000000000|tok-123|nonce-abc|ios|iphone",
},
{
name: "keeps empty metadata slots in v3 payloads",
build: () =>
buildDeviceAuthPayloadV3({
deviceId: "dev-2",
clientId: "openclaw-ios",
clientMode: "ui",
role: "operator",
scopes: ["operator.read"],
signedAtMs: 1_700_000_000_001,
nonce: "nonce-def",
}),
expected: "v3|dev-2|openclaw-ios|ui|operator|operator.read|1700000000001||nonce-def||",
},
])("$name", ({ build, expected }) => {
expect(build()).toBe(expected);
});
it("normalizes metadata with ASCII-only lowercase", () => {
expect(normalizeDeviceMetadataForAuth(" İOS ")).toBe("İos");
expect(normalizeDeviceMetadataForAuth(" MAC ")).toBe("mac");
expect(normalizeDeviceMetadataForAuth(undefined)).toBe("");
it.each([
{ input: " İOS ", expected: "İos" },
{ input: " MAC ", expected: "mac" },
{ input: undefined, expected: "" },
])("normalizes metadata %j", ({ input, expected }) => {
expect(normalizeDeviceMetadataForAuth(input)).toBe(expected);
});
});

View File

@ -6,8 +6,9 @@ import {
} from "./probe-auth.js";
describe("resolveGatewayProbeAuthSafe", () => {
it("returns probe auth credentials when available", () => {
const result = resolveGatewayProbeAuthSafe({
it.each([
{
name: "returns probe auth credentials when available",
cfg: {
gateway: {
auth: {
@ -15,20 +16,17 @@ describe("resolveGatewayProbeAuthSafe", () => {
},
},
} as OpenClawConfig,
mode: "local",
mode: "local" as const,
env: {} as NodeJS.ProcessEnv,
});
expect(result).toEqual({
auth: {
token: "token-value",
password: undefined,
expected: {
auth: {
token: "token-value",
password: undefined,
},
},
});
});
it("returns warning and empty auth when token SecretRef is unresolved", () => {
const result = resolveGatewayProbeAuthSafe({
},
{
name: "returns warning and empty auth when a local token SecretRef is unresolved",
cfg: {
gateway: {
auth: {
@ -42,17 +40,15 @@ describe("resolveGatewayProbeAuthSafe", () => {
},
},
} as OpenClawConfig,
mode: "local",
mode: "local" as const,
env: {} as NodeJS.ProcessEnv,
});
expect(result.auth).toEqual({});
expect(result.warning).toContain("gateway.auth.token");
expect(result.warning).toContain("unresolved");
});
it("does not fall through to remote token when local token SecretRef is unresolved", () => {
const result = resolveGatewayProbeAuthSafe({
expected: {
auth: {},
warningIncludes: ["gateway.auth.token", "unresolved"],
},
},
{
name: "does not fall through to remote token when the local SecretRef is unresolved",
cfg: {
gateway: {
mode: "local",
@ -70,17 +66,15 @@ describe("resolveGatewayProbeAuthSafe", () => {
},
},
} as OpenClawConfig,
mode: "local",
mode: "local" as const,
env: {} as NodeJS.ProcessEnv,
});
expect(result.auth).toEqual({});
expect(result.warning).toContain("gateway.auth.token");
expect(result.warning).toContain("unresolved");
});
it("ignores unresolved local token SecretRef in remote mode when remote-only auth is requested", () => {
const result = resolveGatewayProbeAuthSafe({
expected: {
auth: {},
warningIncludes: ["gateway.auth.token", "unresolved"],
},
},
{
name: "ignores unresolved local token SecretRefs in remote mode",
cfg: {
gateway: {
mode: "remote",
@ -98,16 +92,22 @@ describe("resolveGatewayProbeAuthSafe", () => {
},
},
} as OpenClawConfig,
mode: "remote",
mode: "remote" as const,
env: {} as NodeJS.ProcessEnv,
});
expect(result).toEqual({
auth: {
token: undefined,
password: undefined,
expected: {
auth: {
token: undefined,
password: undefined,
},
},
});
},
])("$name", ({ cfg, mode, env, expected }) => {
const result = resolveGatewayProbeAuthSafe({ cfg, mode, env });
expect(result.auth).toEqual(expected.auth);
for (const fragment of expected.warningIncludes ?? []) {
expect(result.warning).toContain(fragment);
}
});
});