From aca4d359440ab28472ad4a51ae78bfbba2cd4f24 Mon Sep 17 00:00:00 2001 From: Bill Date: Sun, 19 Oct 2025 23:48:36 -0400 Subject: [PATCH] test: add coverage for VaultTools uncovered paths Add tests for: - getBacklinks with snippet options (includeSnippets true/false) - getBacklinks error handling (file not found, read errors) - validateWikilinks error paths (file not found, read errors) - validateWikilinks successful validation with resolved/unresolved links Improves vault-tools.ts coverage from 35.85% to 54.9% statements. Adds 7 new tests (27 to 34 total). --- tests/vault-tools.test.ts | 123 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 123 insertions(+) diff --git a/tests/vault-tools.test.ts b/tests/vault-tools.test.ts index 1b3805a..d20ae2a 100644 --- a/tests/vault-tools.test.ts +++ b/tests/vault-tools.test.ts @@ -455,4 +455,127 @@ describe('VaultTools', () => { expect(parsed.items[0].frontmatterSummary).toBeUndefined(); }); }); + + describe('getBacklinks', () => { + it('should return error if file not found', async () => { + mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(null); + + const result = await vaultTools.getBacklinks('nonexistent.md'); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('not found'); + }); + + it('should return backlinks with snippets when includeSnippets is true', async () => { + const targetFile = createMockTFile('target.md'); + const sourceFile = createMockTFile('source.md'); + + mockVault.getAbstractFileByPath = jest.fn() + .mockReturnValueOnce(targetFile) + .mockReturnValue(sourceFile); + mockVault.read = jest.fn().mockResolvedValue('This links to [[target]]'); + mockMetadata.resolvedLinks = { + 'source.md': { + 'target.md': 1 + } + }; + mockMetadata.getFirstLinkpathDest = jest.fn().mockReturnValue(targetFile); + + const result = await vaultTools.getBacklinks('target.md', false, true); + + expect(result.isError).toBeUndefined(); + const parsed = JSON.parse(result.content[0].text); + expect(parsed.backlinks).toBeDefined(); + expect(parsed.backlinks.length).toBeGreaterThan(0); + expect(parsed.backlinks[0].occurrences[0].snippet).toBeTruthy(); + }); + + it('should return backlinks without snippets when includeSnippets is false', async () => { + const targetFile = createMockTFile('target.md'); + const sourceFile = createMockTFile('source.md'); + + mockVault.getAbstractFileByPath = jest.fn() + .mockReturnValueOnce(targetFile) + .mockReturnValue(sourceFile); + mockVault.read = jest.fn().mockResolvedValue('This links to [[target]]'); + mockMetadata.resolvedLinks = { + 'source.md': { + 'target.md': 1 + } + }; + mockMetadata.getFirstLinkpathDest = jest.fn().mockReturnValue(targetFile); + + const result = await vaultTools.getBacklinks('target.md', false, false); + + expect(result.isError).toBeUndefined(); + const parsed = JSON.parse(result.content[0].text); + expect(parsed.backlinks).toBeDefined(); + expect(parsed.backlinks.length).toBeGreaterThan(0); + expect(parsed.backlinks[0].occurrences[0].snippet).toBe(''); + }); + + it('should handle read errors gracefully', async () => { + const targetFile = createMockTFile('target.md'); + const sourceFile = createMockTFile('source.md'); + + mockVault.getAbstractFileByPath = jest.fn() + .mockReturnValueOnce(targetFile) + .mockReturnValue(sourceFile); + mockVault.read = jest.fn().mockRejectedValue(new Error('Permission denied')); + mockMetadata.resolvedLinks = { + 'source.md': { + 'target.md': 1 + } + }; + + const result = await vaultTools.getBacklinks('target.md'); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('error'); + }); + }); + + describe('validateWikilinks', () => { + it('should return error if file not found', async () => { + mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(null); + + const result = await vaultTools.validateWikilinks('nonexistent.md'); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('not found'); + }); + + it('should handle read errors gracefully', async () => { + const mockFile = createMockTFile('test.md'); + + mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(mockFile); + mockVault.read = jest.fn().mockRejectedValue(new Error('Read error')); + + const result = await vaultTools.validateWikilinks('test.md'); + + expect(result.isError).toBe(true); + expect(result.content[0].text).toContain('error'); + }); + + it('should validate wikilinks successfully', async () => { + const mockFile = createMockTFile('test.md'); + const linkedFile = createMockTFile('linked.md'); + + mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(mockFile); + mockVault.read = jest.fn().mockResolvedValue('This is a [[linked]] note and a [[broken]] link.'); + mockVault.getMarkdownFiles = jest.fn().mockReturnValue([linkedFile]); + mockMetadata.getFirstLinkpathDest = jest.fn() + .mockReturnValueOnce(linkedFile) + .mockReturnValueOnce(null); + + const result = await vaultTools.validateWikilinks('test.md'); + + expect(result.isError).toBeUndefined(); + const parsed = JSON.parse(result.content[0].text); + expect(parsed.path).toBe('test.md'); + expect(parsed.totalLinks).toBe(2); + expect(parsed.resolvedLinks.length).toBe(1); + expect(parsed.unresolvedLinks.length).toBe(1); + }); + }); }); \ No newline at end of file