mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 19:08:27 +00:00
refactor(test): reuse withGatewayServer in auth/http suites
This commit is contained in:
@@ -2,7 +2,13 @@ import { afterAll, beforeAll, describe, expect, it } from "vitest";
|
|||||||
import { HISTORY_CONTEXT_MARKER } from "../auto-reply/reply/history.js";
|
import { HISTORY_CONTEXT_MARKER } from "../auto-reply/reply/history.js";
|
||||||
import { CURRENT_MESSAGE_MARKER } from "../auto-reply/reply/mentions.js";
|
import { CURRENT_MESSAGE_MARKER } from "../auto-reply/reply/mentions.js";
|
||||||
import { emitAgentEvent } from "../infra/agent-events.js";
|
import { emitAgentEvent } from "../infra/agent-events.js";
|
||||||
import { agentCommand, getFreePort, installGatewayTestHooks, testState } from "./test-helpers.js";
|
import {
|
||||||
|
agentCommand,
|
||||||
|
getFreePort,
|
||||||
|
installGatewayTestHooks,
|
||||||
|
testState,
|
||||||
|
withGatewayServer,
|
||||||
|
} from "./test-helpers.js";
|
||||||
|
|
||||||
installGatewayTestHooks({ scope: "suite" });
|
installGatewayTestHooks({ scope: "suite" });
|
||||||
|
|
||||||
@@ -345,46 +351,46 @@ describe("OpenAI-compatible HTTP API (e2e)", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
it("returns 429 for repeated failed auth when gateway.auth.rateLimit is configured", async () => {
|
it("returns 429 for repeated failed auth when gateway.auth.rateLimit is configured", async () => {
|
||||||
const { startGatewayServer } = await import("./server.js");
|
|
||||||
testState.gatewayAuth = {
|
testState.gatewayAuth = {
|
||||||
mode: "token",
|
mode: "token",
|
||||||
token: "secret",
|
token: "secret",
|
||||||
rateLimit: { maxAttempts: 1, windowMs: 60_000, lockoutMs: 60_000, exemptLoopback: false },
|
rateLimit: { maxAttempts: 1, windowMs: 60_000, lockoutMs: 60_000, exemptLoopback: false },
|
||||||
// oxlint-disable-next-line typescript/no-explicit-any
|
// oxlint-disable-next-line typescript/no-explicit-any
|
||||||
} as any;
|
} as any;
|
||||||
const port = await getFreePort();
|
await withGatewayServer(
|
||||||
const server = await startGatewayServer(port, {
|
async ({ port }) => {
|
||||||
host: "127.0.0.1",
|
const headers = {
|
||||||
controlUiEnabled: false,
|
"content-type": "application/json",
|
||||||
openAiChatCompletionsEnabled: true,
|
authorization: "Bearer wrong",
|
||||||
});
|
};
|
||||||
try {
|
const body = {
|
||||||
const headers = {
|
model: "openclaw",
|
||||||
"content-type": "application/json",
|
messages: [{ role: "user", content: "hi" }],
|
||||||
authorization: "Bearer wrong",
|
};
|
||||||
};
|
|
||||||
const body = {
|
|
||||||
model: "openclaw",
|
|
||||||
messages: [{ role: "user", content: "hi" }],
|
|
||||||
};
|
|
||||||
|
|
||||||
const first = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {
|
const first = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
expect(first.status).toBe(401);
|
expect(first.status).toBe(401);
|
||||||
|
|
||||||
const second = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {
|
const second = await fetch(`http://127.0.0.1:${port}/v1/chat/completions`, {
|
||||||
method: "POST",
|
method: "POST",
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify(body),
|
body: JSON.stringify(body),
|
||||||
});
|
});
|
||||||
expect(second.status).toBe(429);
|
expect(second.status).toBe(429);
|
||||||
expect(second.headers.get("retry-after")).toBeTruthy();
|
expect(second.headers.get("retry-after")).toBeTruthy();
|
||||||
} finally {
|
},
|
||||||
await server.close({ reason: "rate-limit auth test done" });
|
{
|
||||||
}
|
serverOptions: {
|
||||||
|
host: "127.0.0.1",
|
||||||
|
controlUiEnabled: false,
|
||||||
|
openAiChatCompletionsEnabled: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("streams SSE chunks when stream=true", async () => {
|
it("streams SSE chunks when stream=true", async () => {
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import {
|
|||||||
startServerWithClient,
|
startServerWithClient,
|
||||||
testTailscaleWhois,
|
testTailscaleWhois,
|
||||||
testState,
|
testState,
|
||||||
|
withGatewayServer,
|
||||||
} from "./test-helpers.js";
|
} from "./test-helpers.js";
|
||||||
|
|
||||||
installGatewayTestHooks({ scope: "suite" });
|
installGatewayTestHooks({ scope: "suite" });
|
||||||
@@ -596,62 +597,64 @@ describe("gateway server auth/connect", () => {
|
|||||||
} as any);
|
} as any);
|
||||||
const prevToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
const prevToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = "secret";
|
process.env.OPENCLAW_GATEWAY_TOKEN = "secret";
|
||||||
const port = await getFreePort();
|
try {
|
||||||
const server = await startGatewayServer(port);
|
await withGatewayServer(async ({ port }) => {
|
||||||
const ws = new WebSocket(`ws://127.0.0.1:${port}`, {
|
const ws = new WebSocket(`ws://127.0.0.1:${port}`, {
|
||||||
headers: {
|
headers: {
|
||||||
origin: "https://localhost",
|
origin: "https://localhost",
|
||||||
"x-forwarded-for": "203.0.113.10",
|
"x-forwarded-for": "203.0.113.10",
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
const challengePromise = onceMessage<{ payload?: unknown }>(
|
const challengePromise = onceMessage<{ payload?: unknown }>(
|
||||||
ws,
|
ws,
|
||||||
(o) => o.type === "event" && o.event === "connect.challenge",
|
(o) => o.type === "event" && o.event === "connect.challenge",
|
||||||
);
|
);
|
||||||
await new Promise<void>((resolve) => ws.once("open", resolve));
|
await new Promise<void>((resolve) => ws.once("open", resolve));
|
||||||
const challenge = await challengePromise;
|
const challenge = await challengePromise;
|
||||||
const nonce = (challenge.payload as { nonce?: unknown } | undefined)?.nonce;
|
const nonce = (challenge.payload as { nonce?: unknown } | undefined)?.nonce;
|
||||||
expect(typeof nonce).toBe("string");
|
expect(typeof nonce).toBe("string");
|
||||||
const { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, signDevicePayload } =
|
const { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, signDevicePayload } =
|
||||||
await import("../infra/device-identity.js");
|
await import("../infra/device-identity.js");
|
||||||
const identity = loadOrCreateDeviceIdentity();
|
const identity = loadOrCreateDeviceIdentity();
|
||||||
const scopes = ["operator.admin", "operator.approvals", "operator.pairing"];
|
const scopes = ["operator.admin", "operator.approvals", "operator.pairing"];
|
||||||
const signedAtMs = Date.now();
|
const signedAtMs = Date.now();
|
||||||
const payload = buildDeviceAuthPayload({
|
const payload = buildDeviceAuthPayload({
|
||||||
deviceId: identity.deviceId,
|
deviceId: identity.deviceId,
|
||||||
clientId: GATEWAY_CLIENT_NAMES.CONTROL_UI,
|
clientId: GATEWAY_CLIENT_NAMES.CONTROL_UI,
|
||||||
clientMode: GATEWAY_CLIENT_MODES.WEBCHAT,
|
clientMode: GATEWAY_CLIENT_MODES.WEBCHAT,
|
||||||
role: "operator",
|
role: "operator",
|
||||||
scopes,
|
scopes,
|
||||||
signedAtMs,
|
signedAtMs,
|
||||||
token: "secret",
|
token: "secret",
|
||||||
nonce: String(nonce),
|
nonce: String(nonce),
|
||||||
});
|
});
|
||||||
const device = {
|
const device = {
|
||||||
id: identity.deviceId,
|
id: identity.deviceId,
|
||||||
publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem),
|
publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem),
|
||||||
signature: signDevicePayload(identity.privateKeyPem, payload),
|
signature: signDevicePayload(identity.privateKeyPem, payload),
|
||||||
signedAt: signedAtMs,
|
signedAt: signedAtMs,
|
||||||
nonce: String(nonce),
|
nonce: String(nonce),
|
||||||
};
|
};
|
||||||
const res = await connectReq(ws, {
|
const res = await connectReq(ws, {
|
||||||
token: "secret",
|
token: "secret",
|
||||||
scopes,
|
scopes,
|
||||||
device,
|
device,
|
||||||
client: {
|
client: {
|
||||||
id: GATEWAY_CLIENT_NAMES.CONTROL_UI,
|
id: GATEWAY_CLIENT_NAMES.CONTROL_UI,
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
platform: "web",
|
platform: "web",
|
||||||
mode: GATEWAY_CLIENT_MODES.WEBCHAT,
|
mode: GATEWAY_CLIENT_MODES.WEBCHAT,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(res.ok).toBe(true);
|
expect(res.ok).toBe(true);
|
||||||
ws.close();
|
ws.close();
|
||||||
await server.close();
|
});
|
||||||
if (prevToken === undefined) {
|
} finally {
|
||||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
if (prevToken === undefined) {
|
||||||
} else {
|
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = prevToken;
|
} else {
|
||||||
|
process.env.OPENCLAW_GATEWAY_TOKEN = prevToken;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -660,46 +663,48 @@ describe("gateway server auth/connect", () => {
|
|||||||
testState.gatewayAuth = { mode: "token", token: "secret" };
|
testState.gatewayAuth = { mode: "token", token: "secret" };
|
||||||
const prevToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
const prevToken = process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = "secret";
|
process.env.OPENCLAW_GATEWAY_TOKEN = "secret";
|
||||||
const port = await getFreePort();
|
try {
|
||||||
const server = await startGatewayServer(port);
|
await withGatewayServer(async ({ port }) => {
|
||||||
const ws = await openWs(port, { origin: originForPort(port) });
|
const ws = await openWs(port, { origin: originForPort(port) });
|
||||||
const { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, signDevicePayload } =
|
const { loadOrCreateDeviceIdentity, publicKeyRawBase64UrlFromPem, signDevicePayload } =
|
||||||
await import("../infra/device-identity.js");
|
await import("../infra/device-identity.js");
|
||||||
const identity = loadOrCreateDeviceIdentity();
|
const identity = loadOrCreateDeviceIdentity();
|
||||||
const signedAtMs = Date.now() - 60 * 60 * 1000;
|
const signedAtMs = Date.now() - 60 * 60 * 1000;
|
||||||
const payload = buildDeviceAuthPayload({
|
const payload = buildDeviceAuthPayload({
|
||||||
deviceId: identity.deviceId,
|
deviceId: identity.deviceId,
|
||||||
clientId: GATEWAY_CLIENT_NAMES.CONTROL_UI,
|
clientId: GATEWAY_CLIENT_NAMES.CONTROL_UI,
|
||||||
clientMode: GATEWAY_CLIENT_MODES.WEBCHAT,
|
clientMode: GATEWAY_CLIENT_MODES.WEBCHAT,
|
||||||
role: "operator",
|
role: "operator",
|
||||||
scopes: [],
|
scopes: [],
|
||||||
signedAtMs,
|
signedAtMs,
|
||||||
token: "secret",
|
token: "secret",
|
||||||
});
|
});
|
||||||
const device = {
|
const device = {
|
||||||
id: identity.deviceId,
|
id: identity.deviceId,
|
||||||
publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem),
|
publicKey: publicKeyRawBase64UrlFromPem(identity.publicKeyPem),
|
||||||
signature: signDevicePayload(identity.privateKeyPem, payload),
|
signature: signDevicePayload(identity.privateKeyPem, payload),
|
||||||
signedAt: signedAtMs,
|
signedAt: signedAtMs,
|
||||||
};
|
};
|
||||||
const res = await connectReq(ws, {
|
const res = await connectReq(ws, {
|
||||||
token: "secret",
|
token: "secret",
|
||||||
device,
|
device,
|
||||||
client: {
|
client: {
|
||||||
id: GATEWAY_CLIENT_NAMES.CONTROL_UI,
|
id: GATEWAY_CLIENT_NAMES.CONTROL_UI,
|
||||||
version: "1.0.0",
|
version: "1.0.0",
|
||||||
platform: "web",
|
platform: "web",
|
||||||
mode: GATEWAY_CLIENT_MODES.WEBCHAT,
|
mode: GATEWAY_CLIENT_MODES.WEBCHAT,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
expect(res.ok).toBe(true);
|
expect(res.ok).toBe(true);
|
||||||
expect((res.payload as { auth?: unknown } | undefined)?.auth).toBeUndefined();
|
expect((res.payload as { auth?: unknown } | undefined)?.auth).toBeUndefined();
|
||||||
ws.close();
|
ws.close();
|
||||||
await server.close();
|
});
|
||||||
if (prevToken === undefined) {
|
} finally {
|
||||||
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
if (prevToken === undefined) {
|
||||||
} else {
|
delete process.env.OPENCLAW_GATEWAY_TOKEN;
|
||||||
process.env.OPENCLAW_GATEWAY_TOKEN = prevToken;
|
} else {
|
||||||
|
process.env.OPENCLAW_GATEWAY_TOKEN = prevToken;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user