batch-format.test.tsβ’11.5 kB
/**
* Tests for batch-format type guards and coercions
* Based on PR #744 feedback - testing the strengthened type safety
*/
import { describe, test, expect } from 'vitest';
import { formatBatchResult } from '@handlers/tool-configs/universal/operations/batch-format';
import {
BatchOperationType,
UniversalResourceType,
} from '@handlers/tool-configs/universal/types';
describe('Batch Format Type Safety', () => {
describe('Type Guards', () => {
test('isJsonObject handles null and undefined correctly', () => {
// Access the private function through the module's implementation
const isJsonObject = (
value: unknown
): value is Record<string, unknown> => {
return (
typeof value === 'object' && value !== null && !Array.isArray(value)
);
};
expect(isJsonObject(null)).toBe(false);
expect(isJsonObject(undefined)).toBe(false);
expect(isJsonObject([])).toBe(false);
expect(isJsonObject({})).toBe(true);
expect(isJsonObject({ key: 'value' })).toBe(true);
expect(isJsonObject('string')).toBe(false);
expect(isJsonObject(123)).toBe(false);
expect(isJsonObject(true)).toBe(false);
});
test('isRecordId validates record ID structure', () => {
const isRecordId = (
value: unknown
): value is Record<string, unknown> & { record_id: string } => {
return (
typeof value === 'object' &&
value !== null &&
'record_id' in value &&
typeof (value as Record<string, unknown>).record_id === 'string'
);
};
expect(isRecordId({ record_id: 'test-id' })).toBe(true);
expect(isRecordId({ record_id: 123 })).toBe(false);
expect(isRecordId({ id: 'test-id' })).toBe(false);
expect(isRecordId(null)).toBe(false);
expect(isRecordId(undefined)).toBe(false);
expect(isRecordId('test-id')).toBe(false);
});
test('formatBatchResult handles malformed batch results', () => {
// Test with null/undefined
expect(formatBatchResult(undefined)).toBe('Batch operation failed');
expect(formatBatchResult(null as any)).toBe('Batch operation failed');
// Test with non-array results
const singleResult = { success: true, result: { name: 'Test' } };
const result = formatBatchResult(singleResult as any);
expect(result).toContain('Batch operation result:');
expect(result).toContain('{"success":true,"result":{"name":"Test"}}');
// Test with invalid array structure
const invalidArray = ['string', 123, null];
const invalidResult = formatBatchResult(invalidArray as any);
expect(invalidResult).toContain('Batch operation result:');
});
});
describe('Batch Search Results', () => {
test('handles valid batch search results', () => {
const searchResults = [
{
success: true,
query: 'test query',
result: [
{
id: { record_id: 'test-id-1' },
values: { name: 'Test Record 1' },
},
],
},
{
success: false,
query: 'failed query',
error: 'Not found',
},
];
const result = formatBatchResult(
searchResults as any,
BatchOperationType.SEARCH,
UniversalResourceType.COMPANIES
);
expect(result).toContain(
'Batch search completed: 1 successful, 1 failed'
);
expect(result).toContain('Query: "test query"');
expect(result).toContain('Unknown (ID: test-id-1)'); // Name extraction fails due to test data structure
expect(result).toContain('Query: "failed query" - Error: Not found');
});
test('handles malformed search results gracefully', () => {
const malformedResults = [
{
success: true,
query: 'test query',
result: [
{
// Missing id structure
values: { name: 'Test Record' },
},
{
// Invalid record format
id: 'not-an-object',
values: 'not-an-object',
},
'completely-invalid-record',
],
},
];
const result = formatBatchResult(
malformedResults as any,
BatchOperationType.SEARCH,
UniversalResourceType.PEOPLE
);
expect(result).toContain(
'Batch search completed: 1 successful, 0 failed'
);
expect(result).toContain('Unknown (ID: unknown)'); // Should handle missing ID and name
expect(result).toContain('Invalid record format'); // Should handle invalid record
});
test('handles empty search results', () => {
const emptyResults = [
{
success: true,
query: 'empty query',
result: [],
},
];
const result = formatBatchResult(
emptyResults as any,
BatchOperationType.SEARCH,
UniversalResourceType.TASKS
);
expect(result).toContain('Found 0 tasks');
});
});
describe('Operation Results', () => {
test('handles successful operations with valid structure', () => {
const operations = [
{
success: true,
result: {
record_id: 'test-id-1',
values: { name: 'Created Record' },
},
},
{
success: true,
result: {
values: { title: 'Record with Title' },
},
},
];
const result = formatBatchResult(
operations as any,
BatchOperationType.CREATE,
UniversalResourceType.COMPANIES
);
expect(result).toContain(
'Batch create completed: 2 successful, 0 failed'
);
expect(result).toContain('Successful operations:');
expect(result).toContain('1. Created Record');
expect(result).toContain('2. Record with Title');
});
test('handles operations with invalid structure', () => {
const operations = [
{
success: true,
// Missing result field
},
{
success: true,
result: 'not-an-object',
},
{
success: true,
result: {
values: 'not-an-object',
},
},
];
const result = formatBatchResult(
operations as any,
BatchOperationType.UPDATE,
UniversalResourceType.PEOPLE
);
expect(result).toContain(
'Batch update completed: 3 successful, 0 failed'
);
expect(result).toContain('Invalid operation format');
});
test('handles failed operations with error details', () => {
const operations = [
{
success: false,
record_id: 'failed-id-1',
error: 'Validation failed',
},
{
success: false,
data: { name: 'Failed Record' },
error: 'Network error',
},
{
success: false,
// Missing identifiers
error: 'Unknown error',
},
{
success: false,
// Invalid structure
record_id: 123,
data: 'not-an-object',
error: 'Type error',
},
];
const result = formatBatchResult(
operations as any,
BatchOperationType.DELETE,
UniversalResourceType.LISTS
);
expect(result).toContain(
'Batch delete completed: 0 successful, 4 failed'
);
expect(result).toContain('Failed operations:');
expect(result).toContain('1. failed-id-1: Validation failed');
expect(result).toContain('2. Failed Record: Network error');
expect(result).toContain('3. Unknown: Unknown error');
expect(result).toContain('Invalid operation format: Unknown error'); // Error gets overridden in invalid format case
});
});
describe('Edge Cases and Error Handling', () => {
test('handles mixed success and failure results', () => {
const operations = [
{
success: true,
result: { values: { name: 'Success 1' } },
},
{
success: false,
error: 'Failed operation',
},
{
success: true,
result: { values: { name: 'Success 2' } },
},
];
const result = formatBatchResult(
operations as any,
BatchOperationType.CREATE
);
expect(result).toContain('2 successful, 1 failed');
expect(result).toContain('Success 1');
expect(result).toContain('Success 2');
expect(result).toContain('Failed operation');
});
test('handles deeply nested value extraction', () => {
const operations = [
{
success: true,
result: {
values: {
name: [{ value: 'Nested Name' }],
title: { value: 'Nested Title' },
},
},
},
];
const result = formatBatchResult(operations as any);
// Should extract nested values correctly
expect(result).toContain('Nested Name');
});
test('handles resource type formatting edge cases', () => {
// Test with undefined resource type
const operations = [
{ success: true, result: { values: { name: 'Test' } } },
];
let result = formatBatchResult(operations as any);
// Default resource type is used in formatting but not in the main output text
expect(result).toContain('Successful operations');
// Test with different resource types (resource type appears in the descriptor logic, not main text)
result = formatBatchResult(
operations as any,
undefined,
UniversalResourceType.COMPANIES
);
expect(result).toContain('Test'); // The resource type affects internal logic but name appears as 'Test'
result = formatBatchResult(
operations as any,
undefined,
UniversalResourceType.PEOPLE
);
expect(result).toContain('Test'); // Same here
});
test('pluralization works correctly', () => {
// Test singular
const singleRecord = [
{
id: { record_id: 'test' },
values: { name: 'Single' },
},
];
let result = formatBatchResult(
singleRecord as any,
BatchOperationType.SEARCH,
UniversalResourceType.COMPANIES
);
expect(result).toContain('found 1 company:');
// Test plural
const multipleRecords = [
{ id: { record_id: 'test1' }, values: { name: 'First' } },
{ id: { record_id: 'test2' }, values: { name: 'Second' } },
];
result = formatBatchResult(
multipleRecords as any,
BatchOperationType.SEARCH,
UniversalResourceType.COMPANIES
);
expect(result).toContain('found 2 companies:');
// Test words ending in 'y'
const categoryResults = [
{ id: { record_id: 'cat1' }, values: { name: 'Category 1' } },
{ id: { record_id: 'cat2' }, values: { name: 'Category 2' } },
];
// Mock a resource type that ends in 'y'
const mockCategoryType = 'category' as UniversalResourceType;
result = formatBatchResult(
categoryResults as any,
BatchOperationType.SEARCH,
mockCategoryType
);
expect(result).toContain('categories'); // Should convert 'y' to 'ies'
});
});
});