/**
* Unit tests for Population Tool with Zod validation
*
* Tests Zod schema validation and error messages
*/
import { describe, it, expect } from 'vitest';
import {
PopulationToolInputSchema,
POPULATION_TOOL_NAME,
getPopulationToolDefinition,
} from '../../src/application/tools/population-tool.js';
describe('Population Tool - Zod Validation', () => {
describe('PopulationToolInputSchema', () => {
it('should validate correct input', () => {
const validInput = { states: [1, 6, 36] };
const result = PopulationToolInputSchema.safeParse(validInput);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.states).toEqual([1, 6, 36]);
}
});
it('should validate all states query (0)', () => {
const validInput = { states: [0] };
const result = PopulationToolInputSchema.safeParse(validInput);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.states).toEqual([0]);
}
});
it('should reject empty states array', () => {
const invalidInput = { states: [] };
const result = PopulationToolInputSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.errors[0].message).toContain('at least 1');
}
});
it('should reject missing states field', () => {
const invalidInput = {};
const result = PopulationToolInputSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.errors[0].path).toContain('states');
}
});
it('should reject non-integer state codes', () => {
const invalidInput = { states: [1.5, 6] };
const result = PopulationToolInputSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.errors[0].message).toContain('integer');
}
});
it('should reject negative state codes', () => {
const invalidInput = { states: [-1] };
const result = PopulationToolInputSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.errors[0].message).toContain('greater than or equal to 0');
}
});
it('should reject state codes greater than 56', () => {
const invalidInput = { states: [57] };
const result = PopulationToolInputSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.errors[0].message).toContain('less than or equal to 56');
}
});
it('should reject non-array states', () => {
const invalidInput = { states: 'not-an-array' };
const result = PopulationToolInputSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.errors[0].code).toBe('invalid_type');
}
});
it('should reject string state codes', () => {
const invalidInput = { states: ['1', '6'] };
const result = PopulationToolInputSchema.safeParse(invalidInput);
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.errors[0].code).toBe('invalid_type');
}
});
it('should validate boundary values (0 and 56)', () => {
const validInput = { states: [0, 1, 56] };
const result = PopulationToolInputSchema.safeParse(validInput);
expect(result.success).toBe(true);
if (result.success) {
expect(result.data.states).toEqual([0, 1, 56]);
}
});
});
describe('getPopulationToolDefinition', () => {
it('should return tool definition with correct name', () => {
const definition = getPopulationToolDefinition();
expect(definition.name).toBe(POPULATION_TOOL_NAME);
expect(definition.name).toBe('get-population');
});
it('should generate JSON Schema from Zod', () => {
const definition = getPopulationToolDefinition();
const schema = definition.inputSchema as Record<string, unknown>;
expect(schema).toBeDefined();
// Check for definitions structure
expect(schema.$ref || schema.type).toBeDefined();
});
it('should have states property in schema definitions', () => {
const definition = getPopulationToolDefinition();
const schema = definition.inputSchema as Record<string, unknown>;
const definitions = schema.definitions as Record<string, Record<string, unknown>>;
if (definitions) {
const rootSchema = definitions.PopulationToolInput;
const properties = rootSchema?.properties as Record<string, unknown>;
expect(properties?.states).toBeDefined();
} else {
// Fallback for direct schema
const properties = schema.properties as Record<string, unknown>;
expect(properties?.states).toBeDefined();
}
});
it('should mark states as required in schema', () => {
const definition = getPopulationToolDefinition();
const schema = definition.inputSchema as Record<string, unknown>;
const definitions = schema.definitions as Record<string, Record<string, unknown>>;
if (definitions) {
const rootSchema = definitions.PopulationToolInput;
const required = rootSchema?.required as string[];
expect(required).toContain('states');
} else {
// Fallback for direct schema
const required = schema.required as string[];
expect(required).toContain('states');
}
});
it('should include description', () => {
const definition = getPopulationToolDefinition();
expect(definition.description).toBeTruthy();
expect(definition.description).toContain('FIPS state code');
});
it('should generate schema with array validation', () => {
const definition = getPopulationToolDefinition();
const schema = definition.inputSchema as Record<string, unknown>;
const definitions = schema.definitions as Record<string, Record<string, unknown>>;
if (definitions) {
const rootSchema = definitions.PopulationToolInput;
const properties = rootSchema?.properties as Record<string, unknown>;
const statesProperty = properties?.states as Record<string, unknown>;
expect(statesProperty?.type).toBe('array');
expect(statesProperty?.minItems).toBe(1);
}
});
it('should generate schema with number constraints', () => {
const definition = getPopulationToolDefinition();
const schema = definition.inputSchema as Record<string, unknown>;
const definitions = schema.definitions as Record<string, Record<string, unknown>>;
if (definitions) {
const rootSchema = definitions.PopulationToolInput;
const properties = rootSchema?.properties as Record<string, unknown>;
const statesProperty = properties?.states as Record<string, unknown>;
const items = statesProperty?.items as Record<string, unknown>;
expect(items?.type).toBe('integer');
expect(items?.minimum).toBe(0);
expect(items?.maximum).toBe(56);
}
});
});
});