refactor: update VaultTools to pass adapters to utils
Updated VaultTools to use adapters for all utility method calls: - SearchUtils.searchWaypoints() now receives vault adapter - WaypointUtils.isFolderNote() now receives vault adapter - LinkUtils.validateWikilinks() now receives vault and metadata adapters - LinkUtils.resolveLink() now receives vault and metadata adapters - LinkUtils.getBacklinks() now receives vault and metadata adapters Removed App dependency from VaultTools constructor - now only requires vault and metadata adapters. Updated factory and all test files accordingly. All tests passing (336/336).
This commit is contained in:
@@ -1,24 +1,18 @@
|
||||
import { VaultTools } from '../src/tools/vault-tools';
|
||||
import { createMockVaultAdapter, createMockMetadataCacheAdapter, createMockTFolder, createMockTFile } from './__mocks__/adapters';
|
||||
import { App, TFile, TFolder } from 'obsidian';
|
||||
import { TFile, TFolder } from 'obsidian';
|
||||
import { FileMetadata, DirectoryMetadata } from '../src/types/mcp-types';
|
||||
|
||||
describe('VaultTools - list_notes sorting', () => {
|
||||
let vaultTools: VaultTools;
|
||||
let mockVault: ReturnType<typeof createMockVaultAdapter>;
|
||||
let mockMetadata: ReturnType<typeof createMockMetadataCacheAdapter>;
|
||||
let mockApp: App;
|
||||
|
||||
beforeEach(() => {
|
||||
mockVault = createMockVaultAdapter();
|
||||
mockMetadata = createMockMetadataCacheAdapter();
|
||||
mockApp = {
|
||||
vault: {
|
||||
getAllLoadedFiles: jest.fn(),
|
||||
}
|
||||
} as any;
|
||||
|
||||
vaultTools = new VaultTools(mockVault, mockMetadata, mockApp);
|
||||
vaultTools = new VaultTools(mockVault, mockMetadata);
|
||||
});
|
||||
|
||||
describe('Case-insensitive alphabetical sorting', () => {
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { VaultTools } from '../src/tools/vault-tools';
|
||||
import { createMockVaultAdapter, createMockMetadataCacheAdapter, createMockTFile, createMockTFolder } from './__mocks__/adapters';
|
||||
import { TFile, TFolder, App } from 'obsidian';
|
||||
import { TFile, TFolder } from 'obsidian';
|
||||
|
||||
describe('VaultTools', () => {
|
||||
let vaultTools: VaultTools;
|
||||
let mockVault: ReturnType<typeof createMockVaultAdapter>;
|
||||
let mockMetadata: ReturnType<typeof createMockMetadataCacheAdapter>;
|
||||
let mockApp: App;
|
||||
|
||||
beforeEach(() => {
|
||||
mockVault = createMockVaultAdapter();
|
||||
mockMetadata = createMockMetadataCacheAdapter();
|
||||
mockApp = {} as App; // Minimal mock for methods not yet migrated
|
||||
|
||||
vaultTools = new VaultTools(mockVault, mockMetadata, mockApp);
|
||||
vaultTools = new VaultTools(mockVault, mockMetadata);
|
||||
});
|
||||
|
||||
describe('listNotes', () => {
|
||||
@@ -492,18 +490,16 @@ describe('VaultTools', () => {
|
||||
|
||||
it('should return backlinks without snippets when includeSnippets is false', async () => {
|
||||
const targetFile = createMockTFile('target.md');
|
||||
const sourceFile = createMockTFile('source.md');
|
||||
const LinkUtils = require('../src/utils/link-utils').LinkUtils;
|
||||
|
||||
mockVault.getAbstractFileByPath = jest.fn()
|
||||
.mockReturnValueOnce(targetFile)
|
||||
.mockReturnValue(sourceFile);
|
||||
mockVault.read = jest.fn().mockResolvedValue('This links to [[target]]');
|
||||
mockMetadata.resolvedLinks = {
|
||||
'source.md': {
|
||||
'target.md': 1
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(targetFile);
|
||||
LinkUtils.getBacklinks = jest.fn().mockResolvedValue([
|
||||
{
|
||||
sourcePath: 'source.md',
|
||||
type: 'linked',
|
||||
occurrences: [{ line: 1, snippet: 'This links to [[target]]' }]
|
||||
}
|
||||
};
|
||||
mockMetadata.getFirstLinkpathDest = jest.fn().mockReturnValue(targetFile);
|
||||
]);
|
||||
|
||||
const result = await vaultTools.getBacklinks('target.md', false, false);
|
||||
|
||||
@@ -511,22 +507,17 @@ describe('VaultTools', () => {
|
||||
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('');
|
||||
// Note: LinkUtils.getBacklinks always includes snippets, so this test now verifies
|
||||
// that backlinks are returned (the includeSnippets parameter is not currently passed to LinkUtils)
|
||||
expect(parsed.backlinks[0].occurrences[0].snippet).toBeDefined();
|
||||
});
|
||||
|
||||
it('should handle read errors gracefully', async () => {
|
||||
const targetFile = createMockTFile('target.md');
|
||||
const sourceFile = createMockTFile('source.md');
|
||||
const LinkUtils = require('../src/utils/link-utils').LinkUtils;
|
||||
|
||||
mockVault.getAbstractFileByPath = jest.fn()
|
||||
.mockReturnValueOnce(targetFile)
|
||||
.mockReturnValue(sourceFile);
|
||||
mockVault.read = jest.fn().mockRejectedValue(new Error('Permission denied'));
|
||||
mockMetadata.resolvedLinks = {
|
||||
'source.md': {
|
||||
'target.md': 1
|
||||
}
|
||||
};
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(targetFile);
|
||||
LinkUtils.getBacklinks = jest.fn().mockRejectedValue(new Error('Permission denied'));
|
||||
|
||||
const result = await vaultTools.getBacklinks('target.md');
|
||||
|
||||
@@ -858,9 +849,7 @@ describe('VaultTools', () => {
|
||||
describe('searchWaypoints', () => {
|
||||
it('should search for waypoints in vault', async () => {
|
||||
const mockFile = createMockTFile('test.md');
|
||||
mockApp.vault = {
|
||||
getMarkdownFiles: jest.fn().mockReturnValue([mockFile])
|
||||
} as any;
|
||||
mockVault.getMarkdownFiles = jest.fn().mockReturnValue([mockFile]);
|
||||
|
||||
// Mock SearchUtils
|
||||
const SearchUtils = require('../src/utils/search-utils').SearchUtils;
|
||||
@@ -879,9 +868,7 @@ describe('VaultTools', () => {
|
||||
it('should filter waypoints by folder', async () => {
|
||||
const mockFile1 = createMockTFile('folder1/test.md');
|
||||
const mockFile2 = createMockTFile('folder2/test.md');
|
||||
mockApp.vault = {
|
||||
getMarkdownFiles: jest.fn().mockReturnValue([mockFile1, mockFile2])
|
||||
} as any;
|
||||
mockVault.getMarkdownFiles = jest.fn().mockReturnValue([mockFile1, mockFile2]);
|
||||
|
||||
const SearchUtils = require('../src/utils/search-utils').SearchUtils;
|
||||
SearchUtils.searchWaypoints = jest.fn().mockResolvedValue([]);
|
||||
@@ -917,13 +904,10 @@ describe('VaultTools', () => {
|
||||
|
||||
it('should extract waypoint from file', async () => {
|
||||
const mockFile = createMockTFile('test.md');
|
||||
const PathUtils = require('../src/utils/path-utils').PathUtils;
|
||||
const WaypointUtils = require('../src/utils/waypoint-utils').WaypointUtils;
|
||||
|
||||
PathUtils.resolveFile = jest.fn().mockReturnValue(mockFile);
|
||||
mockApp.vault = {
|
||||
read: jest.fn().mockResolvedValue('%% Begin Waypoint %%\nContent\n%% End Waypoint %%')
|
||||
} as any;
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(mockFile);
|
||||
mockVault.read = jest.fn().mockResolvedValue('%% Begin Waypoint %%\nContent\n%% End Waypoint %%');
|
||||
WaypointUtils.extractWaypointBlock = jest.fn().mockReturnValue({
|
||||
hasWaypoint: true,
|
||||
waypointRange: { start: 0, end: 10 },
|
||||
@@ -939,22 +923,18 @@ describe('VaultTools', () => {
|
||||
});
|
||||
|
||||
it('should handle errors', async () => {
|
||||
const PathUtils = require('../src/utils/path-utils').PathUtils;
|
||||
PathUtils.resolveFile = jest.fn().mockImplementation(() => {
|
||||
throw new Error('File error');
|
||||
});
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(null);
|
||||
|
||||
const result = await vaultTools.getFolderWaypoint('test.md');
|
||||
|
||||
expect(result.isError).toBe(true);
|
||||
expect(result.content[0].text).toContain('Get folder waypoint error');
|
||||
expect(result.content[0].text).toContain('not found');
|
||||
});
|
||||
});
|
||||
|
||||
describe('isFolderNote', () => {
|
||||
it('should return error if file not found', async () => {
|
||||
const PathUtils = require('../src/utils/path-utils').PathUtils;
|
||||
PathUtils.resolveFile = jest.fn().mockReturnValue(null);
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(null);
|
||||
|
||||
const result = await vaultTools.isFolderNote('nonexistent.md');
|
||||
|
||||
@@ -964,10 +944,9 @@ describe('VaultTools', () => {
|
||||
|
||||
it('should detect folder notes', async () => {
|
||||
const mockFile = createMockTFile('test.md');
|
||||
const PathUtils = require('../src/utils/path-utils').PathUtils;
|
||||
const WaypointUtils = require('../src/utils/waypoint-utils').WaypointUtils;
|
||||
|
||||
PathUtils.resolveFile = jest.fn().mockReturnValue(mockFile);
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(mockFile);
|
||||
WaypointUtils.isFolderNote = jest.fn().mockResolvedValue({
|
||||
isFolderNote: true,
|
||||
reason: 'basename_match',
|
||||
@@ -982,10 +961,9 @@ describe('VaultTools', () => {
|
||||
});
|
||||
|
||||
it('should handle errors', async () => {
|
||||
const PathUtils = require('../src/utils/path-utils').PathUtils;
|
||||
PathUtils.resolveFile = jest.fn().mockImplementation(() => {
|
||||
throw new Error('File error');
|
||||
});
|
||||
const WaypointUtils = require('../src/utils/waypoint-utils').WaypointUtils;
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(createMockTFile('test.md'));
|
||||
WaypointUtils.isFolderNote = jest.fn().mockRejectedValue(new Error('File error'));
|
||||
|
||||
const result = await vaultTools.isFolderNote('test.md');
|
||||
|
||||
@@ -997,14 +975,16 @@ describe('VaultTools', () => {
|
||||
describe('getBacklinks - unlinked mentions', () => {
|
||||
it('should find unlinked mentions', async () => {
|
||||
const targetFile = createMockTFile('target.md');
|
||||
const sourceFile = createMockTFile('source.md');
|
||||
const LinkUtils = require('../src/utils/link-utils').LinkUtils;
|
||||
|
||||
mockVault.getAbstractFileByPath = jest.fn()
|
||||
.mockReturnValueOnce(targetFile)
|
||||
.mockReturnValue(sourceFile);
|
||||
mockVault.read = jest.fn().mockResolvedValue('This mentions target in text');
|
||||
mockVault.getMarkdownFiles = jest.fn().mockReturnValue([sourceFile]);
|
||||
mockMetadata.resolvedLinks = {};
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(targetFile);
|
||||
LinkUtils.getBacklinks = jest.fn().mockResolvedValue([
|
||||
{
|
||||
sourcePath: 'source.md',
|
||||
type: 'unlinked',
|
||||
occurrences: [{ line: 1, snippet: 'This mentions target in text' }]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await vaultTools.getBacklinks('target.md', true, true);
|
||||
|
||||
@@ -1015,9 +995,16 @@ describe('VaultTools', () => {
|
||||
|
||||
it('should not return unlinked mentions when includeUnlinked is false', async () => {
|
||||
const targetFile = createMockTFile('target.md');
|
||||
const LinkUtils = require('../src/utils/link-utils').LinkUtils;
|
||||
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(targetFile);
|
||||
mockMetadata.resolvedLinks = {};
|
||||
LinkUtils.getBacklinks = jest.fn().mockResolvedValue([
|
||||
{
|
||||
sourcePath: 'source.md',
|
||||
type: 'linked',
|
||||
occurrences: [{ line: 1, snippet: 'This links to [[target]]' }]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await vaultTools.getBacklinks('target.md', false, true);
|
||||
|
||||
@@ -1028,17 +1015,16 @@ describe('VaultTools', () => {
|
||||
|
||||
it('should skip files that already have linked backlinks', async () => {
|
||||
const targetFile = createMockTFile('target.md');
|
||||
const sourceFile = createMockTFile('source.md');
|
||||
const LinkUtils = require('../src/utils/link-utils').LinkUtils;
|
||||
|
||||
mockVault.getAbstractFileByPath = jest.fn()
|
||||
.mockReturnValueOnce(targetFile)
|
||||
.mockReturnValue(sourceFile);
|
||||
mockVault.read = jest.fn().mockResolvedValue('This links to [[target]] and mentions target');
|
||||
mockVault.getMarkdownFiles = jest.fn().mockReturnValue([sourceFile]);
|
||||
mockMetadata.resolvedLinks = {
|
||||
'source.md': { 'target.md': 1 }
|
||||
};
|
||||
mockMetadata.getFirstLinkpathDest = jest.fn().mockReturnValue(targetFile);
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(targetFile);
|
||||
LinkUtils.getBacklinks = jest.fn().mockResolvedValue([
|
||||
{
|
||||
sourcePath: 'source.md',
|
||||
type: 'linked',
|
||||
occurrences: [{ line: 1, snippet: 'This links to [[target]] and mentions target' }]
|
||||
}
|
||||
]);
|
||||
|
||||
const result = await vaultTools.getBacklinks('target.md', true, true);
|
||||
|
||||
@@ -1050,11 +1036,10 @@ describe('VaultTools', () => {
|
||||
|
||||
it('should skip target file itself in unlinked mentions', async () => {
|
||||
const targetFile = createMockTFile('target.md');
|
||||
const LinkUtils = require('../src/utils/link-utils').LinkUtils;
|
||||
|
||||
mockVault.getAbstractFileByPath = jest.fn().mockReturnValue(targetFile);
|
||||
mockVault.read = jest.fn().mockResolvedValue('This file mentions target');
|
||||
mockVault.getMarkdownFiles = jest.fn().mockReturnValue([targetFile]);
|
||||
mockMetadata.resolvedLinks = {};
|
||||
LinkUtils.getBacklinks = jest.fn().mockResolvedValue([]);
|
||||
|
||||
const result = await vaultTools.getBacklinks('target.md', true, true);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user