Skip to main content
Glama
workouts.test.ts5.65 kB
import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js"; import { describe, expect, it, vi } from "vitest"; import type { Workout } from "../generated/client/types/index.js"; import { formatWorkout } from "../utils/formatters.js"; import type { HevyClient } from "../utils/hevyClient.js"; import { registerWorkoutTools } from "./workouts.js"; function createMockServer() { const tool = vi.fn(); const server = { tool } as unknown as McpServer; return { server, tool }; } function getToolRegistration(toolSpy: ReturnType<typeof vi.fn>, name: string) { const match = toolSpy.mock.calls.find(([toolName]) => toolName === name); if (!match) { throw new Error(`Tool ${name} was not registered`); } const [, , , handler] = match as [ string, string, Record<string, unknown>, (args: Record<string, unknown>) => Promise<{ content: Array<{ type: string; text: string }>; isError?: boolean; }>, ]; return { handler }; } describe("registerWorkoutTools", () => { it("returns error responses when Hevy client is not initialized", async () => { const { server, tool } = createMockServer(); registerWorkoutTools(server, null); const toolNames = [ "get-workouts", "get-workout", "get-workout-count", "get-workout-events", "create-workout", "update-workout", ]; for (const name of toolNames) { const { handler } = getToolRegistration(tool, name); const response = await handler({}); expect(response).toMatchObject({ isError: true, content: [ { type: "text", text: expect.stringContaining( "API client not initialized. Please provide HEVY_API_KEY.", ), }, ], }); } }); it("get-workouts returns formatted workouts from the client", async () => { const { server, tool } = createMockServer(); const workout: Workout = { id: "w1", title: "Morning Workout", description: "Great session", start_time: 1711522800000, end_time: 1711526400000, created_at: "2025-03-27T07:00:00Z", updated_at: "2025-03-27T07:10:00Z", exercises: [], }; const hevyClient = { getWorkouts: vi.fn().mockResolvedValue({ workouts: [workout] }), } as unknown as HevyClient; registerWorkoutTools(server, hevyClient); const { handler } = getToolRegistration(tool, "get-workouts"); const response = await handler({ page: 1, pageSize: 5 }); expect(hevyClient.getWorkouts).toHaveBeenCalledWith({ page: 1, pageSize: 5, }); const parsed = JSON.parse(response.content[0].text) as unknown[]; expect(parsed).toEqual([formatWorkout(workout)]); }); it("get-workout returns an empty response when workout is not found", async () => { const { server, tool } = createMockServer(); const hevyClient = { getWorkout: vi.fn().mockResolvedValue(null), } as unknown as HevyClient; registerWorkoutTools(server, hevyClient); const { handler } = getToolRegistration(tool, "get-workout"); const response = await handler({ workoutId: "missing-id" }); expect(hevyClient.getWorkout).toHaveBeenCalledWith("missing-id"); expect(response.content[0]?.text).toBe( "Workout with ID missing-id not found", ); }); it("get-workout-count returns the numeric count from the client", async () => { const { server, tool } = createMockServer(); const hevyClient = { getWorkoutCount: vi.fn().mockResolvedValue({ workoutCount: 42 }), } as unknown as HevyClient; registerWorkoutTools(server, hevyClient); const { handler } = getToolRegistration(tool, "get-workout-count"); const response = await handler({}); expect(hevyClient.getWorkoutCount).toHaveBeenCalledTimes(1); const parsed = JSON.parse(response.content[0].text) as unknown; expect(parsed).toEqual({ count: 42 }); }); it("create-workout maps arguments to the request body and formats the response", async () => { const { server, tool } = createMockServer(); const createResult: Workout = { id: "created-id", title: "New Workout", description: "New workout description", start_time: 1711522800000, end_time: 1711526400000, created_at: "2025-03-27T07:00:00Z", updated_at: "2025-03-27T07:00:00Z", exercises: [], }; const hevyClient = { createWorkout: vi.fn().mockResolvedValue(createResult), } as unknown as HevyClient; registerWorkoutTools(server, hevyClient); const { handler } = getToolRegistration(tool, "create-workout"); const args = { title: "New Workout", description: null, startTime: "2025-03-27T07:00:00Z", endTime: "2025-03-27T08:00:00Z", isPrivate: false, exercises: [ { exerciseTemplateId: "template-id", supersetId: 1, notes: "Some notes", sets: [ { type: "normal" as const, weight: 80, reps: 8, distance: null, duration: null, rpe: 7, customMetric: null, }, ], }, ], }; const response = await handler(args as Record<string, unknown>); expect(hevyClient.createWorkout).toHaveBeenCalledWith({ workout: { title: "New Workout", description: null, start_time: "2025-03-27T07:00:00Z", end_time: "2025-03-27T08:00:00Z", is_private: false, exercises: [ { exercise_template_id: "template-id", superset_id: 1, notes: "Some notes", sets: [ { type: "normal", weight_kg: 80, reps: 8, distance_meters: null, duration_seconds: null, rpe: 7, custom_metric: null, }, ], }, ], }, }); const parsed = JSON.parse(response.content[0].text) as unknown; expect(parsed).toEqual(formatWorkout(createResult)); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/chrisdoc/hevy-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server