Phase 2 - Breaking Changes (v2.0.0): - Added typed result interfaces (FileMetadata, DirectoryMetadata, VaultInfo, SearchResult, SearchMatch) - Unified parameter naming: list_notes now uses 'path' parameter (removed 'folder') - Enhanced tool responses with structured JSON for all tools - list_notes: Returns array of FileMetadata/DirectoryMetadata with full metadata - search_notes: Returns SearchResult with line numbers, snippets, and match ranges - get_vault_info: Returns VaultInfo with comprehensive statistics - Updated all tool descriptions to document structured responses - Version bumped to 2.0.0 (breaking changes) Phase 2.1 - Post-Testing Fixes: - Fixed root listing to exclude vault root folder itself (handles path '', '/', and isRoot()) - Fixed alphabetical sorting to be case-insensitive for stable ordering - Improved directory metadata with better timestamp detection and error handling - Fixed parent folder validation order (check if file before checking existence) - Updated documentation with root path examples and leading slash warnings - Added comprehensive test suite for sorting and root listing behavior - Fixed test mocks to use proper TFile/TFolder instances Tests: All 64 tests passing Build: Successful, no errors
Tests
This directory contains unit and integration tests for the Obsidian MCP Server plugin.
Current Status
The test files are currently documentation of expected behavior. To actually run these tests, you need to set up a testing framework.
Setting Up Jest (Recommended)
- Install Jest and related dependencies:
npm install --save-dev jest @types/jest ts-jest
- Create a
jest.config.jsfile in the project root:
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
roots: ['<rootDir>/tests'],
testMatch: ['**/*.test.ts'],
moduleFileExtensions: ['ts', 'tsx', 'js', 'jsx', 'json', 'node'],
collectCoverageFrom: [
'src/**/*.ts',
'!src/**/*.d.ts',
],
};
- Add test script to
package.json:
{
"scripts": {
"test": "jest",
"test:watch": "jest --watch",
"test:coverage": "jest --coverage"
}
}
- Run tests:
npm test
Test Files
path-utils.test.ts
Tests for the PathUtils class, covering:
- Path normalization (cross-platform)
- Path validation
- File/folder resolution
- Path manipulation utilities
Key Test Categories:
- normalizePath: Tests for handling leading/trailing slashes, backslashes, drive letters
- isValidVaultPath: Tests for path validation rules
- Cross-platform: Tests for Windows, macOS, and Linux path handling
Mocking Obsidian API
Since these tests run outside of Obsidian, you'll need to mock the Obsidian API:
// Example mock setup
jest.mock('obsidian', () => ({
App: jest.fn(),
TFile: jest.fn(),
TFolder: jest.fn(),
TAbstractFile: jest.fn(),
// ... other Obsidian types
}));
Running Tests Without Jest
If you prefer not to set up Jest, you can:
- Use the test files as documentation of expected behavior
- Manually test the functionality through the MCP server
- Use TypeScript's type checking to catch errors:
npm run build
Future Improvements
- Set up Jest testing framework
- Add integration tests with mock Obsidian vault
- Add tests for error-messages.ts
- Add tests for tool implementations
- Add tests for MCP server endpoints
- Set up CI/CD with automated testing
- Add code coverage reporting
Test Coverage Goals
- PathUtils: 100% coverage (critical for cross-platform support)
- ErrorMessages: 100% coverage (important for user experience)
- Tool implementations: 80%+ coverage
- Server/middleware: 70%+ coverage
Writing New Tests
When adding new features, please:
- Write tests first (TDD approach recommended)
- Test both success and error cases
- Test edge cases and boundary conditions
- Test cross-platform compatibility where relevant
- Add descriptive test names that explain the expected behavior
Example test structure:
describe('FeatureName', () => {
describe('methodName', () => {
test('should handle normal case', () => {
// Arrange
const input = 'test';
// Act
const result = method(input);
// Assert
expect(result).toBe('expected');
});
test('should handle error case', () => {
expect(() => method(null)).toThrow();
});
});
});