test: add vault-tools edge case tests
- Add test for list() skipping root folder (line 267) - Add test for list() normalizing aliases from string to array (line 325) - Add test for list() handling array aliases (line 325) - Add test for getFolderMetadata() handling folder with mtime (line 374) - Add test for getFolderMetadata() handling folder without mtime - Add test for list() on non-root path (line 200) - Add test for search() stopping at maxResults=1 on file boundary (line 608) - Add test for search() stopping at maxResults=1 within file (line 620) - Add test for search() adjusting snippet for long lines (line 650) Coverage improved from 95.66% to 98.19% for vault-tools.ts
This commit is contained in:
@@ -387,7 +387,7 @@ describe('VaultTools', () => {
|
||||
expect(parsed.items[0].frontmatterSummary.tags).toEqual(['single-tag']);
|
||||
});
|
||||
|
||||
it('should handle string aliases and convert to array', async () => {
|
||||
it('should normalize aliases from string to array in list()', async () => {
|
||||
const mockFile = createMockTFile('test.md');
|
||||
const mockRoot = createMockTFolder('', [mockFile]);
|
||||
const mockCache = {
|
||||
@@ -406,6 +406,25 @@ describe('VaultTools', () => {
|
||||
expect(parsed.items[0].frontmatterSummary.aliases).toEqual(['single-alias']);
|
||||
});
|
||||
|
||||
it('should handle array aliases in list()', async () => {
|
||||
const mockFile = createMockTFile('test.md');
|
||||
const mockRoot = createMockTFolder('', [mockFile]);
|
||||
const mockCache = {
|
||||
frontmatter: {
|
||||
aliases: ['alias1', 'alias2']
|
||||
}
|
||||
};
|
||||
|
||||
mockVault.getRoot = jest.fn().mockReturnValue(mockRoot);
|
||||
mockMetadata.getFileCache = jest.fn().mockReturnValue(mockCache);
|
||||
|
||||
const result = await vaultTools.list({ withFrontmatterSummary: true });
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
const parsed = JSON.parse(result.content[0].text);
|
||||
expect(parsed.items[0].frontmatterSummary.aliases).toEqual(['alias1', 'alias2']);
|
||||
});
|
||||
|
||||
it('should handle frontmatter extraction error gracefully', async () => {
|
||||
const mockFile = createMockTFile('test.md');
|
||||
const mockRoot = createMockTFolder('', [mockFile]);
|
||||
@@ -1097,6 +1116,24 @@ describe('VaultTools', () => {
|
||||
});
|
||||
|
||||
describe('list - edge cases', () => {
|
||||
it('should skip root folder in list() when iterating children', async () => {
|
||||
// Create a root folder that appears as a child (edge case)
|
||||
const rootChild = createMockTFolder('');
|
||||
(rootChild as any).isRoot = jest.fn().mockReturnValue(true);
|
||||
const normalFile = createMockTFile('test.md');
|
||||
const mockRoot = createMockTFolder('', [rootChild, normalFile]);
|
||||
|
||||
mockVault.getRoot = jest.fn().mockReturnValue(mockRoot);
|
||||
|
||||
const result = await vaultTools.list({});
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
const parsed = JSON.parse(result.content[0].text);
|
||||
// Should only include the normal file, not the root child
|
||||
expect(parsed.items.length).toBe(1);
|
||||
expect(parsed.items[0].path).toBe('test.md');
|
||||
});
|
||||
|
||||
it('should handle invalid path in list', async () => {
|
||||
const result = await vaultTools.list({ path: '../invalid' });
|
||||
|
||||
@@ -1165,5 +1202,105 @@ describe('VaultTools', () => {
|
||||
// Should return from beginning when cursor not found
|
||||
expect(parsed.items.length).toBeGreaterThan(0);
|
||||
});
|
||||
|
||||
it('should handle folder without mtime in getFolderMetadata', async () => {
|
||||
// Create a folder without stat property
|
||||
const mockFolder = createMockTFolder('test-folder');
|
||||
delete (mockFolder as any).stat;
|
||||
|
||||
const mockRoot = createMockTFolder('', [mockFolder]);
|
||||
mockVault.getRoot = jest.fn().mockReturnValue(mockRoot);
|
||||
|
||||
const result = await vaultTools.list({});
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
const parsed = JSON.parse(result.content[0].text);
|
||||
expect(parsed.items[0].kind).toBe('directory');
|
||||
// Modified time should be 0 when stat is not available
|
||||
expect(parsed.items[0].modified).toBe(0);
|
||||
});
|
||||
|
||||
it('should handle folder with mtime in getFolderMetadata', async () => {
|
||||
// Create a folder WITH stat property containing mtime
|
||||
const mockFolder = createMockTFolder('test-folder');
|
||||
(mockFolder as any).stat = { mtime: 12345 };
|
||||
|
||||
const mockRoot = createMockTFolder('', [mockFolder]);
|
||||
mockVault.getRoot = jest.fn().mockReturnValue(mockRoot);
|
||||
|
||||
const result = await vaultTools.list({});
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
const parsed = JSON.parse(result.content[0].text);
|
||||
expect(parsed.items[0].kind).toBe('directory');
|
||||
// Modified time should be set from stat.mtime
|
||||
expect(parsed.items[0].modified).toBe(12345);
|
||||
});
|
||||
|
||||
it('should handle list on non-root path', async () => {
|
||||
const mockFolder = createMockTFolder('subfolder', [
|
||||
createMockTFile('subfolder/test.md')
|
||||
]);
|
||||
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(mockFolder);
|
||||
|
||||
const result = await vaultTools.list({ path: 'subfolder' });
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
const parsed = JSON.parse(result.content[0].text);
|
||||
expect(parsed.items.length).toBe(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('search - maxResults edge cases', () => {
|
||||
it('should stop at maxResults=1 when limit reached on file boundary', async () => {
|
||||
const mockFile1 = createMockTFile('file1.md');
|
||||
const mockFile2 = createMockTFile('file2.md');
|
||||
mockVault.getMarkdownFiles = jest.fn().mockReturnValue([mockFile1, mockFile2]);
|
||||
mockVault.read = jest.fn()
|
||||
.mockResolvedValueOnce('first match here')
|
||||
.mockResolvedValueOnce('second match here');
|
||||
|
||||
const result = await vaultTools.search({ query: 'match', maxResults: 1 });
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
const parsed = JSON.parse(result.content[0].text);
|
||||
// Should stop after first match
|
||||
expect(parsed.totalMatches).toBe(1);
|
||||
expect(parsed.filesSearched).toBe(1);
|
||||
});
|
||||
|
||||
it('should stop at maxResults=1 when limit reached within file', async () => {
|
||||
const mockFile = createMockTFile('test.md');
|
||||
mockVault.getMarkdownFiles = jest.fn().mockReturnValue([mockFile]);
|
||||
mockVault.read = jest.fn().mockResolvedValue('match on line 1\nmatch on line 2\nmatch on line 3');
|
||||
|
||||
const result = await vaultTools.search({ query: 'match', maxResults: 1 });
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
const parsed = JSON.parse(result.content[0].text);
|
||||
// Should stop after first match within the file
|
||||
expect(parsed.totalMatches).toBe(1);
|
||||
});
|
||||
|
||||
it('should adjust snippet for long lines at end of line', async () => {
|
||||
const mockFile = createMockTFile('test.md');
|
||||
// Create a very long line with the target at the end
|
||||
const longLine = 'a'.repeat(500) + 'target';
|
||||
mockVault.getMarkdownFiles = jest.fn().mockReturnValue([mockFile]);
|
||||
mockVault.read = jest.fn().mockResolvedValue(longLine);
|
||||
|
||||
const result = await vaultTools.search({
|
||||
query: 'target',
|
||||
returnSnippets: true,
|
||||
snippetLength: 100
|
||||
});
|
||||
|
||||
expect(result.isError).toBeUndefined();
|
||||
const parsed = JSON.parse(result.content[0].text);
|
||||
expect(parsed.matches[0].snippet.length).toBeLessThanOrEqual(100);
|
||||
// Snippet should be adjusted to show the end of the line
|
||||
expect(parsed.matches[0].snippet).toContain('target');
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user