Skip to main content
Glama
grovesjosephn

Pokemon MCP Server

ingestion.test.ts10.6 kB
import { describe, it, expect, beforeAll, afterAll, beforeEach, vi, } from 'vitest'; import Database from 'better-sqlite3'; import { TestDatabase } from './tests/helpers/testDatabase.js'; // Mock node-fetch vi.mock('node-fetch', () => ({ default: vi.fn(), })); import fetch from 'node-fetch'; const mockFetch = vi.mocked(fetch); // Import ingestion types interface Pokemon { id: number; name: string; height: number; weight: number; base_experience: number; generation: number; species?: { name: string; url: string }; sprites: { front_default: string; back_default: string; front_shiny: string; back_shiny: string; }; stats: Array<{ stat: { name: string; url: string }; base_stat: number; effort: number; }>; types: Array<{ slot: number; type: { name: string; url: string }; }>; abilities: Array<{ ability: { name: string; url: string }; is_hidden: boolean; slot: number; }>; } describe('Pokemon Data Ingestion', () => { let testDb: TestDatabase; let db: Database.Database; beforeAll(async () => { // Create test database with production schema testDb = new TestDatabase('ingestion-test'); testDb.insertTestData(); db = testDb.getDatabase(); }); afterAll(async () => { if (testDb) { await testDb.cleanup(); } }); beforeEach(() => { // Clear all data before each test testDb.clearData(); // Re-insert basic reference data needed for ingestion testDb.insertTestData(); // Reset all mocks vi.clearAllMocks(); }); describe('Database Schema', () => { it('should create all required tables', () => { const tables = db .prepare("SELECT name FROM sqlite_master WHERE type='table'") .all() as any[]; const tableNames = tables.map((t) => t.name); expect(tableNames).toContain('pokemon'); expect(tableNames).toContain('stats'); expect(tableNames).toContain('pokemon_types'); expect(tableNames).toContain('pokemon_abilities'); expect(tableNames).toContain('types'); expect(tableNames).toContain('abilities'); expect(tableNames).toContain('moves'); expect(tableNames).toContain('pokemon_moves'); }); it('should have correct pokemon table structure', () => { const columns = db.prepare('PRAGMA table_info(pokemon)').all() as any[]; const columnNames = columns.map((c) => c.name); expect(columnNames).toContain('id'); expect(columnNames).toContain('name'); expect(columnNames).toContain('height'); expect(columnNames).toContain('weight'); expect(columnNames).toContain('base_experience'); expect(columnNames).toContain('generation'); expect(columnNames).toContain('species_url'); expect(columnNames).toContain('sprite_url'); }); }); describe('Data Insertion', () => { it('should insert pokemon data correctly', () => { const pokemon = { id: 1, name: 'bulbasaur', height: 7, weight: 69, base_experience: 64, generation: 1, species_url: 'https://pokeapi.co/api/v2/pokemon-species/1/', sprite_url: 'https://raw.githubusercontent.com/PokeAPI/sprites/master/sprites/pokemon/1.png', }; const insertPokemon = db.prepare(` INSERT INTO pokemon (id, name, height, weight, base_experience, generation, species_url, sprite_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `); const result = insertPokemon.run( pokemon.id, pokemon.name, pokemon.height, pokemon.weight, pokemon.base_experience, pokemon.generation, pokemon.species_url, pokemon.sprite_url ); expect(result.changes).toBe(1); const retrieved = db .prepare('SELECT * FROM pokemon WHERE id = ?') .get(1) as any; expect(retrieved.name).toBe('bulbasaur'); expect(retrieved.generation).toBe(1); }); it('should insert pokemon stats correctly', () => { // First insert a pokemon const insertPokemon = db.prepare(` INSERT INTO pokemon (id, name, height, weight, base_experience, generation, species_url, sprite_url) VALUES (1, 'bulbasaur', 7, 69, 64, 1, 'test', 'test') `); insertPokemon.run(); // Insert stats const stats = [ { pokemon_id: 1, stat_name: 'hp', base_stat: 45, effort: 0 }, { pokemon_id: 1, stat_name: 'attack', base_stat: 49, effort: 0 }, ]; const insertStat = db.prepare(` INSERT INTO stats (pokemon_id, stat_name, base_stat, effort) VALUES (?, ?, ?, ?) `); stats.forEach((stat) => { const result = insertStat.run( stat.pokemon_id, stat.stat_name, stat.base_stat, stat.effort ); expect(result.changes).toBe(1); }); const retrievedStats = db .prepare('SELECT * FROM stats WHERE pokemon_id = 1') .all(); expect(retrievedStats).toHaveLength(2); }); it('should insert pokemon types correctly', () => { // First insert a pokemon const insertPokemon = db.prepare(` INSERT INTO pokemon (id, name, height, weight, base_experience, generation, species_url, sprite_url) VALUES (1, 'bulbasaur', 7, 69, 64, 1, 'test', 'test') `); insertPokemon.run(); // Insert types (using the production schema with separate types table) const typeData = [ { id: 1, name: 'grass' }, { id: 2, name: 'poison' }, ]; // Types should already exist from test data, but ensure they're there const insertTypeRecord = db.prepare(` INSERT OR IGNORE INTO types (id, name) VALUES (?, ?) `); typeData.forEach((type) => { insertTypeRecord.run(type.id, type.name); }); // Insert pokemon-type relationships using correct production schema const insertPokemonType = db.prepare(` INSERT INTO pokemon_types (pokemon_id, type_id, slot) VALUES (?, ?, ?) `); typeData.forEach((type, index) => { const result = insertPokemonType.run(1, type.id, index + 1); expect(result.changes).toBe(1); }); // Retrieve types using JOIN (production-style query) const retrievedTypes = db .prepare( ` SELECT t.name as type_name FROM pokemon_types pt JOIN types t ON pt.type_id = t.id WHERE pt.pokemon_id = 1 ORDER BY pt.slot ` ) .all() as any[]; const typeNames = retrievedTypes.map((t) => t.type_name); expect(typeNames).toContain('grass'); expect(typeNames).toContain('poison'); }); it('should handle duplicate pokemon insertions', () => { const insertPokemon = db.prepare(` INSERT OR REPLACE INTO pokemon (id, name, height, weight, base_experience, generation, species_url, sprite_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `); // Insert first time insertPokemon.run(1, 'bulbasaur', 7, 69, 64, 1, 'test', 'test'); // Insert again with same ID insertPokemon.run(1, 'updated-bulbasaur', 8, 70, 65, 1, 'test', 'test'); const result = db .prepare('SELECT * FROM pokemon WHERE id = 1') .get() as any; expect(result.name).toBe('updated-bulbasaur'); expect(result.height).toBe(8); }); }); describe('Data Validation', () => { it('should handle missing required fields', () => { const insertPokemon = db.prepare(` INSERT INTO pokemon (name) VALUES (?) `); // Name is NOT NULL, so this should throw expect(() => { insertPokemon.run(null); }).toThrow(); }); it('should validate foreign key constraints', () => { // First insert a valid pokemon const insertPokemon = db.prepare(` INSERT INTO pokemon (id, name, height, weight, base_experience, generation, species_url, sprite_url) VALUES (1, 'test-pokemon', 7, 69, 64, 1, 'test', 'test') `); insertPokemon.run(); const insertStat = db.prepare(` INSERT INTO stats (pokemon_id, stat_name, base_stat, effort) VALUES (?, ?, ?, ?) `); // This should work with a valid foreign key const result = insertStat.run(1, 'hp', 50, 0); expect(result.changes).toBe(1); // Verify the stat was inserted const stat = db.prepare('SELECT * FROM stats WHERE pokemon_id = 1').get(); expect(stat).toBeDefined(); }); }); describe('API Response Processing', () => { it('should handle valid pokemon API response format', () => { const mockPokemon: Pokemon = { id: 1, name: 'bulbasaur', height: 7, weight: 69, base_experience: 64, generation: 1, sprites: { front_default: 'sprite_url', back_default: 'back_sprite', front_shiny: 'shiny_sprite', back_shiny: 'back_shiny', }, stats: [ { stat: { name: 'hp', url: 'url' }, base_stat: 45, effort: 0 }, { stat: { name: 'attack', url: 'url' }, base_stat: 49, effort: 0 }, ], types: [ { slot: 1, type: { name: 'grass', url: 'url' } }, { slot: 2, type: { name: 'poison', url: 'url' } }, ], abilities: [ { ability: { name: 'overgrow', url: 'url' }, is_hidden: false, slot: 1, }, ], }; // This would be the data structure we expect from the API expect(mockPokemon.id).toBe(1); expect(mockPokemon.stats).toHaveLength(2); expect(mockPokemon.types).toHaveLength(2); expect(mockPokemon.abilities).toHaveLength(1); }); }); describe('Error Handling', () => { it('should handle database connection errors gracefully', () => { expect(() => { new Database('/invalid/path/database.sqlite'); }).toThrow(); }); it('should handle malformed data gracefully', () => { const insertPokemon = db.prepare(` INSERT INTO pokemon (id, name, height, weight, base_experience, generation, species_url, sprite_url) VALUES (?, ?, ?, ?, ?, ?, ?, ?) `); // Test with invalid data types expect(() => { insertPokemon.run( 'not-a-number', 'test', 'not-a-number', 69, 64, 1, 'test', 'test' ); }).toThrow(); }); }); });

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/grovesjosephn/pokemcp'

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