mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 00:35:51 +00:00
style: 完成所有文件的lint
This commit is contained in:
@@ -1,28 +1,28 @@
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { getLanIPs } from "../cert.js";
|
||||
import { describe, test, expect } from 'bun:test'
|
||||
import { getLanIPs } from '../cert.js'
|
||||
|
||||
describe("getLanIPs", () => {
|
||||
test("returns an array", () => {
|
||||
const ips = getLanIPs();
|
||||
expect(Array.isArray(ips)).toBe(true);
|
||||
});
|
||||
describe('getLanIPs', () => {
|
||||
test('returns an array', () => {
|
||||
const ips = getLanIPs()
|
||||
expect(Array.isArray(ips)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns only IPv4 addresses", () => {
|
||||
const ips = getLanIPs();
|
||||
test('returns only IPv4 addresses', () => {
|
||||
const ips = getLanIPs()
|
||||
for (const ip of ips) {
|
||||
// IPv4 format: x.x.x.x
|
||||
expect(ip).toMatch(/^\d+\.\d+\.\d+\.\d+$/);
|
||||
expect(ip).toMatch(/^\d+\.\d+\.\d+\.\d+$/)
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
test("does not include loopback addresses", () => {
|
||||
const ips = getLanIPs();
|
||||
expect(ips).not.toContain("127.0.0.1");
|
||||
});
|
||||
test('does not include loopback addresses', () => {
|
||||
const ips = getLanIPs()
|
||||
expect(ips).not.toContain('127.0.0.1')
|
||||
})
|
||||
|
||||
test("may be empty in isolated environments", () => {
|
||||
test('may be empty in isolated environments', () => {
|
||||
// This test just ensures it doesn't throw
|
||||
const ips = getLanIPs();
|
||||
expect(ips.length).toBeGreaterThanOrEqual(0);
|
||||
});
|
||||
});
|
||||
const ips = getLanIPs()
|
||||
expect(ips.length).toBeGreaterThanOrEqual(0)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,287 +1,306 @@
|
||||
import { describe, test, expect, mock } from "bun:test";
|
||||
import { describe, test, expect, mock } from 'bun:test'
|
||||
import {
|
||||
__testing,
|
||||
decodeClientWsMessage,
|
||||
MAX_CLIENT_WS_PAYLOAD_BYTES,
|
||||
resolveNewSessionPermissionMode,
|
||||
type ServerConfig,
|
||||
} from "../server.js";
|
||||
} from '../server.js'
|
||||
import {
|
||||
authTokensEqual,
|
||||
decodeWebSocketAuthProtocol,
|
||||
encodeWebSocketAuthProtocol,
|
||||
extractWebSocketAuthToken,
|
||||
} from "../ws-auth.js";
|
||||
import { buildRcsWsUrl } from "../rcs-upstream.js";
|
||||
} from '../ws-auth.js'
|
||||
import { buildRcsWsUrl } from '../rcs-upstream.js'
|
||||
|
||||
function makeTestWs(sent: unknown[]) {
|
||||
type TestWs = Parameters<typeof __testing.dispatchClientMessage>[0];
|
||||
type TestWs = Parameters<typeof __testing.dispatchClientMessage>[0]
|
||||
|
||||
return {
|
||||
readyState: 1,
|
||||
send: mock((message: string) => {
|
||||
sent.push(JSON.parse(message));
|
||||
sent.push(JSON.parse(message))
|
||||
}),
|
||||
close: mock(() => {}),
|
||||
raw: null,
|
||||
isInner: false,
|
||||
url: "",
|
||||
origin: "",
|
||||
protocol: "",
|
||||
} as unknown as TestWs;
|
||||
url: '',
|
||||
origin: '',
|
||||
protocol: '',
|
||||
} as unknown as TestWs
|
||||
}
|
||||
|
||||
describe("Server HTTP endpoints", () => {
|
||||
test("package.json has correct bin and main entries", async () => {
|
||||
const pkg = await import("../../package.json", { with: { type: "json" } });
|
||||
expect(pkg.default.name).toBe("acp-link");
|
||||
expect(pkg.default.main).toBe("./dist/server.js");
|
||||
expect(pkg.default.bin).toBeDefined();
|
||||
expect(pkg.default.bin["acp-link"]).toBe("dist/cli/bin.js");
|
||||
});
|
||||
describe('Server HTTP endpoints', () => {
|
||||
test('package.json has correct bin and main entries', async () => {
|
||||
const pkg = await import('../../package.json', { with: { type: 'json' } })
|
||||
expect(pkg.default.name).toBe('acp-link')
|
||||
expect(pkg.default.main).toBe('./dist/server.js')
|
||||
expect(pkg.default.bin).toBeDefined()
|
||||
expect(pkg.default.bin['acp-link']).toBe('dist/cli/bin.js')
|
||||
})
|
||||
|
||||
test("ServerConfig interface accepts all expected fields", () => {
|
||||
test('ServerConfig interface accepts all expected fields', () => {
|
||||
const config: ServerConfig = {
|
||||
port: 9315,
|
||||
host: "localhost",
|
||||
command: "echo",
|
||||
host: 'localhost',
|
||||
command: 'echo',
|
||||
args: [],
|
||||
cwd: "/tmp",
|
||||
cwd: '/tmp',
|
||||
debug: false,
|
||||
token: "test-token",
|
||||
token: 'test-token',
|
||||
https: false,
|
||||
};
|
||||
expect(config.port).toBe(9315);
|
||||
expect(config.token).toBe("test-token");
|
||||
});
|
||||
}
|
||||
expect(config.port).toBe(9315)
|
||||
expect(config.token).toBe('test-token')
|
||||
})
|
||||
|
||||
test("ServerConfig allows optional fields to be omitted", () => {
|
||||
test('ServerConfig allows optional fields to be omitted', () => {
|
||||
const config: ServerConfig = {
|
||||
port: 9315,
|
||||
host: "localhost",
|
||||
command: "echo",
|
||||
host: 'localhost',
|
||||
command: 'echo',
|
||||
args: [],
|
||||
cwd: "/tmp",
|
||||
};
|
||||
expect(config.debug).toBeUndefined();
|
||||
expect(config.token).toBeUndefined();
|
||||
expect(config.https).toBeUndefined();
|
||||
});
|
||||
});
|
||||
cwd: '/tmp',
|
||||
}
|
||||
expect(config.debug).toBeUndefined()
|
||||
expect(config.token).toBeUndefined()
|
||||
expect(config.https).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
describe("WebSocket message types", () => {
|
||||
describe('WebSocket message types', () => {
|
||||
const clientMessageTypes = [
|
||||
"connect",
|
||||
"disconnect",
|
||||
"new_session",
|
||||
"prompt",
|
||||
"permission_response",
|
||||
"cancel",
|
||||
"set_session_model",
|
||||
"list_sessions",
|
||||
"load_session",
|
||||
"resume_session",
|
||||
"ping",
|
||||
];
|
||||
'connect',
|
||||
'disconnect',
|
||||
'new_session',
|
||||
'prompt',
|
||||
'permission_response',
|
||||
'cancel',
|
||||
'set_session_model',
|
||||
'list_sessions',
|
||||
'load_session',
|
||||
'resume_session',
|
||||
'ping',
|
||||
]
|
||||
|
||||
test("all client message types are recognized", () => {
|
||||
expect(clientMessageTypes.length).toBe(11);
|
||||
expect(clientMessageTypes).toContain("ping");
|
||||
expect(clientMessageTypes).toContain("connect");
|
||||
expect(clientMessageTypes).toContain("cancel");
|
||||
});
|
||||
test('all client message types are recognized', () => {
|
||||
expect(clientMessageTypes.length).toBe(11)
|
||||
expect(clientMessageTypes).toContain('ping')
|
||||
expect(clientMessageTypes).toContain('connect')
|
||||
expect(clientMessageTypes).toContain('cancel')
|
||||
})
|
||||
|
||||
test("decodes supported client message payloads", () => {
|
||||
expect(decodeClientWsMessage('{"type":"ping"}')).toEqual({ type: "ping" });
|
||||
test('decodes supported client message payloads', () => {
|
||||
expect(decodeClientWsMessage('{"type":"ping"}')).toEqual({ type: 'ping' })
|
||||
expect(
|
||||
decodeClientWsMessage(Buffer.from('{"type":"prompt","payload":{"content":[]}}')),
|
||||
).toEqual({ type: "prompt", payload: { content: [] } });
|
||||
decodeClientWsMessage(
|
||||
Buffer.from('{"type":"prompt","payload":{"content":[]}}'),
|
||||
),
|
||||
).toEqual({ type: 'prompt', payload: { content: [] } })
|
||||
expect(
|
||||
decodeClientWsMessage(new TextEncoder().encode('{"type":"cancel"}').buffer),
|
||||
).toEqual({ type: "cancel" });
|
||||
decodeClientWsMessage(
|
||||
new TextEncoder().encode('{"type":"cancel"}').buffer,
|
||||
),
|
||||
).toEqual({ type: 'cancel' })
|
||||
expect(
|
||||
decodeClientWsMessage([
|
||||
Buffer.from('{"type":"list_sessions","payload":{"cursor":"'),
|
||||
Buffer.from('next"}}'),
|
||||
]),
|
||||
).toEqual({ type: "list_sessions", payload: { cwd: undefined, cursor: "next" } });
|
||||
});
|
||||
).toEqual({
|
||||
type: 'list_sessions',
|
||||
payload: { cwd: undefined, cursor: 'next' },
|
||||
})
|
||||
})
|
||||
|
||||
test("rejects malformed typed client payloads", () => {
|
||||
test('rejects malformed typed client payloads', () => {
|
||||
expect(() => decodeClientWsMessage('{"type":"prompt"}')).toThrow(
|
||||
"Invalid prompt payload",
|
||||
);
|
||||
'Invalid prompt payload',
|
||||
)
|
||||
expect(() =>
|
||||
decodeClientWsMessage('{"type":"load_session","payload":{}}'),
|
||||
).toThrow("Invalid load_session payload");
|
||||
).toThrow('Invalid load_session payload')
|
||||
expect(() => decodeClientWsMessage('{"type":"unknown"}')).toThrow(
|
||||
"Unknown message type",
|
||||
);
|
||||
'Unknown message type',
|
||||
)
|
||||
expect(() =>
|
||||
decodeClientWsMessage(
|
||||
'{"type":"new_session","payload":{"permissionMode":123}}',
|
||||
),
|
||||
).toThrow("Invalid new_session.permissionMode");
|
||||
).toThrow('Invalid new_session.permissionMode')
|
||||
expect(() =>
|
||||
decodeClientWsMessage(
|
||||
'{"type":"new_session","payload":{"permissionMode":{}}}',
|
||||
),
|
||||
).toThrow("Invalid new_session.permissionMode");
|
||||
).toThrow('Invalid new_session.permissionMode')
|
||||
expect(() =>
|
||||
decodeClientWsMessage(
|
||||
'{"type":"new_session","payload":{"permissionMode":null}}',
|
||||
),
|
||||
).toThrow("Invalid new_session.permissionMode");
|
||||
});
|
||||
).toThrow('Invalid new_session.permissionMode')
|
||||
})
|
||||
|
||||
test("rejects oversized client message payloads before decoding", () => {
|
||||
const payload = "x".repeat(MAX_CLIENT_WS_PAYLOAD_BYTES + 1);
|
||||
expect(() => decodeClientWsMessage(payload)).toThrow("WebSocket message too large");
|
||||
});
|
||||
});
|
||||
test('rejects oversized client message payloads before decoding', () => {
|
||||
const payload = 'x'.repeat(MAX_CLIENT_WS_PAYLOAD_BYTES + 1)
|
||||
expect(() => decodeClientWsMessage(payload)).toThrow(
|
||||
'WebSocket message too large',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("WebSocket auth protocol", () => {
|
||||
test("round-trips tokens through a WebSocket subprotocol token", () => {
|
||||
const protocol = encodeWebSocketAuthProtocol("secret/token+with=symbols");
|
||||
expect(protocol).toStartWith("rcs.auth.");
|
||||
expect(protocol).not.toContain("secret/token");
|
||||
expect(decodeWebSocketAuthProtocol(protocol)).toBe("secret/token+with=symbols");
|
||||
});
|
||||
describe('WebSocket auth protocol', () => {
|
||||
test('round-trips tokens through a WebSocket subprotocol token', () => {
|
||||
const protocol = encodeWebSocketAuthProtocol('secret/token+with=symbols')
|
||||
expect(protocol).toStartWith('rcs.auth.')
|
||||
expect(protocol).not.toContain('secret/token')
|
||||
expect(decodeWebSocketAuthProtocol(protocol)).toBe(
|
||||
'secret/token+with=symbols',
|
||||
)
|
||||
})
|
||||
|
||||
test("ignores query-token style inputs", () => {
|
||||
expect(decodeWebSocketAuthProtocol(undefined)).toBeUndefined();
|
||||
expect(decodeWebSocketAuthProtocol("token=secret")).toBeUndefined();
|
||||
expect(decodeWebSocketAuthProtocol("other, rcs.auth.")).toBeUndefined();
|
||||
});
|
||||
test('ignores query-token style inputs', () => {
|
||||
expect(decodeWebSocketAuthProtocol(undefined)).toBeUndefined()
|
||||
expect(decodeWebSocketAuthProtocol('token=secret')).toBeUndefined()
|
||||
expect(decodeWebSocketAuthProtocol('other, rcs.auth.')).toBeUndefined()
|
||||
})
|
||||
|
||||
test("prefers Authorization headers and supports protocol auth", () => {
|
||||
test('prefers Authorization headers and supports protocol auth', () => {
|
||||
expect(
|
||||
extractWebSocketAuthToken({
|
||||
authorization: "Bearer header-token",
|
||||
protocol: encodeWebSocketAuthProtocol("protocol-token"),
|
||||
authorization: 'Bearer header-token',
|
||||
protocol: encodeWebSocketAuthProtocol('protocol-token'),
|
||||
}),
|
||||
).toBe("header-token");
|
||||
).toBe('header-token')
|
||||
expect(
|
||||
extractWebSocketAuthToken({
|
||||
protocol: encodeWebSocketAuthProtocol("protocol-token"),
|
||||
protocol: encodeWebSocketAuthProtocol('protocol-token'),
|
||||
}),
|
||||
).toBe("protocol-token");
|
||||
});
|
||||
).toBe('protocol-token')
|
||||
})
|
||||
|
||||
test("compares auth tokens through the shared constant-time path", () => {
|
||||
expect(authTokensEqual("secret-token", "secret-token")).toBe(true);
|
||||
expect(authTokensEqual("secret-token", "wrong-token")).toBe(false);
|
||||
expect(authTokensEqual(undefined, "secret-token")).toBe(false);
|
||||
});
|
||||
});
|
||||
test('compares auth tokens through the shared constant-time path', () => {
|
||||
expect(authTokensEqual('secret-token', 'secret-token')).toBe(true)
|
||||
expect(authTokensEqual('secret-token', 'wrong-token')).toBe(false)
|
||||
expect(authTokensEqual(undefined, 'secret-token')).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("RCS upstream URL normalization", () => {
|
||||
test("removes legacy token query params from WebSocket URLs", () => {
|
||||
describe('RCS upstream URL normalization', () => {
|
||||
test('removes legacy token query params from WebSocket URLs', () => {
|
||||
expect(
|
||||
buildRcsWsUrl("http://example.test/acp/ws?token=old-secret&x=1"),
|
||||
).toBe("ws://example.test/acp/ws?x=1");
|
||||
});
|
||||
buildRcsWsUrl('http://example.test/acp/ws?token=old-secret&x=1'),
|
||||
).toBe('ws://example.test/acp/ws?x=1')
|
||||
})
|
||||
|
||||
test("adds /acp/ws for base URLs", () => {
|
||||
expect(buildRcsWsUrl("https://example.test/")).toBe(
|
||||
"wss://example.test/acp/ws",
|
||||
);
|
||||
});
|
||||
});
|
||||
test('adds /acp/ws for base URLs', () => {
|
||||
expect(buildRcsWsUrl('https://example.test/')).toBe(
|
||||
'wss://example.test/acp/ws',
|
||||
)
|
||||
})
|
||||
})
|
||||
|
||||
describe("permission mode resolution", () => {
|
||||
test("uses client requested non-bypass modes", () => {
|
||||
expect(resolveNewSessionPermissionMode("plan", "acceptEdits")).toBe("plan");
|
||||
});
|
||||
describe('permission mode resolution', () => {
|
||||
test('uses client requested non-bypass modes', () => {
|
||||
expect(resolveNewSessionPermissionMode('plan', 'acceptEdits')).toBe('plan')
|
||||
})
|
||||
|
||||
test("uses local default when client does not request a mode", () => {
|
||||
expect(resolveNewSessionPermissionMode(undefined, "acceptEdits")).toBe("acceptEdits");
|
||||
});
|
||||
test('uses local default when client does not request a mode', () => {
|
||||
expect(resolveNewSessionPermissionMode(undefined, 'acceptEdits')).toBe(
|
||||
'acceptEdits',
|
||||
)
|
||||
})
|
||||
|
||||
test("rejects client requested bypassPermissions without local default", () => {
|
||||
test('rejects client requested bypassPermissions without local default', () => {
|
||||
expect(() =>
|
||||
resolveNewSessionPermissionMode("bypassPermissions", "acceptEdits"),
|
||||
).toThrow("bypassPermissions requires local ACP_PERMISSION_MODE");
|
||||
resolveNewSessionPermissionMode('bypassPermissions', 'acceptEdits'),
|
||||
).toThrow('bypassPermissions requires local ACP_PERMISSION_MODE')
|
||||
expect(() =>
|
||||
resolveNewSessionPermissionMode("bypass", "acceptEdits"),
|
||||
).toThrow("bypassPermissions requires local ACP_PERMISSION_MODE");
|
||||
resolveNewSessionPermissionMode('bypass', 'acceptEdits'),
|
||||
).toThrow('bypassPermissions requires local ACP_PERMISSION_MODE')
|
||||
expect(() =>
|
||||
resolveNewSessionPermissionMode("bypasspermissions", "acceptEdits"),
|
||||
).toThrow("bypassPermissions requires local ACP_PERMISSION_MODE");
|
||||
resolveNewSessionPermissionMode('bypasspermissions', 'acceptEdits'),
|
||||
).toThrow('bypassPermissions requires local ACP_PERMISSION_MODE')
|
||||
expect(() =>
|
||||
resolveNewSessionPermissionMode("bypassPermissions", undefined),
|
||||
).toThrow("bypassPermissions requires local ACP_PERMISSION_MODE");
|
||||
});
|
||||
resolveNewSessionPermissionMode('bypassPermissions', undefined),
|
||||
).toThrow('bypassPermissions requires local ACP_PERMISSION_MODE')
|
||||
})
|
||||
|
||||
test("rejects unknown client permission modes before forwarding", () => {
|
||||
test('rejects unknown client permission modes before forwarding', () => {
|
||||
expect(() =>
|
||||
resolveNewSessionPermissionMode("unknown-mode", "acceptEdits"),
|
||||
).toThrow("Invalid permissionMode: unknown-mode");
|
||||
});
|
||||
resolveNewSessionPermissionMode('unknown-mode', 'acceptEdits'),
|
||||
).toThrow('Invalid permissionMode: unknown-mode')
|
||||
})
|
||||
|
||||
test("allows bypassPermissions when local default already enables it", () => {
|
||||
expect(resolveNewSessionPermissionMode("bypassPermissions", "bypassPermissions")).toBe("bypassPermissions");
|
||||
expect(resolveNewSessionPermissionMode("bypass", "bypassPermissions")).toBe("bypassPermissions");
|
||||
expect(resolveNewSessionPermissionMode("bypassPermissions", "bypass")).toBe("bypassPermissions");
|
||||
});
|
||||
test('allows bypassPermissions when local default already enables it', () => {
|
||||
expect(
|
||||
resolveNewSessionPermissionMode('bypassPermissions', 'bypassPermissions'),
|
||||
).toBe('bypassPermissions')
|
||||
expect(resolveNewSessionPermissionMode('bypass', 'bypassPermissions')).toBe(
|
||||
'bypassPermissions',
|
||||
)
|
||||
expect(resolveNewSessionPermissionMode('bypassPermissions', 'bypass')).toBe(
|
||||
'bypassPermissions',
|
||||
)
|
||||
})
|
||||
|
||||
test("new_session rejects client bypass before forwarding to the agent", async () => {
|
||||
const sent: unknown[] = [];
|
||||
const ws = makeTestWs(sent);
|
||||
const originalTestInternals = process.env.ACP_LINK_TEST_INTERNALS;
|
||||
process.env.ACP_LINK_TEST_INTERNALS = "1";
|
||||
let unregisterClient = () => {};
|
||||
let restoreMode = () => {};
|
||||
test('new_session rejects client bypass before forwarding to the agent', async () => {
|
||||
const sent: unknown[] = []
|
||||
const ws = makeTestWs(sent)
|
||||
const originalTestInternals = process.env.ACP_LINK_TEST_INTERNALS
|
||||
process.env.ACP_LINK_TEST_INTERNALS = '1'
|
||||
let unregisterClient = () => {}
|
||||
let restoreMode = () => {}
|
||||
|
||||
try {
|
||||
const newSession = mock(async () => ({
|
||||
sessionId: "should-not-be-created",
|
||||
}));
|
||||
sessionId: 'should-not-be-created',
|
||||
}))
|
||||
unregisterClient = __testing.registerClient(ws, {
|
||||
connection: { newSession },
|
||||
});
|
||||
restoreMode = __testing.setDefaultPermissionMode("acceptEdits");
|
||||
})
|
||||
restoreMode = __testing.setDefaultPermissionMode('acceptEdits')
|
||||
|
||||
await __testing.dispatchClientMessage(ws, {
|
||||
type: "new_session",
|
||||
type: 'new_session',
|
||||
payload: {
|
||||
cwd: "/tmp",
|
||||
permissionMode: "bypass",
|
||||
cwd: '/tmp',
|
||||
permissionMode: 'bypass',
|
||||
},
|
||||
});
|
||||
})
|
||||
|
||||
expect(newSession).not.toHaveBeenCalled();
|
||||
expect(__testing.getClientSessionId(ws)).toBeNull();
|
||||
expect(newSession).not.toHaveBeenCalled()
|
||||
expect(__testing.getClientSessionId(ws)).toBeNull()
|
||||
expect(sent).toEqual([
|
||||
{
|
||||
type: "error",
|
||||
type: 'error',
|
||||
payload: {
|
||||
message: expect.stringContaining(
|
||||
"bypassPermissions requires local ACP_PERMISSION_MODE",
|
||||
'bypassPermissions requires local ACP_PERMISSION_MODE',
|
||||
),
|
||||
},
|
||||
},
|
||||
]);
|
||||
])
|
||||
} finally {
|
||||
restoreMode();
|
||||
unregisterClient();
|
||||
restoreMode()
|
||||
unregisterClient()
|
||||
if (originalTestInternals === undefined) {
|
||||
delete process.env.ACP_LINK_TEST_INTERNALS;
|
||||
delete process.env.ACP_LINK_TEST_INTERNALS
|
||||
} else {
|
||||
process.env.ACP_LINK_TEST_INTERNALS = originalTestInternals;
|
||||
process.env.ACP_LINK_TEST_INTERNALS = originalTestInternals
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
})
|
||||
})
|
||||
|
||||
describe("Heartbeat constants", () => {
|
||||
test("PERMISSION_TIMEOUT_MS is 5 minutes", () => {
|
||||
const PERMISSION_TIMEOUT_MS = 5 * 60 * 1000;
|
||||
expect(PERMISSION_TIMEOUT_MS).toBe(300_000);
|
||||
});
|
||||
describe('Heartbeat constants', () => {
|
||||
test('PERMISSION_TIMEOUT_MS is 5 minutes', () => {
|
||||
const PERMISSION_TIMEOUT_MS = 5 * 60 * 1000
|
||||
expect(PERMISSION_TIMEOUT_MS).toBe(300_000)
|
||||
})
|
||||
|
||||
test("HEARTBEAT_INTERVAL_MS is 30 seconds", () => {
|
||||
const HEARTBEAT_INTERVAL_MS = 30_000;
|
||||
expect(HEARTBEAT_INTERVAL_MS).toBe(30_000);
|
||||
});
|
||||
});
|
||||
test('HEARTBEAT_INTERVAL_MS is 30 seconds', () => {
|
||||
const HEARTBEAT_INTERVAL_MS = 30_000
|
||||
expect(HEARTBEAT_INTERVAL_MS).toBe(30_000)
|
||||
})
|
||||
})
|
||||
|
||||
@@ -1,69 +1,86 @@
|
||||
import { describe, test, expect } from "bun:test";
|
||||
import { isRequest, isResponse, isNotification } from "../types.js";
|
||||
import type { JsonRpcRequest, JsonRpcResponse, JsonRpcNotification } from "../types.js";
|
||||
import { describe, test, expect } from 'bun:test'
|
||||
import { isRequest, isResponse, isNotification } from '../types.js'
|
||||
import type {
|
||||
JsonRpcRequest,
|
||||
JsonRpcResponse,
|
||||
JsonRpcNotification,
|
||||
} from '../types.js'
|
||||
|
||||
describe("isRequest", () => {
|
||||
test("returns true for a valid JSON-RPC request", () => {
|
||||
const msg: JsonRpcRequest = { jsonrpc: "2.0", id: 1, method: "test" };
|
||||
expect(isRequest(msg)).toBe(true);
|
||||
});
|
||||
describe('isRequest', () => {
|
||||
test('returns true for a valid JSON-RPC request', () => {
|
||||
const msg: JsonRpcRequest = { jsonrpc: '2.0', id: 1, method: 'test' }
|
||||
expect(isRequest(msg)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns true for request with params", () => {
|
||||
const msg = { jsonrpc: "2.0" as const, id: "abc", method: "test", params: { x: 1 } };
|
||||
expect(isRequest(msg)).toBe(true);
|
||||
});
|
||||
test('returns true for request with params', () => {
|
||||
const msg = {
|
||||
jsonrpc: '2.0' as const,
|
||||
id: 'abc',
|
||||
method: 'test',
|
||||
params: { x: 1 },
|
||||
}
|
||||
expect(isRequest(msg)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for response (no method)", () => {
|
||||
const msg: JsonRpcResponse = { jsonrpc: "2.0", id: 1, result: {} };
|
||||
expect(isRequest(msg)).toBe(false);
|
||||
});
|
||||
test('returns false for response (no method)', () => {
|
||||
const msg: JsonRpcResponse = { jsonrpc: '2.0', id: 1, result: {} }
|
||||
expect(isRequest(msg)).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false for notification (no id)", () => {
|
||||
const msg: JsonRpcNotification = { jsonrpc: "2.0", method: "notify" };
|
||||
expect(isRequest(msg)).toBe(false);
|
||||
});
|
||||
});
|
||||
test('returns false for notification (no id)', () => {
|
||||
const msg: JsonRpcNotification = { jsonrpc: '2.0', method: 'notify' }
|
||||
expect(isRequest(msg)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("isResponse", () => {
|
||||
test("returns true for a valid JSON-RPC response with result", () => {
|
||||
const msg: JsonRpcResponse = { jsonrpc: "2.0", id: 1, result: "ok" };
|
||||
expect(isResponse(msg)).toBe(true);
|
||||
});
|
||||
describe('isResponse', () => {
|
||||
test('returns true for a valid JSON-RPC response with result', () => {
|
||||
const msg: JsonRpcResponse = { jsonrpc: '2.0', id: 1, result: 'ok' }
|
||||
expect(isResponse(msg)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns true for a valid JSON-RPC error response", () => {
|
||||
const msg: JsonRpcResponse = { jsonrpc: "2.0", id: 2, error: { code: -32600, message: "bad" } };
|
||||
expect(isResponse(msg)).toBe(true);
|
||||
});
|
||||
test('returns true for a valid JSON-RPC error response', () => {
|
||||
const msg: JsonRpcResponse = {
|
||||
jsonrpc: '2.0',
|
||||
id: 2,
|
||||
error: { code: -32600, message: 'bad' },
|
||||
}
|
||||
expect(isResponse(msg)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for request (has method)", () => {
|
||||
const msg: JsonRpcRequest = { jsonrpc: "2.0", id: 1, method: "test" };
|
||||
expect(isResponse(msg)).toBe(false);
|
||||
});
|
||||
test('returns false for request (has method)', () => {
|
||||
const msg: JsonRpcRequest = { jsonrpc: '2.0', id: 1, method: 'test' }
|
||||
expect(isResponse(msg)).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false for notification", () => {
|
||||
const msg: JsonRpcNotification = { jsonrpc: "2.0", method: "notify" };
|
||||
expect(isResponse(msg)).toBe(false);
|
||||
});
|
||||
});
|
||||
test('returns false for notification', () => {
|
||||
const msg: JsonRpcNotification = { jsonrpc: '2.0', method: 'notify' }
|
||||
expect(isResponse(msg)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
describe("isNotification", () => {
|
||||
test("returns true for a valid JSON-RPC notification", () => {
|
||||
const msg: JsonRpcNotification = { jsonrpc: "2.0", method: "update" };
|
||||
expect(isNotification(msg)).toBe(true);
|
||||
});
|
||||
describe('isNotification', () => {
|
||||
test('returns true for a valid JSON-RPC notification', () => {
|
||||
const msg: JsonRpcNotification = { jsonrpc: '2.0', method: 'update' }
|
||||
expect(isNotification(msg)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns true for notification with params", () => {
|
||||
const msg = { jsonrpc: "2.0" as const, method: "progress", params: { pct: 50 } };
|
||||
expect(isNotification(msg)).toBe(true);
|
||||
});
|
||||
test('returns true for notification with params', () => {
|
||||
const msg = {
|
||||
jsonrpc: '2.0' as const,
|
||||
method: 'progress',
|
||||
params: { pct: 50 },
|
||||
}
|
||||
expect(isNotification(msg)).toBe(true)
|
||||
})
|
||||
|
||||
test("returns false for request (has id)", () => {
|
||||
const msg: JsonRpcRequest = { jsonrpc: "2.0", id: 1, method: "test" };
|
||||
expect(isNotification(msg)).toBe(false);
|
||||
});
|
||||
test('returns false for request (has id)', () => {
|
||||
const msg: JsonRpcRequest = { jsonrpc: '2.0', id: 1, method: 'test' }
|
||||
expect(isNotification(msg)).toBe(false)
|
||||
})
|
||||
|
||||
test("returns false for response (no method)", () => {
|
||||
const msg: JsonRpcResponse = { jsonrpc: "2.0", id: 1, result: null };
|
||||
expect(isNotification(msg)).toBe(false);
|
||||
});
|
||||
});
|
||||
test('returns false for response (no method)', () => {
|
||||
const msg: JsonRpcResponse = { jsonrpc: '2.0', id: 1, result: null }
|
||||
expect(isNotification(msg)).toBe(false)
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user