Skip to main content
Glama

mcp-gsheets

formatters.test.ts13.6 kB
import { describe, it, expect } from 'vitest'; import { formatSuccessResponse, formatValuesResponse, formatBatchValuesResponse, formatSpreadsheetMetadata, formatUpdateResponse, formatAppendResponse, formatClearResponse, formatSpreadsheetCreatedResponse, formatSheetOperationResponse, formatToolResponse, } from '../../../src/utils/formatters'; import { testValues } from '../../fixtures/test-data'; describe('formatSuccessResponse', () => { it('should format data without message', () => { const data = { key: 'value', number: 123 }; const result = formatSuccessResponse(data); expect(result.content).toHaveLength(1); expect(result.content[0].type).toBe('text'); expect(result.content[0].text).toBe(JSON.stringify(data, null, 2)); }); it('should format data with message', () => { const data = { key: 'value' }; const message = 'Operation successful'; const result = formatSuccessResponse(data, message); expect(result.content[0].text).toContain(message); expect(result.content[0].text).toContain(JSON.stringify(data, null, 2)); }); it('should handle null data', () => { const result = formatSuccessResponse(null); expect(result.content[0].text).toBe('null'); }); it('should handle undefined data', () => { const result = formatSuccessResponse(undefined); expect(result.content[0].text).toBe(JSON.stringify(undefined, null, 2)); }); it('should handle complex nested objects', () => { const complexData = { level1: { level2: { array: [1, 2, 3], object: { a: 'b' }, }, }, }; const result = formatSuccessResponse(complexData); expect(result.content[0].text).toContain('"level1"'); expect(result.content[0].text).toContain('"level2"'); }); }); describe('formatValuesResponse', () => { it('should format values with range', () => { const values = testValues.simple; const range = 'Sheet1!A1:C3'; const result = formatValuesResponse(values, range); expect(result.content[0].type).toBe('text'); const parsed = JSON.parse(result.content[0].text!); expect(parsed.range).toBe(range); expect(parsed.rowCount).toBe(3); expect(parsed.columnCount).toBe(3); expect(parsed.values).toEqual(values); }); it('should format values without range', () => { const values = [['A1', 'B1']]; const result = formatValuesResponse(values); const parsed = JSON.parse(result.content[0].text!); expect(parsed.range).toBeUndefined(); expect(parsed.rowCount).toBe(1); expect(parsed.columnCount).toBe(2); }); it('should handle empty values array', () => { const result = formatValuesResponse([]); expect(result.content[0].text).toBe('No data found'); }); it('should handle empty values array with range', () => { const result = formatValuesResponse([], 'Sheet1!A1:B10'); expect(result.content[0].text).toBe('No data found in range: Sheet1!A1:B10'); }); it('should handle null values', () => { const result = formatValuesResponse(null as any); expect(result.content[0].text).toBe('No data found'); }); it('should handle undefined values', () => { const result = formatValuesResponse(undefined as any); expect(result.content[0].text).toBe('No data found'); }); it('should handle jagged arrays', () => { const values = [ ['A1', 'B1', 'C1'], ['A2', 'B2'], // shorter row ['A3'], // even shorter ]; const result = formatValuesResponse(values); const parsed = JSON.parse(result.content[0].text!); expect(parsed.rowCount).toBe(3); expect(parsed.columnCount).toBe(3); // based on first row }); it('should handle empty first row', () => { const values = [[], ['A2', 'B2']]; const result = formatValuesResponse(values); const parsed = JSON.parse(result.content[0].text!); expect(parsed.columnCount).toBe(0); }); }); describe('formatBatchValuesResponse', () => { it('should format multiple value ranges', () => { const valueRanges = [ { range: 'Sheet1!A1:B2', values: [['A1', 'B1'], ['A2', 'B2']], }, { range: 'Sheet2!C1:D2', values: [['C1', 'D1'], ['C2', 'D2']], }, ]; const result = formatBatchValuesResponse(valueRanges); const parsed = JSON.parse(result.content[0].text!); expect(parsed.totalRanges).toBe(2); expect(parsed.valueRanges).toHaveLength(2); expect(parsed.valueRanges[0].range).toBe('Sheet1!A1:B2'); expect(parsed.valueRanges[0].rowCount).toBe(2); expect(parsed.valueRanges[0].columnCount).toBe(2); }); it('should handle value ranges without values', () => { const valueRanges = [ { range: 'Sheet1!A1:B2', values: null, }, { range: 'Sheet2!C1:D2', // no values property }, ]; const result = formatBatchValuesResponse(valueRanges); const parsed = JSON.parse(result.content[0].text!); expect(parsed.valueRanges[0].rowCount).toBe(0); expect(parsed.valueRanges[0].values).toEqual([]); expect(parsed.valueRanges[1].rowCount).toBe(0); expect(parsed.valueRanges[1].values).toEqual([]); }); it('should handle empty valueRanges array', () => { const result = formatBatchValuesResponse([]); const parsed = JSON.parse(result.content[0].text!); expect(parsed.totalRanges).toBe(0); expect(parsed.valueRanges).toEqual([]); }); }); describe('formatSpreadsheetMetadata', () => { it('should format complete metadata', () => { const metadata = { spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms', properties: { title: 'Test Spreadsheet', locale: 'en_US', timeZone: 'America/New_York', }, sheets: [ { properties: { sheetId: 0, title: 'Sheet1', index: 0, gridProperties: { rowCount: 1000, columnCount: 26, }, tabColor: { red: 1.0, green: 0.5, blue: 0.0, }, }, }, ], }; const result = formatSpreadsheetMetadata(metadata); const parsed = JSON.parse(result.content[0].text!); expect(parsed.spreadsheetId).toBe(metadata.spreadsheetId); expect(parsed.title).toBe('Test Spreadsheet'); expect(parsed.locale).toBe('en_US'); expect(parsed.timeZone).toBe('America/New_York'); expect(parsed.sheets).toHaveLength(1); expect(parsed.sheets[0].sheetId).toBe(0); expect(parsed.sheets[0].rowCount).toBe(1000); expect(parsed.sheets[0].columnCount).toBe(26); }); it('should handle metadata without properties', () => { const metadata = { spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms', }; const result = formatSpreadsheetMetadata(metadata); const parsed = JSON.parse(result.content[0].text!); expect(parsed.title).toBeUndefined(); expect(parsed.locale).toBeUndefined(); expect(parsed.sheets).toBeUndefined(); }); it('should handle sheets without properties', () => { const metadata = { spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms', sheets: [ {}, // no properties { properties: {} }, // empty properties ], }; const result = formatSpreadsheetMetadata(metadata); const parsed = JSON.parse(result.content[0].text!); expect(parsed.sheets).toHaveLength(2); expect(parsed.sheets[0].sheetId).toBeUndefined(); expect(parsed.sheets[1].sheetId).toBeUndefined(); }); it('should handle null/undefined values gracefully', () => { const metadata = { spreadsheetId: '1BxiMVs0XRA5nFMdKvBdBZjgmUUqptlbs74OgvE2upms', properties: null, sheets: null, }; const result = formatSpreadsheetMetadata(metadata); const parsed = JSON.parse(result.content[0].text!); expect(parsed.spreadsheetId).toBe(metadata.spreadsheetId); expect(parsed.title).toBeUndefined(); expect(parsed.sheets).toBeUndefined(); }); }); describe('formatUpdateResponse', () => { it('should format with range', () => { const result = formatUpdateResponse(9, 'Sheet1!A1:C3'); expect(result.content[0].text).toBe('Successfully updated 9 cells in range: Sheet1!A1:C3'); }); it('should format without range', () => { const result = formatUpdateResponse(5); expect(result.content[0].text).toBe('Successfully updated 5 cells'); }); it('should handle zero cells', () => { const result = formatUpdateResponse(0); expect(result.content[0].text).toBe('Successfully updated 0 cells'); }); it('should handle large numbers', () => { const result = formatUpdateResponse(1000000); expect(result.content[0].text).toBe('Successfully updated 1000000 cells'); }); }); describe('formatAppendResponse', () => { it('should format append response', () => { const updates = { updatedCells: 9, updatedRange: 'Sheet1!A11:C13', }; const result = formatAppendResponse(updates); expect(result.content[0].text).toBe('Successfully appended 9 cells to range: Sheet1!A11:C13'); }); it('should handle missing updatedCells', () => { const updates = { updatedRange: 'Sheet1!A11:C13', }; const result = formatAppendResponse(updates); expect(result.content[0].text).toBe('Successfully appended 0 cells to range: Sheet1!A11:C13'); }); it('should handle null updates object properties', () => { const updates = { updatedCells: null, updatedRange: 'Sheet1!A1', }; const result = formatAppendResponse(updates); expect(result.content[0].text).toBe('Successfully appended 0 cells to range: Sheet1!A1'); }); }); describe('formatClearResponse', () => { it('should format clear response', () => { const result = formatClearResponse('Sheet1!A1:C3'); expect(result.content[0].text).toBe('Successfully cleared range: Sheet1!A1:C3'); }); it('should handle complex range', () => { const result = formatClearResponse("'My Sheet'!A:Z"); expect(result.content[0].text).toBe("Successfully cleared range: 'My Sheet'!A:Z"); }); }); describe('formatSpreadsheetCreatedResponse', () => { it('should format with all properties', () => { const spreadsheet = { spreadsheetId: 'new-id-123', spreadsheetUrl: 'https://docs.google.com/spreadsheets/d/new-id-123/edit', properties: { title: 'New Spreadsheet', }, }; const result = formatSpreadsheetCreatedResponse(spreadsheet); expect(result.content[0].text).toContain('Spreadsheet created successfully'); const parsed = JSON.parse(result.content[0].text!.split('\n\n')[1]); expect(parsed.spreadsheetId).toBe('new-id-123'); expect(parsed.spreadsheetUrl).toBe('https://docs.google.com/spreadsheets/d/new-id-123/edit'); expect(parsed.title).toBe('New Spreadsheet'); }); it('should handle missing properties', () => { const spreadsheet = { spreadsheetId: 'new-id-123', spreadsheetUrl: 'https://docs.google.com/spreadsheets/d/new-id-123/edit', }; const result = formatSpreadsheetCreatedResponse(spreadsheet); const parsed = JSON.parse(result.content[0].text!.split('\n\n')[1]); expect(parsed.title).toBeUndefined(); }); }); describe('formatSheetOperationResponse', () => { it('should format operation without details', () => { const result = formatSheetOperationResponse('Sheet deleted'); expect(result.content[0].text).toBe('Sheet deleted completed successfully'); }); it('should format operation with details', () => { const details = { sheetId: 123, title: 'Deleted Sheet' }; const result = formatSheetOperationResponse('Sheet deleted', details); expect(result.content[0].text).toContain('Sheet deleted completed successfully'); expect(result.content[0].text).toContain(JSON.stringify(details, null, 2)); }); it('should handle complex details object', () => { const details = { operation: 'duplicate', source: { sheetId: 1, title: 'Original' }, destination: { sheetId: 2, title: 'Copy' }, timestamp: new Date().toISOString(), }; const result = formatSheetOperationResponse('Sheet duplicated', details); expect(result.content[0].text).toContain('Sheet duplicated completed successfully'); expect(result.content[0].text).toContain('"operation": "duplicate"'); }); it('should handle null details', () => { const result = formatSheetOperationResponse('Operation completed', null); expect(result.content[0].text).toBe('Operation completed completed successfully'); }); }); describe('formatToolResponse', () => { it('should format message only', () => { const result = formatToolResponse('Simple message'); expect(result.content[0].text).toBe('Simple message'); }); it('should format message with data', () => { const data = { count: 10, status: 'complete' }; const result = formatToolResponse('Operation finished', data); expect(result.content[0].text).toContain('Operation finished'); expect(result.content[0].text).toContain(JSON.stringify(data, null, 2)); }); it('should handle empty message', () => { const result = formatToolResponse(''); expect(result.content[0].text).toBe(''); }); it('should handle undefined data', () => { const result = formatToolResponse('Message', undefined); expect(result.content[0].text).toBe('Message'); }); });

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/freema/mcp-gsheets'

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