Files
obsidian-mcp-server/docs/plans/2025-01-20-tools-coverage-implementation.md

7.8 KiB

Implementation Plan: 100% Tool Coverage

Date: 2025-01-20 Goal: Achieve 100% test coverage on note-tools.ts and vault-tools.ts Approach: Add targeted test cases to existing test files

Overview

This plan addresses the remaining coverage gaps in the tool modules to achieve 100% statement coverage as part of pre-release validation.

Current Coverage Status

  • note-tools.ts: 96.01% → Target: 100% (9 uncovered lines)
  • vault-tools.ts: 94.22% → Target: 100% (14 uncovered lines)

Gap Analysis

Note-Tools Uncovered Lines (9 lines)

  1. Lines 238-239: Conflict resolution loop (when creating files with duplicate names)
  2. Lines 377, 408, 590, 710, 836: Folder-not-file errors (5 occurrences across different methods)
  3. Line 647: Include compressed data flag in Excalidraw read
  4. Line 771: Add frontmatter to file that has no existing frontmatter

Vault-Tools Uncovered Lines (14 lines)

  1. Line 76: Invalid path validation error
  2. Line 200: Folder assignment (possibly unreachable)
  3. Line 267: Skip root folder in iteration
  4. Line 272: Glob filtering skip
  5. Line 325: Alias as string (non-array) normalization
  6. Line 374: Folder mtime extraction error catch
  7. Lines 452-456, 524-528: Defensive "path doesn't exist" returns
  8. Lines 596-597: Glob filtering in search
  9. Lines 608, 620: MaxResults early termination
  10. Line 650: Snippet end-of-line adjustment
  11. Line 777: Search error catch block

Implementation Tasks

Task 1: Add Note-Tools Conflict Resolution Tests

Objective: Cover lines 238-239 (conflict resolution loop)

Steps:

  1. Add test: "creates file with incremented counter when conflicts exist"
  2. Mock PathUtils.fileExists to return true for "file.md" and "file 2.md"
  3. Verify creates "file 3.md"
  4. Run coverage to confirm lines 238-239 covered

Files to modify:

  • tests/note-tools.test.ts

Expected outcome: Lines 238-239 covered


Task 2: Add Note-Tools Folder-Not-File Error Tests

Objective: Cover lines 377, 408, 590, 710, 836 (folder instead of file errors)

Steps:

  1. Add test for read_note: "returns error when path is a folder"

    • Mock PathUtils.folderExists to return true
    • Verify error message uses ErrorMessages.notAFile()
  2. Add test for rename_file: "returns error when path is a folder"

  3. Add test for update_note: "returns error when path is a folder"

  4. Add test for delete_note: "returns error when path is a folder"

  5. Add test for update_sections: "returns error when path is a folder"

  6. Run coverage to confirm all 5 lines covered

Files to modify:

  • tests/note-tools.test.ts

Expected outcome: Lines 377, 408, 590, 710, 836 covered


Task 3: Add Note-Tools Excalidraw and Frontmatter Tests

Objective: Cover lines 647, 771

Steps:

  1. Add test for read_excalidraw: "includes compressed data when flag is true"

    • Call with includeCompressed=true
    • Verify result.compressedData is included
  2. Add test for update_frontmatter: "adds frontmatter to file without existing frontmatter"

    • Mock file content without frontmatter
    • Update frontmatter
    • Verify frontmatter added at beginning with newline separator
  3. Run coverage to confirm lines 647, 771 covered

Files to modify:

  • tests/note-tools.test.ts

Expected outcome: Lines 647, 771 covered, note-tools.ts at 100%


Task 4: Add Vault-Tools Invalid Path and Glob Tests

Objective: Cover lines 76, 272, 596-597

Steps:

  1. Add test for list(): "returns error for invalid vault path"

    • Mock PathUtils.isValidVaultPath to return false
    • Verify error message
  2. Add test for list(): "filters items using glob excludes"

    • Mock GlobUtils.shouldInclude to return false for some items
    • Verify filtered items not in results
  3. Add test for search(): "applies glob filtering to search results"

    • Provide includes/excludes patterns
    • Verify filtered files not searched
  4. Run coverage to confirm lines 76, 272, 596-597 covered

Files to modify:

  • tests/vault-tools.test.ts

Expected outcome: Lines 76, 272, 596-597 covered


Task 5: Add Vault-Tools Edge Case Tests

Objective: Cover lines 267, 325, 374, 608, 620, 650

Steps:

  1. Add test for list(): "skips root folder in iteration"

    • Mock folder structure with root folder (path='', isRoot()=true)
    • Verify root not in results
  2. Add test for list(): "normalizes aliases from string to array"

    • Mock cache.frontmatter.aliases as string instead of array
    • Verify result has aliases as array
  3. Add test for getFolderMetadata(): "handles folder without mtime stat"

    • Mock folder without stat or with invalid stat
    • Verify doesn't crash, uses default mtime
  4. Add test for search(): "stops searching when maxResults=1 reached"

    • Multiple files with matches
    • Verify only 1 result returned
  5. Add test for search(): "adjusts snippet for long lines at end"

    • Mock line longer than snippetLength ending with match
    • Verify snippet adjustment logic (line 650)
  6. Run coverage to confirm lines 267, 325, 374, 608, 620, 650 covered

Files to modify:

  • tests/vault-tools.test.ts

Expected outcome: Lines 267, 325, 374, 608, 620, 650 covered


Task 6: Add Vault-Tools Defensive Code Coverage

Objective: Cover lines 200, 452-456, 524-528, 777

Steps:

  1. Analyze if lines 200, 452-456, 524-528 are truly unreachable

    • If unreachable: Document why (defensive code)
    • If reachable: Add tests to trigger them
  2. Add test for search(): "handles file read errors gracefully"

    • Mock vault.read to throw error
    • Verify error caught, logged to console, search continues
    • Covers line 777
  3. For defensive returns (452-456, 524-528):

    • Attempt to trigger "path doesn't exist" cases
    • If impossible: Document as unreachable defensive code
  4. Run coverage to verify maximum possible coverage

Files to modify:

  • tests/vault-tools.test.ts
  • Possibly: add comments in source code marking defensive code

Expected outcome: Lines covered or documented as unreachable


Task 7: Verify 100% Coverage

Objective: Confirm 100% coverage achieved

Steps:

  1. Run npm run test:coverage
  2. Check coverage report:
    • note-tools.ts: 100% or documented gaps
    • vault-tools.ts: 100% or documented gaps
  3. If any gaps remain:
    • Identify what's uncovered
    • Add tests or document as unreachable
  4. Final coverage verification

Expected outcome: Both tools at 100% coverage


Task 8: Run Full Test Suite and Build

Objective: Verify no regressions

Steps:

  1. Run npm test - all tests must pass
  2. Run npm run build - must succeed
  3. Verify total test count increased
  4. Document final metrics

Expected outcome: All tests passing, build successful


Task 9: Create Summary and Merge

Objective: Document and integrate work

Steps:

  1. Update IMPLEMENTATION_SUMMARY.md with:
    • Coverage improvements (before/after)
    • Test counts
    • Any unreachable code documented
  2. Use finishing-a-development-branch skill
  3. Merge to master

Expected outcome: Work merged, documentation updated

Success Criteria

  • note-tools.ts at 100% statement coverage
  • vault-tools.ts at 100% statement coverage
  • All tests passing
  • Build succeeds
  • Any unreachable code documented
  • Work merged to master

Risk Mitigation

If some lines are truly unreachable:

  • Document with inline comments explaining why
  • Accept 99.x% if justified
  • Focus on getting all reachable code to 100%

If tests become too complex:

  • Consider minor refactoring for testability
  • Use subagent review to validate approach
  • Ensure tests remain maintainable

Estimated Effort

  • Note-tools tests: ~1 hour (7 new test cases)
  • Vault-tools tests: ~1.5 hours (10-12 new test cases)
  • Verification and cleanup: ~0.5 hours
  • Total: ~3 hours