Skip to main content
Glama

mcp-structured-memory

updateSection.test.ts18.3 kB
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest' import { updateSectionTool } from './updateSection.js' import { StorageManager } from '../storage/StorageManager.js' import { Memory } from '../types/memory.js' // Mock the StorageManager vi.mock('../storage/StorageManager.js', () => { return { StorageManager: vi.fn().mockImplementation(() => ({ readMemory: vi.fn(), findSection: vi.fn(), updateSection: vi.fn() })) } }) describe('updateSection Tool', () => { let storageManager: StorageManager let mockMemory: Memory beforeEach(() => { storageManager = new StorageManager() mockMemory = { metadata: { id: 'test-memory', created: '2025-07-30T12:00:00Z', updated: '2025-07-31T10:00:00Z', status: 'active', tags: ['work'] }, content: `# Test Memory ## Existing Section This is existing content. ## Another Section More content here.`, filePath: '/test/path/test-memory.md' } vi.mocked(storageManager.readMemory).mockResolvedValue(mockMemory) vi.mocked(storageManager.updateSection).mockResolvedValue(undefined) }) afterEach(() => { vi.clearAllMocks() }) describe('Input Validation', () => { it('should throw error when memory_id is missing', async () => { await expect( updateSectionTool(storageManager, { section: 'test', content: 'content' }) ).rejects.toThrow('memory_id, section, and content are required') }) it('should throw error when section is missing', async () => { await expect( updateSectionTool(storageManager, { memory_id: 'test', content: 'content' }) ).rejects.toThrow('memory_id, section, and content are required') }) it('should throw error when content is missing', async () => { await expect( updateSectionTool(storageManager, { memory_id: 'test', section: 'test' }) ).rejects.toThrow('memory_id, section, and content are required') }) it('should allow empty string content', async () => { vi.mocked(storageManager.findSection).mockReturnValue({ name: 'Existing Section', content: 'existing content', level: 2 }) await updateSectionTool(storageManager, { memory_id: 'test', section: 'Existing Section', content: '' }) expect(storageManager.updateSection).toHaveBeenCalledWith( 'test', 'Existing Section', '', 'append' ) }) it('should throw error when memory document not found', async () => { vi.mocked(storageManager.readMemory).mockResolvedValue(null) await expect( updateSectionTool(storageManager, { memory_id: 'nonexistent', section: 'test', content: 'content' }) ).rejects.toThrow("Memory document 'nonexistent' not found") }) it('should validate mode parameter', async () => { vi.mocked(storageManager.findSection).mockReturnValue({ name: 'Test Section', content: 'existing content', level: 2 }) await expect( updateSectionTool(storageManager, { memory_id: 'test', section: 'Test Section', content: 'content', mode: 'invalid' as any }) ).rejects.toThrow('mode must be either "append" or "replace"') }) }) describe('Mode Handling', () => { beforeEach(() => { vi.mocked(storageManager.findSection).mockReturnValue({ name: 'Test Section', content: 'existing content', level: 2 }) }) it('should default to append mode when mode not specified', async () => { await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: 'new content' }) expect(storageManager.updateSection).toHaveBeenCalledWith( 'test-memory', 'Test Section', 'new content', 'append' ) }) it('should use append mode when explicitly specified', async () => { await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: 'new content', mode: 'append' }) expect(storageManager.updateSection).toHaveBeenCalledWith( 'test-memory', 'Test Section', 'new content', 'append' ) }) it('should use replace mode when specified', async () => { await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: 'replacement content', mode: 'replace' }) expect(storageManager.updateSection).toHaveBeenCalledWith( 'test-memory', 'Test Section', 'replacement content', 'replace' ) }) }) describe('Section Existence Detection', () => { it('should detect existing section', async () => { vi.mocked(storageManager.findSection).mockReturnValue({ name: 'Existing Section', content: 'current content', level: 2 }) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Existing Section', content: 'new content' }) expect(result.content[0].text).toContain('Appended content to section "Existing Section"') }) it('should detect when section does not exist', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'New Section', content: 'new content' }) expect(result.content[0].text).toContain('Created new section "New Section" with content') }) it('should handle section name case sensitivity', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'EXISTING SECTION', content: 'content' }) expect(storageManager.findSection).toHaveBeenCalledWith( mockMemory.content, 'EXISTING SECTION' ) }) }) describe('Action Text Generation', () => { it('should generate correct action text for new section creation', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Brand New Section', content: 'some content' }) expect(result.content[0].text).toContain('**Action**: Created new section "Brand New Section" with content') }) it('should generate correct action text for appending to existing section', async () => { vi.mocked(storageManager.findSection).mockReturnValue({ name: 'Existing Section', content: 'old content', level: 2 }) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Existing Section', content: 'additional content' }) expect(result.content[0].text).toContain('**Action**: Appended content to section "Existing Section"') }) it('should generate correct action text for replacing section content', async () => { vi.mocked(storageManager.findSection).mockReturnValue({ name: 'Existing Section', content: 'old content', level: 2 }) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Existing Section', content: 'replacement content', mode: 'replace' }) expect(result.content[0].text).toContain('**Action**: Replaced content in section "Existing Section"') }) }) describe('Content Statistics', () => { it('should count words correctly for simple content', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: 'This is five words exactly' }) expect(result.content[0].text).toContain('**Content Added**: 5 words, 1 lines') }) it('should count words correctly for multi-line content', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const content = `Line one has four words Line two has four words Line three has four words` const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: content }) expect(result.content[0].text).toContain('**Content Added**: 15 words, 3 lines') }) it('should handle empty content correctly', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: '' }) expect(result.content[0].text).toContain('**Content Added**: 0 words, 1 lines') }) it('should handle content with only whitespace', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: ' \n \n ' }) expect(result.content[0].text).toContain('**Content Added**: 0 words, 3 lines') }) it('should filter out empty words when counting', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: 'word1 word2 word3' }) expect(result.content[0].text).toContain('**Content Added**: 3 words, 1 lines') }) it('should count lines correctly for content with trailing newlines', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: 'Line 1\nLine 2\n' }) expect(result.content[0].text).toContain('**Content Added**: 4 words, 3 lines') }) }) describe('Response Format', () => { it('should include all required response fields', async () => { vi.mocked(storageManager.findSection).mockReturnValue({ name: 'Test Section', content: 'existing', level: 2 }) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: 'New content here', mode: 'replace' }) expect(result.content[0].text).toContain("Successfully updated memory document 'test-memory':") expect(result.content[0].text).toContain('**Action**: Replaced content in section "Test Section"') expect(result.content[0].text).toContain('**Content Added**: 3 words, 1 lines') expect(result.content[0].text).toContain('**Mode**: replace') expect(result.content[0].text).toContain('The section has been updated') expect(result.content[0].text).toContain('You can view it using the get_section tool') }) it('should have correct response structure', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'New Section', content: 'content' }) expect(result).toEqual({ content: [{ type: 'text', text: expect.stringContaining('Successfully updated memory document') }] }) }) it('should include usage hints in response', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test', content: 'content' }) expect(result.content[0].text).toContain('get_section tool') expect(result.content[0].text).toContain('get_memory_summary') }) }) describe('Storage Manager Integration', () => { it('should call readMemory with correct memory_id', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) await updateSectionTool(storageManager, { memory_id: 'specific-memory-id', section: 'Test Section', content: 'content' }) expect(storageManager.readMemory).toHaveBeenCalledWith('specific-memory-id') }) it('should call findSection with correct parameters', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Target Section', content: 'content' }) expect(storageManager.findSection).toHaveBeenCalledWith( mockMemory.content, 'Target Section' ) }) it('should call updateSection with correct parameters', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Target Section', content: 'new content', mode: 'replace' }) expect(storageManager.updateSection).toHaveBeenCalledWith( 'test-memory', 'Target Section', 'new content', 'replace' ) }) it('should handle storage manager read errors', async () => { vi.mocked(storageManager.readMemory).mockRejectedValue(new Error('Read failed')) await expect( updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'section', content: 'content' }) ).rejects.toThrow('Read failed') }) it('should handle storage manager update errors', async () => { vi.mocked(storageManager.findSection).mockReturnValue(null) vi.mocked(storageManager.updateSection).mockRejectedValue(new Error('Update failed')) await expect( updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'section', content: 'content' }) ).rejects.toThrow('Update failed') }) }) describe('Edge Cases', () => { it('should handle very long section names', async () => { const longSectionName = 'A'.repeat(200) vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: longSectionName, content: 'content' }) expect(storageManager.findSection).toHaveBeenCalledWith( mockMemory.content, longSectionName ) expect(result.content[0].text).toContain(`Created new section "${longSectionName}" with content`) }) it('should handle very long content', async () => { const longContent = 'word '.repeat(10000).trim() vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: longContent }) expect(storageManager.updateSection).toHaveBeenCalledWith( 'test-memory', 'Test Section', longContent, 'append' ) expect(result.content[0].text).toContain('**Content Added**: 10000 words, 1 lines') }) it('should handle special characters in section names', async () => { const specialSectionName = 'Section: #1 (API & Database) - Notes!' vi.mocked(storageManager.findSection).mockReturnValue(null) await updateSectionTool(storageManager, { memory_id: 'test-memory', section: specialSectionName, content: 'content' }) expect(storageManager.findSection).toHaveBeenCalledWith( mockMemory.content, specialSectionName ) }) it('should handle content with markdown formatting', async () => { const markdownContent = `# This is a heading **Bold text** and *italic text* - List item 1 - List item 2 > Blockquote \`\`\`javascript console.log('code block'); \`\`\`` vi.mocked(storageManager.findSection).mockReturnValue(null) await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Markdown Section', content: markdownContent }) expect(storageManager.updateSection).toHaveBeenCalledWith( 'test-memory', 'Markdown Section', markdownContent, 'append' ) }) it('should handle content with unicode characters', async () => { const unicodeContent = 'Café ☕ Notes 🚀 with émojis and ñ characters' vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Unicode Section', content: unicodeContent }) expect(storageManager.updateSection).toHaveBeenCalledWith( 'test-memory', 'Unicode Section', unicodeContent, 'append' ) expect(result.content[0].text).toContain('**Content Added**: 9 words, 1 lines') }) it('should handle section names with leading/trailing whitespace', async () => { const sectionName = ' Test Section ' vi.mocked(storageManager.findSection).mockReturnValue(null) await updateSectionTool(storageManager, { memory_id: 'test-memory', section: sectionName, content: 'content' }) expect(storageManager.findSection).toHaveBeenCalledWith( mockMemory.content, sectionName ) }) it('should handle content with only newlines', async () => { const newlineContent = '\n\n\n\n\n' vi.mocked(storageManager.findSection).mockReturnValue(null) const result = await updateSectionTool(storageManager, { memory_id: 'test-memory', section: 'Test Section', content: newlineContent }) expect(result.content[0].text).toContain('**Content Added**: 0 words, 6 lines') }) }) })

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/nmeierpolys/mcp-structured-memory'

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