/**
* @jest-environment node
*/
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { registerHelloWorldTools } from "../../src/tools/hello-world";
describe("Hello World Tools", () => {
let mockServer: jest.Mocked<McpServer>;
let registeredTool: any;
beforeEach(() => {
// Создаем мок сервера
mockServer = {
tool: jest.fn().mockImplementation((name, description, schema, handler) => {
registeredTool = { name, description, schema, handler };
return mockServer;
}),
} as any;
// Регистрируем инструменты
registerHelloWorldTools(mockServer);
});
afterEach(() => {
jest.clearAllMocks();
});
describe("Tool Registration", () => {
it("should register hello_world tool with correct name", () => {
expect(mockServer.tool).toHaveBeenCalledWith(
"hello_world",
expect.any(String),
expect.any(Object),
expect.any(Function)
);
});
it("should register tool with correct description", () => {
expect(registeredTool.description).toBe("Say hello to someone or the world");
});
it("should register tool with correct schema", () => {
expect(registeredTool.schema).toEqual({
name: {
type: "string",
description: "Name to greet (optional)",
},
});
});
});
describe("Hello World Handler", () => {
it("should greet with provided name", async () => {
const result = await registeredTool.handler({ name: "Alice" });
expect(result).toEqual({
content: [
{
type: "text",
text: "Hello, Alice! This is the Radius MCP Server.",
},
],
});
});
it("should greet with default 'World' when no name provided", async () => {
const result = await registeredTool.handler({});
expect(result).toEqual({
content: [
{
type: "text",
text: "Hello, World! This is the Radius MCP Server.",
},
],
});
});
it("should greet with default 'World' when name is undefined", async () => {
const result = await registeredTool.handler({ name: undefined });
expect(result).toEqual({
content: [
{
type: "text",
text: "Hello, World! This is the Radius MCP Server.",
},
],
});
});
it("should reject null as name value", async () => {
await expect(registeredTool.handler({ name: null })).rejects.toThrow();
});
it("should handle empty string name", async () => {
const result = await registeredTool.handler({ name: "" });
expect(result).toEqual({
content: [
{
type: "text",
text: "Hello, World! This is the Radius MCP Server.",
},
],
});
});
it("should handle special characters in name", async () => {
const result = await registeredTool.handler({ name: "Алиса & Bob" });
expect(result).toEqual({
content: [
{
type: "text",
text: "Hello, Алиса & Bob! This is the Radius MCP Server.",
},
],
});
});
it("should handle very long name", async () => {
const longName = "A".repeat(1000);
const result = await registeredTool.handler({ name: longName });
expect(result).toEqual({
content: [
{
type: "text",
text: `Hello, ${longName}! This is the Radius MCP Server.`,
},
],
});
});
});
describe("Input Validation", () => {
it("should validate input with Zod schema", async () => {
// Тест с валидными данными
await expect(registeredTool.handler({ name: "Test" })).resolves.toBeDefined();
await expect(registeredTool.handler({})).resolves.toBeDefined();
await expect(registeredTool.handler({ name: undefined })).resolves.toBeDefined();
});
it("should reject invalid input types", async () => {
// Тест с невалидными типами данных
await expect(registeredTool.handler({ name: 123 })).rejects.toThrow();
await expect(registeredTool.handler({ name: true })).rejects.toThrow();
await expect(registeredTool.handler({ name: [] })).rejects.toThrow();
await expect(registeredTool.handler({ name: {} })).rejects.toThrow();
});
it("should reject extra properties", async () => {
await expect(
registeredTool.handler({
name: "Test",
extra: "property",
})
).rejects.toThrow();
});
it("should handle missing required structure", async () => {
await expect(registeredTool.handler(null)).rejects.toThrow();
await expect(registeredTool.handler(undefined)).rejects.toThrow();
});
});
describe("Response Format", () => {
it("should return response in correct MCP format", async () => {
const result = await registeredTool.handler({ name: "Test" });
expect(result).toHaveProperty("content");
expect(Array.isArray(result.content)).toBe(true);
expect(result.content).toHaveLength(1);
expect(result.content[0]).toHaveProperty("type", "text");
expect(result.content[0]).toHaveProperty("text");
});
it("should return text content as string", async () => {
const result = await registeredTool.handler({ name: "Test" });
expect(typeof result.content[0].text).toBe("string");
expect(result.content[0].text).toContain("Hello, Test!");
expect(result.content[0].text).toContain("Radius MCP Server");
});
});
describe("Performance", () => {
it("should handle multiple concurrent requests", async () => {
const promises = Array.from({ length: 100 }, (_, i) => registeredTool.handler({ name: `User${i}` }));
const results = await Promise.all(promises);
expect(results).toHaveLength(100);
results.forEach((result, index) => {
expect(result.content[0].text).toContain(`User${index}`);
});
});
it("should respond quickly", async () => {
const startTime = Date.now();
await registeredTool.handler({ name: "Performance Test" });
const endTime = Date.now();
expect(endTime - startTime).toBeLessThan(100); // Должно выполняться менее чем за 100мс
});
});
describe("Edge Cases", () => {
it("should handle unicode characters", async () => {
const result = await registeredTool.handler({ name: "🚀 Тест" });
expect(result.content[0].text).toBe("Hello, 🚀 Тест! This is the Radius MCP Server.");
});
it("should handle HTML-like strings", async () => {
const result = await registeredTool.handler({ name: "<script>alert('test')</script>" });
expect(result.content[0].text).toBe("Hello, <script>alert('test')</script>! This is the Radius MCP Server.");
});
it("should handle SQL injection-like strings", async () => {
const result = await registeredTool.handler({ name: "'; DROP TABLE users; --" });
expect(result.content[0].text).toBe("Hello, '; DROP TABLE users; --! This is the Radius MCP Server.");
});
});
});