Skip to main content
Glama

Scryfall MCP Server

by bmurdock
query-validator.test.ts8.72 kB
import { describe, it, expect } from "vitest"; import { validateScryfallQuery, validateScryfallQuerySync, validateScryfallQueryAsync, } from "../../src/utils/query-validator.js"; describe("Simple Query Validator", () => { describe("Basic Validation", () => { it("should validate simple card name queries", () => { const result = validateScryfallQuery("lightning bolt"); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); expect(result.warnings).toHaveLength(0); }); it("should validate basic operator queries", () => { const result = validateScryfallQuery("c:red"); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should validate complex boolean queries", () => { const result = validateScryfallQuery("c:red AND t:creature"); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should validate quoted string queries", () => { const result = validateScryfallQuery('o:"enters the battlefield"'); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should validate comparison operators", () => { const result = validateScryfallQuery("cmc>=3"); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should validate nested parentheses", () => { const result = validateScryfallQuery("(c:red OR c:blue) AND (t:creature OR t:instant)"); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should validate multiple operators", () => { const result = validateScryfallQuery("c:red t:creature mv:3 set:dom"); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); }); describe("Error Detection", () => { it("should detect empty queries", () => { const result = validateScryfallQuery(""); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0].message).toContain("empty"); expect(result.errors[0].severity).toBe("error"); }); it("should detect whitespace-only queries", () => { const result = validateScryfallQuery(" "); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0].message).toContain("empty"); }); it("should detect mismatched opening parentheses", () => { const result = validateScryfallQuery("(c:red AND t:creature"); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0].message).toContain("parentheses"); expect(result.suggestions).toContain('Add closing parenthesis ")"'); }); it("should detect mismatched closing parentheses", () => { const result = validateScryfallQuery("c:red AND t:creature)"); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0].message).toContain("parentheses"); expect(result.suggestions).toContain('Add opening parenthesis "("'); }); it("should detect unmatched quotes", () => { const result = validateScryfallQuery('o:"enters the battlefield'); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0].message).toContain("quotes"); expect(result.suggestions).toContain('Add closing quote "'); }); it("should detect multiple quote pairs with odd count", () => { const result = validateScryfallQuery('o:"first" AND o:"second'); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.errors[0].message).toContain("quotes"); }); }); describe("Warning Detection", () => { it("should warn about unknown operators", () => { const result = validateScryfallQuery("unknown:value"); expect(result.isValid).toBe(true); expect(result.warnings).toHaveLength(1); expect(result.warnings[0].message).toContain("Unknown operator"); expect(result.warnings[0].severity).toBe("warning"); }); it("should suggest similar operators for typos", () => { const result = validateScryfallQuery("colr:red"); expect(result.isValid).toBe(true); expect(result.warnings).toHaveLength(1); expect(result.suggestions).toBeDefined(); expect(result.suggestions![0]).toContain("color"); }); it("should warn about && usage", () => { const result = validateScryfallQuery("c:red && t:creature"); expect(result.isValid).toBe(true); expect(result.warnings).toHaveLength(1); expect(result.warnings[0].message).toContain("AND"); expect(result.suggestions).toContain('Replace "&&" with "AND"'); }); it("should warn about || usage", () => { const result = validateScryfallQuery("c:red || c:blue"); expect(result.isValid).toBe(true); expect(result.warnings).toHaveLength(1); expect(result.warnings[0].message).toContain("OR"); expect(result.suggestions).toContain('Replace "||" with "OR"'); }); it("should warn about empty operator values", () => { const result = validateScryfallQuery('c:""'); expect(result.isValid).toBe(true); expect(result.warnings).toHaveLength(1); expect(result.warnings[0].message).toContain("Empty operator value"); }); it("should warn about very long queries", () => { const longQuery = "c:red ".repeat(100); // Creates a 600+ character query const result = validateScryfallQuery(longQuery); expect(result.isValid).toBe(true); expect(result.warnings).toHaveLength(1); expect(result.warnings[0].message).toContain("Very long query"); }); }); describe("Known Operators", () => { const knownOperators = [ "c", "color", "id", "identity", "m", "mana", "mv", "manavalue", "cmc", "name", "n", "oracle", "o", "type", "t", "power", "pow", "toughness", "tou", "set", "s", "rarity", "r", "format", "f", "is", "not", "usd", ]; knownOperators.forEach((operator) => { it(`should recognize ${operator} as a valid operator`, () => { const result = validateScryfallQuery(`${operator}:test`); expect(result.isValid).toBe(true); expect(result.warnings).toHaveLength(0); }); }); }); describe("Async Validation", () => { it("should provide async validation", async () => { const result = await validateScryfallQueryAsync("c:red"); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should handle async errors", async () => { const result = await validateScryfallQueryAsync(""); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); }); }); describe("Sync Validation", () => { it("should provide sync validation", () => { const result = validateScryfallQuerySync("c:red"); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should handle sync errors", () => { const result = validateScryfallQuerySync(""); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); }); }); describe("Edge Cases", () => { it("should handle queries with only spaces and operators", () => { const result = validateScryfallQuery(" c:red "); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should handle complex nested structures", () => { const result = validateScryfallQuery( "((c:red OR c:blue) AND t:creature) OR (c:green AND t:instant)" ); expect(result.isValid).toBe(true); expect(result.errors).toHaveLength(0); }); it("should handle multiple unknown operators", () => { const result = validateScryfallQuery("xyz:value abc:value"); expect(result.isValid).toBe(true); // Should detect at least one unknown operator expect(result.warnings.length).toBeGreaterThanOrEqual(1); expect(result.warnings.some((w) => w.message.includes("Unknown operator"))).toBe(true); }); it("should handle mixed valid and invalid syntax", () => { const result = validateScryfallQuery("c:red AND (t:creature"); expect(result.isValid).toBe(false); expect(result.errors).toHaveLength(1); expect(result.warnings).toHaveLength(0); }); }); });

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/bmurdock/scryfall-mcp'

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