Skip to main content
Glama
samwang0723

Restaurant Booking MCP Server

restaurantRecommendationService.test.ts11.8 kB
import { RestaurantRecommendationService } from '../services/restaurantRecommendationService.js'; import { Restaurant, RestaurantSearchParams } from '../types/index.js'; describe('RestaurantRecommendationService', () => { let service: RestaurantRecommendationService; beforeEach(() => { service = new RestaurantRecommendationService(); }); const createMockRestaurant = ( overrides: Partial<Restaurant> = {} ): Restaurant => ({ placeId: 'test-place', name: 'Test Restaurant', address: '123 Test St', location: { latitude: 37.7749, longitude: -122.4194 }, rating: 4.5, userRatingsTotal: 100, priceLevel: 2, cuisineTypes: ['Italian'], ...overrides, }); const mockSearchParams: RestaurantSearchParams = { location: { latitude: 37.7749, longitude: -122.4194 }, cuisineTypes: ['Italian'], mood: 'romantic', event: 'dating', radius: 2000, }; describe('Parallel Processing', () => { test('should process restaurants in parallel', async () => { const restaurants = Array.from({ length: 10 }, (_, i) => createMockRestaurant({ placeId: `place_${i}`, name: `Restaurant ${i}`, rating: 4.0 + i / 10, // Varying ratings }) ); const startTime = Date.now(); const recommendations = await service.getRecommendations( restaurants, mockSearchParams ); const duration = Date.now() - startTime; // Should complete quickly due to parallel processing expect(duration).toBeLessThan(200); // Increased from 100ms to account for system load variations expect(recommendations).toHaveLength(3); // Should be sorted by score (highest first) expect(recommendations[0].score).toBeGreaterThanOrEqual( recommendations[1].score ); expect(recommendations[1].score).toBeGreaterThanOrEqual( recommendations[2].score ); }); test('should handle large datasets efficiently', async () => { const restaurants = Array.from({ length: 100 }, (_, i) => createMockRestaurant({ placeId: `place_${i}`, name: `Restaurant ${i}`, rating: 3.0 + Math.random() * 2, // Random ratings between 3-5 priceLevel: (Math.floor(Math.random() * 4) + 1) as 1 | 2 | 3 | 4, cuisineTypes: i % 2 === 0 ? ['Italian'] : ['Japanese'], }) ); const startTime = Date.now(); const recommendations = await service.getRecommendations( restaurants, mockSearchParams ); const duration = Date.now() - startTime; // Should handle 100 restaurants efficiently expect(duration).toBeLessThan(300); // Increased from 200ms to account for processing 100 restaurants with system load variations expect(recommendations).toHaveLength(3); }); }); describe('Scoring Algorithm', () => { test('should score restaurants based on rating', () => { const highRatedRestaurant = createMockRestaurant({ rating: 4.8, userRatingsTotal: 500, }); const lowRatedRestaurant = createMockRestaurant({ rating: 3.2, userRatingsTotal: 50, }); const highScore = (service as any).calculateRestaurantScore( highRatedRestaurant, mockSearchParams ); const lowScore = (service as any).calculateRestaurantScore( lowRatedRestaurant, mockSearchParams ); expect(highScore).toBeGreaterThan(lowScore); }); test('should score cuisine matches higher', () => { const matchingRestaurant = createMockRestaurant({ cuisineTypes: ['Italian', 'Mediterranean'], }); const nonMatchingRestaurant = createMockRestaurant({ cuisineTypes: ['Chinese', 'Asian'], }); const matchingScore = (service as any).calculateRestaurantScore( matchingRestaurant, mockSearchParams ); const nonMatchingScore = (service as any).calculateRestaurantScore( nonMatchingRestaurant, mockSearchParams ); expect(matchingScore).toBeGreaterThan(nonMatchingScore); }); test('should consider event suitability', () => { const datingRestaurant = createMockRestaurant({ priceLevel: 3, cuisineTypes: ['Italian', 'Fine Dining'], rating: 4.5, }); const casualRestaurant = createMockRestaurant({ priceLevel: 1, cuisineTypes: ['Fast Food'], rating: 4.5, }); const datingSuitability = (service as any).calculateEventSuitability( datingRestaurant, 'dating' ); const casualSuitability = (service as any).calculateEventSuitability( casualRestaurant, 'dating' ); expect(datingSuitability).toBeGreaterThan(casualSuitability); }); test('should consider mood matching', () => { const romanticRestaurant = createMockRestaurant({ name: 'Romantic Candlelit Restaurant', cuisineTypes: ['French', 'Fine Dining'], priceLevel: 4, }); const casualRestaurant = createMockRestaurant({ name: 'Sports Bar Grill', cuisineTypes: ['American'], priceLevel: 2, }); const romanticMoodMatch = (service as any).calculateMoodMatch( romanticRestaurant, 'romantic' ); const casualMoodMatch = (service as any).calculateMoodMatch( casualRestaurant, 'romantic' ); expect(romanticMoodMatch).toBeGreaterThan(casualMoodMatch); }); }); describe('Event Suitability', () => { test('should recommend appropriate restaurants for dating', () => { const restaurants = [ createMockRestaurant({ placeId: 'fine-dining', name: 'Elegant Fine Dining', priceLevel: 4, cuisineTypes: ['French', 'Fine Dining'], rating: 4.7, }), createMockRestaurant({ placeId: 'fast-food', name: 'Quick Burger Joint', priceLevel: 1, cuisineTypes: ['Fast Food'], rating: 4.2, }), ]; const fineDiningSuitability = (service as any).calculateEventSuitability( restaurants[0], 'dating' ); const fastFoodSuitability = (service as any).calculateEventSuitability( restaurants[1], 'dating' ); expect(fineDiningSuitability).toBeGreaterThan(fastFoodSuitability); }); test('should recommend appropriate restaurants for business meetings', () => { const businessRestaurant = createMockRestaurant({ priceLevel: 3, cuisineTypes: ['American', 'Steakhouse'], rating: 4.5, }); const casualRestaurant = createMockRestaurant({ priceLevel: 1, cuisineTypes: ['Pizza'], rating: 4.5, }); const businessSuitability = (service as any).calculateEventSuitability( businessRestaurant, 'business' ); const casualSuitability = (service as any).calculateEventSuitability( casualRestaurant, 'business' ); expect(businessSuitability).toBeGreaterThan(casualSuitability); }); }); describe('Reasoning Generation', () => { test('should generate comprehensive reasoning', () => { const restaurant = createMockRestaurant({ rating: 4.6, userRatingsTotal: 250, cuisineTypes: ['Italian'], priceLevel: 3, openingHours: { openNow: true, weekdayText: ['Monday: 5:00 PM – 10:00 PM'], }, }); const reasoning = (service as any).generateReasoning( restaurant, mockSearchParams, 85, // High score 8, // High event suitability 7 // Good mood match ); expect(reasoning).toContain('Excellent rating'); expect(reasoning).toContain('Italian cuisine'); expect(reasoning).toContain('Perfect for dating'); expect(reasoning).toContain('Currently open'); }); test('should handle restaurants with missing data', () => { const restaurant = createMockRestaurant({ rating: 0, userRatingsTotal: 0, priceLevel: undefined, }); const reasoning = (service as any).generateReasoning( restaurant, mockSearchParams, 30, // Low score 5, // Average event suitability 5 // Average mood match ); expect(reasoning).toBeTruthy(); expect(reasoning.length).toBeGreaterThan(0); }); }); describe('Edge Cases', () => { test('should handle empty restaurant list', async () => { const recommendations = await service.getRecommendations( [], mockSearchParams ); expect(recommendations).toHaveLength(0); }); test('should handle restaurants with identical scores', async () => { const restaurants = Array.from({ length: 5 }, (_, i) => createMockRestaurant({ placeId: `identical_${i}`, name: `Identical Restaurant ${i}`, rating: 4.5, userRatingsTotal: 100, priceLevel: 2, cuisineTypes: ['Italian'], }) ); const recommendations = await service.getRecommendations( restaurants, mockSearchParams ); expect(recommendations).toHaveLength(3); // All should have similar scores const scores = recommendations.map(r => r.score); const maxScore = Math.max(...scores); const minScore = Math.min(...scores); expect(maxScore - minScore).toBeLessThan(5); // Small variance }); test('should handle missing search criteria', async () => { const restaurants = [createMockRestaurant()]; const emptyParams: RestaurantSearchParams = { cuisineTypes: [], mood: '', event: '', }; const recommendations = await service.getRecommendations( restaurants, emptyParams ); expect(recommendations).toHaveLength(1); expect(recommendations[0].score).toBeGreaterThan(0); }); }); describe('Performance Benchmarks', () => { test('should maintain performance with varying restaurant counts', async () => { const testSizes = [5, 25, 50, 100]; const timings: number[] = []; for (const size of testSizes) { const restaurants = Array.from({ length: size }, (_, i) => createMockRestaurant({ placeId: `bench_${i}`, rating: 3 + Math.random() * 2, }) ); const startTime = Date.now(); await service.getRecommendations(restaurants, mockSearchParams); const duration = Date.now() - startTime; timings.push(duration); } // Performance should scale reasonably (not exponentially) // Allow more generous scaling for small sample sizes since timing can be inconsistent expect(timings[3]).toBeLessThan(Math.max(timings[0] * 100, 50)); // 100 items shouldn't take 100x longer than 5 items or more than 50ms }); test('should handle concurrent recommendation requests', async () => { const restaurants = Array.from({ length: 20 }, (_, i) => createMockRestaurant({ placeId: `concurrent_${i}` }) ); const concurrentRequests = Array(5) .fill(null) .map(() => service.getRecommendations(restaurants, mockSearchParams)); const startTime = Date.now(); const results = await Promise.all(concurrentRequests); const duration = Date.now() - startTime; expect(duration).toBeLessThan(500); // Should handle 5 concurrent requests quickly expect(results).toHaveLength(5); results.forEach(result => { expect(result).toHaveLength(3); }); }); }); });

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/samwang0723/mcp-booking'

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