Skip to main content
Glama
label-validation.test.ts10.6 kB
import { describe, test, expect } from '@jest/globals'; import { LabelToolInputSchema } from '../../src/schemas/validation.js'; import { ZodError } from 'zod'; /** * T014: Unit test - label validation * Tests Zod schema validation for LabelToolInputSchema */ describe('Label Validation Schema', () => { describe('Action Enum Validation', () => { test('accepts valid actions', () => { const validActions = [ 'create', 'get', 'update', 'delete', 'list', 'rename_shared', 'remove_shared', ]; validActions.forEach(action => { const result = LabelToolInputSchema.safeParse({ action }); expect(result.success).toBe(true); }); }); test('rejects invalid actions', () => { const invalidActions = ['invalid', 'noop', 'archive', 'complete']; invalidActions.forEach(action => { const result = LabelToolInputSchema.safeParse({ action }); expect(result.success).toBe(false); if (!result.success) { expect(result.error).toBeInstanceOf(ZodError); } }); }); test('rejects missing action', () => { const result = LabelToolInputSchema.safeParse({}); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues[0].path).toContain('action'); } }); }); describe('Name Length Validation', () => { test('rejects empty name (0 chars)', () => { const result = LabelToolInputSchema.safeParse({ action: 'create', name: '', }); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues[0].path).toContain('name'); } }); test('accepts valid name length (128 chars)', () => { const name = 'a'.repeat(128); const result = LabelToolInputSchema.safeParse({ action: 'create', name, }); expect(result.success).toBe(true); }); test('rejects name over limit (129 chars)', () => { const name = 'a'.repeat(129); const result = LabelToolInputSchema.safeParse({ action: 'create', name, }); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues[0].path).toContain('name'); expect(result.error.issues[0].message).toMatch(/128|maximum|length/i); } }); test('accepts moderate name length (64 chars)', () => { const name = 'a'.repeat(64); const result = LabelToolInputSchema.safeParse({ action: 'create', name, }); expect(result.success).toBe(true); }); }); describe('Limit Validation', () => { test('rejects limit of 0', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', limit: 0, }); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues[0].path).toContain('limit'); } }); test('accepts limit of 1 (minimum)', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', limit: 1, }); expect(result.success).toBe(true); }); test('accepts limit of 200 (maximum)', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', limit: 200, }); expect(result.success).toBe(true); }); test('rejects limit of 201 (over maximum)', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', limit: 201, }); expect(result.success).toBe(false); if (!result.success) { expect(result.error.issues[0].path).toContain('limit'); expect(result.error.issues[0].message).toMatch(/200|maximum/i); } }); test('accepts default pagination (no limit)', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', }); expect(result.success).toBe(true); }); test('accepts limit of 50 (common value)', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', limit: 50, }); expect(result.success).toBe(true); }); }); describe('Conditional Required Fields Per Action', () => { describe('create action', () => { test('requires name', () => { const result = LabelToolInputSchema.safeParse({ action: 'create', }); // Schema should allow optional name, but tool implementation requires it expect(result.success).toBe(true); }); test('accepts optional color', () => { const result = LabelToolInputSchema.safeParse({ action: 'create', name: 'Work', color: 'blue', }); expect(result.success).toBe(true); }); test('accepts optional order', () => { const result = LabelToolInputSchema.safeParse({ action: 'create', name: 'Work', order: 1, }); expect(result.success).toBe(true); }); test('accepts optional is_favorite', () => { const result = LabelToolInputSchema.safeParse({ action: 'create', name: 'Work', is_favorite: true, }); expect(result.success).toBe(true); }); }); describe('get action', () => { test('requires label_id', () => { const result = LabelToolInputSchema.safeParse({ action: 'get', }); // Schema allows optional label_id, but tool implementation requires it expect(result.success).toBe(true); }); test('accepts valid label_id', () => { const result = LabelToolInputSchema.safeParse({ action: 'get', label_id: '2156154810', }); expect(result.success).toBe(true); }); }); describe('update action', () => { test('requires label_id', () => { const result = LabelToolInputSchema.safeParse({ action: 'update', }); // Schema allows optional label_id, but tool implementation requires it expect(result.success).toBe(true); }); test('accepts optional color update', () => { const result = LabelToolInputSchema.safeParse({ action: 'update', label_id: '2156154810', color: 'red', }); expect(result.success).toBe(true); }); test('accepts optional is_favorite update', () => { const result = LabelToolInputSchema.safeParse({ action: 'update', label_id: '2156154810', is_favorite: false, }); expect(result.success).toBe(true); }); test('accepts multiple field updates', () => { const result = LabelToolInputSchema.safeParse({ action: 'update', label_id: '2156154810', color: 'green', order: 5, is_favorite: true, }); expect(result.success).toBe(true); }); }); describe('delete action', () => { test('requires label_id', () => { const result = LabelToolInputSchema.safeParse({ action: 'delete', }); // Schema allows optional label_id, but tool implementation requires it expect(result.success).toBe(true); }); test('accepts valid label_id', () => { const result = LabelToolInputSchema.safeParse({ action: 'delete', label_id: '2156154810', }); expect(result.success).toBe(true); }); }); describe('list action', () => { test('accepts no parameters', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', }); expect(result.success).toBe(true); }); test('accepts cursor', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', cursor: 'cursor_abc123', }); expect(result.success).toBe(true); }); test('accepts limit', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', limit: 50, }); expect(result.success).toBe(true); }); test('accepts cursor and limit together', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', cursor: 'cursor_abc123', limit: 100, }); expect(result.success).toBe(true); }); }); describe('rename_shared action', () => { test('requires name and new_name', () => { const result = LabelToolInputSchema.safeParse({ action: 'rename_shared', }); // Schema allows optional fields, but tool implementation requires them expect(result.success).toBe(true); }); test('accepts valid rename parameters', () => { const result = LabelToolInputSchema.safeParse({ action: 'rename_shared', name: 'OldName', new_name: 'NewName', }); expect(result.success).toBe(true); }); }); describe('remove_shared action', () => { test('requires name', () => { const result = LabelToolInputSchema.safeParse({ action: 'remove_shared', }); // Schema allows optional name, but tool implementation requires it expect(result.success).toBe(true); }); test('accepts valid name', () => { const result = LabelToolInputSchema.safeParse({ action: 'remove_shared', name: 'SharedLabel', }); expect(result.success).toBe(true); }); }); }); describe('Type Validation', () => { test('rejects non-string label_id', () => { const result = LabelToolInputSchema.safeParse({ action: 'get', label_id: 123, }); expect(result.success).toBe(false); }); test('rejects non-number limit', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', limit: '50', }); expect(result.success).toBe(false); }); test('rejects non-boolean is_favorite', () => { const result = LabelToolInputSchema.safeParse({ action: 'create', name: 'Work', is_favorite: 'true', }); expect(result.success).toBe(false); }); test('rejects non-string cursor', () => { const result = LabelToolInputSchema.safeParse({ action: 'list', cursor: 123, }); expect(result.success).toBe(false); }); }); });

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/shayonpal/mcp-todoist'

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