Skip to main content
Glama

Dice Rolling MCP Server

by jimmcq
search-content.test.ts10.3 kB
import { searchContent, fetchContent, searchableContent, contentLibrary, } from '../src/shared/search-content'; describe('Search Content Module', () => { describe('searchContent', () => { test('throws error for empty query', () => { expect(() => searchContent('')).toThrow('Search query is required'); expect(() => searchContent(' ')).toThrow('Search query is required'); }); test('searches by title', () => { const results = searchContent('advantage'); expect(results).toHaveLength(1); expect(results[0].id).toBe('advantage-disadvantage'); expect(results[0].title).toBe('D&D Advantage and Disadvantage'); }); test('searches by content keywords', () => { const results = searchContent('2d20kh1'); expect(results.length).toBeGreaterThan(0); expect(results.some(r => r.id === 'advantage-disadvantage')).toBe(true); }); test('searches by description', () => { const results = searchContent('fireball'); expect(results.length).toBeGreaterThan(0); expect( results.some(r => r.id === 'spell-damage' || r.id === 'combat-examples') ).toBe(true); }); test('returns multiple matches for broad search', () => { const results = searchContent('dice'); expect(results.length).toBeGreaterThanOrEqual(3); }); test('is case insensitive', () => { const lowerResults = searchContent('advantage'); const upperResults = searchContent('ADVANTAGE'); const mixedResults = searchContent('AdVaNtAgE'); expect(lowerResults).toEqual(upperResults); expect(lowerResults).toEqual(mixedResults); }); test('trims whitespace from query', () => { const results1 = searchContent(' advantage '); const results2 = searchContent('advantage'); expect(results1).toEqual(results2); }); test('returns empty array for no matches', () => { const results = searchContent('nonexistentquery12345'); expect(results).toEqual([]); }); test('returns results with correct structure', () => { const results = searchContent('basic'); expect(results.length).toBeGreaterThan(0); results.forEach(result => { expect(result).toHaveProperty('id'); expect(result).toHaveProperty('title'); expect(result).toHaveProperty('url'); expect(typeof result.id).toBe('string'); expect(typeof result.title).toBe('string'); expect(typeof result.url).toBe('string'); }); }); test('finds all expected content types', () => { // Test that all major content types are searchable const basicResults = searchContent('basic'); const advantageResults = searchContent('advantage'); const explodingResults = searchContent('exploding'); const fudgeResults = searchContent('fudge'); const combatResults = searchContent('combat'); expect(basicResults.length).toBeGreaterThan(0); expect(advantageResults.length).toBeGreaterThan(0); expect(explodingResults.length).toBeGreaterThan(0); expect(fudgeResults.length).toBeGreaterThan(0); expect(combatResults.length).toBeGreaterThan(0); }); }); describe('fetchContent', () => { test('throws error for empty id', () => { expect(() => fetchContent('')).toThrow('ID is required for fetch'); expect(() => fetchContent(' ')).toThrow('ID is required for fetch'); }); test('throws error for invalid id', () => { expect(() => fetchContent('invalid-id')).toThrow( 'Content not found for ID: invalid-id' ); }); test('fetches basic notation content', () => { const doc = fetchContent('basic-notation'); expect(doc.id).toBe('basic-notation'); expect(doc.title).toBe('Basic Dice Notation'); expect(doc.url).toBe('dice://guide/notation#basic'); expect(doc.text).toContain('**XdY** format'); expect(doc.metadata.source).toBe('dice-rolling-mcp'); expect(doc.metadata.type).toBe('dice-notation-guide'); expect(doc.metadata.category).toBe('mechanics'); }); test('fetches advantage-disadvantage content', () => { const doc = fetchContent('advantage-disadvantage'); expect(doc.id).toBe('advantage-disadvantage'); expect(doc.title).toBe('D&D Advantage and Disadvantage'); expect(doc.text).toContain('2d20kh1'); expect(doc.text).toContain('CRITICAL'); }); test('fetches combat examples content', () => { const doc = fetchContent('combat-examples'); expect(doc.id).toBe('combat-examples'); expect(doc.metadata.category).toBe('examples'); expect(doc.text).toContain('Attack Roll'); }); test('returns correct structure for all content', () => { const ids = [ 'basic-notation', 'advantage-disadvantage', 'modifiers', 'ability-scores', 'exploding-dice', 'rerolling', 'percentile-dice', 'fudge-dice', 'success-counting', 'combat-examples', 'spell-damage', 'skill-checks', ]; ids.forEach(id => { const doc = fetchContent(id); expect(doc).toHaveProperty('id'); expect(doc).toHaveProperty('title'); expect(doc).toHaveProperty('text'); expect(doc).toHaveProperty('url'); expect(doc).toHaveProperty('metadata'); expect(doc.metadata).toHaveProperty('source'); expect(doc.metadata).toHaveProperty('type'); expect(doc.metadata).toHaveProperty('category'); }); }); test('categorizes examples correctly', () => { const combatDoc = fetchContent('combat-examples'); const spellDoc = fetchContent('spell-damage'); const skillDoc = fetchContent('skill-checks'); // IDs with 'example' substring are categorized as 'examples' expect(combatDoc.metadata.category).toBe('examples'); expect(spellDoc.metadata.category).toBe('mechanics'); // 'spell-damage' doesn't contain 'example' expect(skillDoc.metadata.category).toBe('mechanics'); // 'skill-checks' doesn't contain 'example' }); test('categorizes mechanics correctly', () => { const basicDoc = fetchContent('basic-notation'); const advantageDoc = fetchContent('advantage-disadvantage'); const explodingDoc = fetchContent('exploding-dice'); expect(basicDoc.metadata.category).toBe('mechanics'); expect(advantageDoc.metadata.category).toBe('mechanics'); expect(explodingDoc.metadata.category).toBe('mechanics'); }); }); describe('searchableContent array', () => { test('has expected number of items', () => { expect(searchableContent.length).toBe(12); }); test('all items have required fields', () => { searchableContent.forEach(item => { expect(item).toHaveProperty('id'); expect(item).toHaveProperty('title'); expect(item).toHaveProperty('url'); expect(item).toHaveProperty('content'); expect(item).toHaveProperty('description'); }); }); test('all ids are unique', () => { const ids = searchableContent.map(item => item.id); const uniqueIds = new Set(ids); expect(uniqueIds.size).toBe(ids.length); }); test('all urls use dice:// protocol', () => { searchableContent.forEach(item => { expect(item.url).toMatch(/^dice:\/\//); }); }); }); describe('contentLibrary object', () => { test('has content for all searchable items', () => { searchableContent.forEach(item => { expect(contentLibrary).toHaveProperty(item.id); expect(typeof contentLibrary[item.id]).toBe('string'); expect(contentLibrary[item.id].length).toBeGreaterThan(0); }); }); test('all content is markdown formatted', () => { Object.values(contentLibrary).forEach(content => { // Check for markdown indicators const hasMarkdown = content.includes('#') || content.includes('**') || content.includes('`') || content.includes('-'); expect(hasMarkdown).toBe(true); }); }); test('advantage-disadvantage content has critical warning', () => { const content = contentLibrary['advantage-disadvantage']; expect(content).toContain('CRITICAL'); expect(content).toContain('2d20kh1'); expect(content).toContain('CORRECT'); expect(content).toContain('WRONG'); }); test('basic-notation explains XdY format', () => { const content = contentLibrary['basic-notation']; expect(content).toContain('XdY'); expect(content).toContain('1d20'); expect(content).toContain('3d6'); }); test('exploding-dice explains mechanic', () => { const content = contentLibrary['exploding-dice']; expect(content).toContain('exploding'); expect(content).toContain('maximum'); expect(content).toContain('3d6!'); }); }); describe('integration between search and fetch', () => { test('search results can be fetched', () => { const searchResults = searchContent('advantage'); expect(searchResults.length).toBeGreaterThan(0); searchResults.forEach(result => { const doc = fetchContent(result.id); expect(doc.id).toBe(result.id); expect(doc.title).toBe(result.title); expect(doc.url).toBe(result.url); }); }); test('all searchable items can be fetched', () => { searchableContent.forEach(item => { expect(() => fetchContent(item.id)).not.toThrow(); }); }); test('fetch returns richer data than search', () => { const searchResults = searchContent('basic'); const searchResult = searchResults[0]; expect(searchResult).toHaveProperty('id'); expect(searchResult).toHaveProperty('title'); expect(searchResult).toHaveProperty('url'); expect(searchResult).not.toHaveProperty('text'); expect(searchResult).not.toHaveProperty('metadata'); const fetchResult = fetchContent(searchResult.id); expect(fetchResult).toHaveProperty('id'); expect(fetchResult).toHaveProperty('title'); expect(fetchResult).toHaveProperty('url'); expect(fetchResult).toHaveProperty('text'); expect(fetchResult).toHaveProperty('metadata'); }); }); });

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/jimmcq/dice-rolling-mcp'

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