Skip to main content
Glama

Google Calendar and Meet MCP Server

by INSIDE-HAIR
integration.test.ts•13.6 kB
/** * Integration tests using Vitest * Tests end-to-end workflows and API integration */ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest"; import fs from "fs/promises"; import type { MockReadFile, MockWriteFile } from "../src/types/index.js"; // Mock dependencies for integration testing vi.mock("fs/promises"); vi.mock("googleapis"); describe("Integration Tests", () => { let GoogleMeetAPI; let api; let mockAuth; let mockCalendar; let mockFetch; beforeEach(async () => { // Clear all mocks vi.clearAllMocks(); // Mock OAuth2 client mockAuth = { setCredentials: vi.fn(), getAccessToken: vi.fn().mockResolvedValue({ token: "integration-token" }), get: vi.fn().mockReturnValue("integration-token"), }; // Mock Calendar API mockCalendar = { calendars: { list: vi.fn().mockResolvedValue({ data: { items: [ { id: "primary", summary: "Primary Calendar", primary: true }, ], }, }), }, events: { insert: vi.fn().mockResolvedValue({ data: { id: "integration-event-id", summary: "Integration Test Event", start: { dateTime: "2025-08-01T10:00:00Z", timeZone: "UTC" }, end: { dateTime: "2025-08-01T11:00:00Z", timeZone: "UTC" }, conferenceData: { entryPoints: [ { entryPointType: "video", uri: "https://meet.google.com/integration-test", }, ], conferenceSolution: { key: { type: "hangoutsMeet" }, }, }, }, }), get: vi.fn().mockResolvedValue({ data: { id: "integration-event-id", summary: "Integration Test Event", start: { dateTime: "2025-08-01T10:00:00Z", timeZone: "UTC" }, end: { dateTime: "2025-08-01T11:00:00Z", timeZone: "UTC" }, }, }), patch: vi.fn().mockResolvedValue({ data: { id: "integration-event-id", summary: "Updated Integration Test Event", start: { dateTime: "2025-08-01T10:00:00Z", timeZone: "UTC" }, end: { dateTime: "2025-08-01T11:00:00Z", timeZone: "UTC" }, }, }), delete: vi.fn().mockResolvedValue({ data: {} }), }, }; // Mock googleapis const { google } = await import("googleapis"); (google.auth as any).OAuth2 = vi.fn(() => mockAuth); (google as any).calendar = vi.fn(() => mockCalendar); // Mock global fetch for Meet API calls mockFetch = vi.fn(); global.fetch = mockFetch; // Mock file system responses (fs.readFile as MockReadFile).mockImplementation(async (path: any) => { if (typeof path === 'string' && path.includes("credentials")) { return JSON.stringify(global.testUtils.getMockCredentials()); } if (typeof path === 'string' && path.includes("token")) { return JSON.stringify(global.testUtils.getMockToken()); } throw new Error("File not found"); }); (fs.writeFile as MockWriteFile).mockResolvedValue(undefined); // Import GoogleMeetAPI after mocks are set up const module = await import("../src/GoogleMeetAPI.js"); GoogleMeetAPI = module.default; api = new GoogleMeetAPI( "integration-credentials.json", "integration-token.json" ); }); afterEach(() => { vi.restoreAllMocks(); delete global.fetch; }); describe("Full Calendar Event Lifecycle", () => { it("should complete a full calendar event workflow", async () => { await api.initialize(); // Step 1: List calendars const calendars = await api.listCalendars(); expect(calendars).toHaveLength(1); expect(calendars[0].primary).toBe(true); // Step 2: Create event with Meet conference const eventData = { summary: "Integration Test Event", description: "Created during integration testing", startTime: "2025-08-01T10:00:00Z", endTime: "2025-08-01T11:00:00Z", attendees: ["test@integration.com"], createMeetConference: true, }; const createdEvent = await api.createCalendarEvent(eventData); expect(createdEvent.id).toBe("integration-event-id"); // Note: conferenceData might not be in formatted event, just check that event creation worked expect(createdEvent.summary).toBe("Integration Test Event"); // Step 3: Get the created event const retrievedEvent = await api.getCalendarEvent("integration-event-id"); expect(retrievedEvent.id).toBe("integration-event-id"); // Step 4: Update the event const updatedEvent = await api.updateCalendarEvent( "integration-event-id", { summary: "Updated Integration Test Event", } ); expect(updatedEvent.summary).toBe("Updated Integration Test Event"); // Step 5: Delete the event await api.deleteCalendarEvent("integration-event-id"); expect(mockCalendar.events.delete).toHaveBeenCalledWith({ calendarId: "primary", eventId: "integration-event-id", }); }); }); describe("Meet API Workflow", () => { it("should handle Meet API operations", async () => { // Mock Meet API responses const mockSpace = { name: "spaces/integration-space-id", meetingUri: "https://meet.google.com/integration-meeting", meetingCode: "int-test-meet", config: { accessType: "TRUSTED", entryPointAccess: "ALL", }, }; mockFetch .mockResolvedValueOnce({ ok: true, headers: { get: vi.fn().mockReturnValue("application/json"), }, json: async () => mockSpace, }) .mockResolvedValueOnce({ ok: true, headers: { get: vi.fn().mockReturnValue("application/json"), }, json: async () => ({ ...mockSpace, config: { accessType: "RESTRICTED" }, }), }) .mockResolvedValueOnce({ ok: true, headers: { get: vi.fn().mockReturnValue("application/json"), }, json: async () => ({}), }); await api.initialize(); // Step 1: Create Meet space const createdSpace = await api.createMeetSpace({ accessType: "TRUSTED", enableRecording: true, }); expect(createdSpace.name).toBe("spaces/integration-space-id"); expect(createdSpace.meetingUri).toContain("meet.google.com"); // Step 2: Update space configuration const updatedSpace = await api.updateMeetSpace( "spaces/integration-space-id", { accessType: "RESTRICTED", } ); expect(updatedSpace.config.accessType).toBe("RESTRICTED"); // Step 3: End active conference await api.endActiveConference("spaces/integration-space-id"); expect(mockFetch).toHaveBeenLastCalledWith( "https://meet.googleapis.com/v2/spaces/integration-space-id:endActiveConference", expect.objectContaining({ method: "POST" }) ); }); }); describe("Error Recovery Scenarios", () => { it("should handle network timeouts", async () => { mockFetch.mockRejectedValueOnce(new Error("Network timeout")); await api.initialize(); // Should fail on first attempt await expect(api.createMeetSpace({})).rejects.toThrow("Network timeout"); }); it("should handle API rate limiting", async () => { mockFetch.mockResolvedValue({ ok: false, status: 429, statusText: "Too Many Requests", headers: { get: vi.fn().mockReturnValue("application/json"), }, json: async () => ({ error: { message: "Rate limit exceeded", code: 429, }, }), }); await api.initialize(); await expect(api.createMeetSpace({})).rejects.toThrow( "Meet API Error: Too Many Requests" ); }); it("should handle invalid credentials", async () => { (fs.readFile as MockReadFile).mockImplementation(async (path: any) => { if (typeof path === 'string' && path.includes("credentials")) { return "invalid json content"; } return JSON.stringify(global.testUtils.getMockToken()); }); const invalidApi = new GoogleMeetAPI( "invalid-credentials.json", "token.json" ); await expect(invalidApi.initialize()).rejects.toThrow(); }); it("should handle expired tokens", async () => { const expiredToken = { ...global.testUtils.getMockToken(), expiry_date: Date.now() - 3600000, // Expired 1 hour ago }; (fs.readFile as MockReadFile).mockImplementation(async (path: any) => { if (typeof path === 'string' && path.includes("credentials")) { return JSON.stringify(global.testUtils.getMockCredentials()); } if (typeof path === 'string' && path.includes("token")) { return JSON.stringify(expiredToken); } throw new Error("File not found"); }); const expiredApi = new GoogleMeetAPI( "integration-credentials.json", "integration-token.json" ); mockAuth.getAccessToken.mockRejectedValue( new Error("Token has been expired or revoked") ); await expect(expiredApi.initialize()).rejects.toThrow( "Authentication failed" ); }); }); describe("Data Validation", () => { beforeEach(async () => { await api.initialize(); }); it("should validate event data before creation", async () => { const validEventData = { summary: "Valid Event", startTime: "2025-08-01T10:00:00Z", endTime: "2025-08-01T11:00:00Z", }; const event = await api.createCalendarEvent(validEventData); expect(event.id).toBe("integration-event-id"); }); it("should validate Meet space configuration", async () => { mockFetch.mockResolvedValue({ ok: true, headers: { get: vi.fn().mockReturnValue("application/json"), }, json: async () => ({ name: "spaces/validation-space" }), }); const validConfig = { accessType: "TRUSTED", enableRecording: true, enableTranscription: false, }; const space = await api.createMeetSpace(validConfig); expect(space.name).toBe("spaces/validation-space"); // Verify the request was made with correct configuration const fetchCall = mockFetch.mock.calls[0]; const requestBody = JSON.parse(fetchCall[1].body); expect(requestBody.config.accessType).toBe("TRUSTED"); }); }); describe("Concurrent Operations", () => { it("should handle multiple concurrent requests", async () => { await api.initialize(); // Make multiple concurrent requests const promises = Array.from({ length: 5 }, () => api.listCalendars()); const results = await Promise.all(promises); expect(results).toHaveLength(5); expect(mockCalendar.calendars.list).toHaveBeenCalledTimes(5); }); it("should handle concurrent Meet space operations", async () => { mockFetch.mockResolvedValue({ ok: true, headers: { get: vi.fn().mockReturnValue("application/json"), }, json: async () => ({ name: "spaces/concurrent-test" }), }); await api.initialize(); // Perform concurrent operations const promises = Array.from({ length: 3 }, () => api.createMeetSpace({ accessType: "OPEN" }) ); const results = await Promise.all(promises); expect(results).toHaveLength(3); expect(mockFetch).toHaveBeenCalledTimes(3); }); }); describe("Configuration Edge Cases", () => { it("should handle different time zones", async () => { await api.initialize(); const eventData = { summary: "Timezone Test", startTime: "2025-08-01T10:00:00Z", endTime: "2025-08-01T11:00:00Z", timeZone: "America/New_York", }; await api.createCalendarEvent(eventData); const callArgs = mockCalendar.events.insert.mock.calls[0][0]; expect(callArgs.resource.start.timeZone).toBe("America/New_York"); expect(callArgs.resource.end.timeZone).toBe("America/New_York"); }); it("should handle guest permissions correctly", async () => { await api.initialize(); const eventData = { summary: "Guest Permissions Test", startTime: "2025-08-01T10:00:00Z", endTime: "2025-08-01T11:00:00Z", guestPermissions: { canInviteOthers: false, canModify: true, canSeeOtherGuests: false, }, }; await api.createCalendarEvent(eventData); const callArgs = mockCalendar.events.insert.mock.calls[0][0]; expect(callArgs.resource.guestsCanInviteOthers).toBe(false); expect(callArgs.resource.guestsCanModify).toBe(true); expect(callArgs.resource.guestsCanSeeOtherGuests).toBe(false); }); }); });

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/INSIDE-HAIR/mcp-google-calendar-and-meet'

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