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).
12 KiB
Implementation Plan: 100% Utility Coverage
Date: 2025-01-20 Branch: feature/utils-coverage Goal: Achieve 100% test coverage on all utility modules using dependency injection pattern
Overview
Apply the same adapter pattern used for tools to utility modules, enabling comprehensive testing. This is pre-release validation work.
Current Coverage Status
- glob-utils.ts: 14.03%
- frontmatter-utils.ts: 47.86%
- search-utils.ts: 1.78%
- link-utils.ts: 13.76%
- waypoint-utils.ts: 49.18%
Target: 100% on all utilities
Implementation Tasks
Task 2: Add comprehensive tests for glob-utils.ts
Objective: Achieve 100% coverage on glob-utils.ts (pure utility, no refactoring needed)
Steps:
- Create
tests/glob-utils.test.ts - Test
globToRegex()pattern conversion:*matches any chars except/**matches any chars including/?matches single char except/[abc]character classes{a,b}alternatives- Edge cases: unclosed brackets, unclosed braces
- Test
matches()with various patterns - Test
matchesIncludes()with empty/populated arrays - Test
matchesExcludes()with empty/populated arrays - Test
shouldInclude()combining includes and excludes - Run coverage to verify 100%
- Commit: "test: add comprehensive glob-utils tests"
Files to create:
tests/glob-utils.test.ts
Expected outcome: glob-utils.ts at 100% coverage
Task 3: Add comprehensive tests for frontmatter-utils.ts
Objective: Achieve 100% coverage on frontmatter-utils.ts
Steps:
- Create
tests/frontmatter-utils.test.ts - Mock
parseYamlfrom obsidian module - Test
extractFrontmatter():- Valid frontmatter with
---delimiters - No frontmatter
- Missing closing delimiter
- Parse errors (mock parseYaml throwing)
- Valid frontmatter with
- Test
extractFrontmatterSummary():- Null input
- Title, tags, aliases extraction
- Tags/aliases as string vs array
- Test
hasFrontmatter()quick check - Test
serializeFrontmatter():- Arrays, objects, strings with special chars
- Empty objects
- Strings needing quotes
- Test
parseExcalidrawMetadata():- Valid Excalidraw with markers
- Compressed data detection
- Uncompressed JSON parsing
- Missing JSON blocks
- Run coverage to verify 100%
- Commit: "test: add comprehensive frontmatter-utils tests"
Files to create:
tests/frontmatter-utils.test.ts
Expected outcome: frontmatter-utils.ts at 100% coverage
Task 4: Refactor search-utils.ts to use IVaultAdapter
Objective: Decouple search-utils from App, use IVaultAdapter
Steps:
- Change
SearchUtils.search()signature:- From:
search(app: App, options: SearchOptions) - To:
search(vault: IVaultAdapter, options: SearchOptions)
- From:
- Update method body:
- Replace
app.vault.getMarkdownFiles()withvault.getMarkdownFiles() - Replace
app.vault.read(file)withvault.read(file)
- Replace
- Change
SearchUtils.searchWaypoints()signature:- From:
searchWaypoints(app: App, folder?: string) - To:
searchWaypoints(vault: IVaultAdapter, folder?: string)
- From:
- Update method body:
- Replace
app.vault.getMarkdownFiles()withvault.getMarkdownFiles() - Replace
app.vault.read(file)withvault.read(file)
- Replace
- Run tests to ensure no breakage (will update callers in Task 7)
- Commit: "refactor: search-utils to use IVaultAdapter"
Files to modify:
src/utils/search-utils.ts
Expected outcome: search-utils.ts uses adapters instead of App
Task 5: Refactor link-utils.ts to use adapters
Objective: Decouple link-utils from App, use adapters
Steps:
- Change
LinkUtils.resolveLink()signature:- From:
resolveLink(app: App, sourcePath: string, linkText: string) - To:
resolveLink(vault: IVaultAdapter, metadata: IMetadataCacheAdapter, sourcePath: string, linkText: string)
- From:
- Update method body:
- Replace
app.vault.getAbstractFileByPath()withvault.getAbstractFileByPath() - Replace
app.metadataCache.getFirstLinkpathDest()withmetadata.getFirstLinkpathDest()
- Replace
- Change
LinkUtils.findSuggestions()signature:- From:
findSuggestions(app: App, linkText: string, ...) - To:
findSuggestions(vault: IVaultAdapter, linkText: string, ...)
- From:
- Update:
app.vault.getMarkdownFiles()→vault.getMarkdownFiles() - Change
LinkUtils.getBacklinks()signature:- From:
getBacklinks(app: App, targetPath: string, ...) - To:
getBacklinks(vault: IVaultAdapter, metadata: IMetadataCacheAdapter, targetPath: string, ...)
- From:
- Update method body:
- Replace
app.vaultcalls withvaultcalls - Replace
app.metadataCachecalls withmetadatacalls
- Replace
- Change
LinkUtils.validateWikilinks()signature:- From:
validateWikilinks(app: App, filePath: string) - To:
validateWikilinks(vault: IVaultAdapter, metadata: IMetadataCacheAdapter, filePath: string)
- From:
- Update all internal calls to
resolveLink()to pass both adapters - Run tests (will break until Task 7)
- Commit: "refactor: link-utils to use adapters"
Files to modify:
src/utils/link-utils.ts
Expected outcome: link-utils.ts uses adapters instead of App
Task 6: Refactor waypoint-utils.ts to use IVaultAdapter
Objective: Decouple waypoint-utils from App, use IVaultAdapter
Steps:
- Change
WaypointUtils.isFolderNote()signature:- From:
isFolderNote(app: App, file: TFile) - To:
isFolderNote(vault: IVaultAdapter, file: TFile)
- From:
- Update method body:
- Replace
await app.vault.read(file)withawait vault.read(file)
- Replace
- Run tests (will break until Task 7)
- Commit: "refactor: waypoint-utils to use IVaultAdapter"
Files to modify:
src/utils/waypoint-utils.ts
Expected outcome: waypoint-utils.ts uses adapters instead of App
Task 7: Update VaultTools to pass adapters to utilities
Objective: Fix all callers of refactored utilities
Steps:
- In VaultTools.search() method:
- Change:
SearchUtils.search(this.app, options) - To:
SearchUtils.search(this.vault, options)
- Change:
- In VaultTools.searchWaypoints() method:
- Change:
SearchUtils.searchWaypoints(this.app, folder) - To:
SearchUtils.searchWaypoints(this.vault, folder)
- Change:
- In VaultTools.validateWikilinks() method:
- Change:
LinkUtils.validateWikilinks(this.app, filePath) - To:
LinkUtils.validateWikilinks(this.vault, this.metadata, filePath)
- Change:
- In VaultTools.resolveWikilink() method:
- Change:
LinkUtils.resolveLink(this.app, sourcePath, linkText) - To:
LinkUtils.resolveLink(this.vault, this.metadata, sourcePath, linkText)
- Change:
- In VaultTools.getBacklinks() method:
- Change:
LinkUtils.getBacklinks(this.app, targetPath, includeUnlinked) - To:
LinkUtils.getBacklinks(this.vault, this.metadata, targetPath, includeUnlinked)
- Change:
- In VaultTools.isFolderNote() method:
- Change:
WaypointUtils.isFolderNote(this.app, file) - To:
WaypointUtils.isFolderNote(this.vault, file)
- Change:
- Run all tests to verify no breakage
- Commit: "refactor: update VaultTools to pass adapters to utils"
Files to modify:
src/tools/vault-tools.ts
Expected outcome: All tests passing, utilities use adapters
Task 8: Add comprehensive tests for search-utils.ts
Objective: Achieve 100% coverage on search-utils.ts
Steps:
- Create
tests/search-utils.test.ts - Set up mock IVaultAdapter
- Test
SearchUtils.search():- Basic literal search
- Regex search with pattern
- Case sensitive vs insensitive
- Folder filtering
- Glob includes/excludes filtering
- Snippet extraction with long lines
- Filename matching (line: 0)
- MaxResults limiting
- File read errors (catch block)
- Zero-width regex matches (prevent infinite loop)
- Test
SearchUtils.searchWaypoints():- Finding waypoint blocks
- Extracting links from waypoints
- Folder filtering
- Unclosed waypoints
- File read errors
- Run coverage to verify 100%
- Commit: "test: add comprehensive search-utils tests"
Files to create:
tests/search-utils.test.ts
Expected outcome: search-utils.ts at 100% coverage
Task 9: Add comprehensive tests for link-utils.ts
Objective: Achieve 100% coverage on link-utils.ts
Steps:
- Create
tests/link-utils.test.ts - Set up mock IVaultAdapter and IMetadataCacheAdapter
- Test
LinkUtils.parseWikilinks():- Simple links
[[target]] - Links with aliases
[[target|alias]] - Links with headings
[[note#heading]] - Multiple links per line
- Simple links
- Test
LinkUtils.resolveLink():- Valid link resolution
- Invalid source path
- Link not found (returns null)
- Test
LinkUtils.findSuggestions():- Exact basename match
- Partial basename match
- Path contains match
- Character similarity scoring
- MaxSuggestions limiting
- Test
LinkUtils.getBacklinks():- Linked backlinks from resolvedLinks
- Unlinked mentions when includeUnlinked=true
- Skip target file itself
- Extract snippets
- Test
LinkUtils.validateWikilinks():- Resolved links
- Unresolved links with suggestions
- File not found
- Test
LinkUtils.extractSnippet()private method via public methods - Run coverage to verify 100%
- Commit: "test: add comprehensive link-utils tests"
Files to create:
tests/link-utils.test.ts
Expected outcome: link-utils.ts at 100% coverage
Task 10: Add comprehensive tests for waypoint-utils.ts
Objective: Achieve 100% coverage on waypoint-utils.ts
Steps:
- Create
tests/waypoint-utils.test.ts - Set up mock IVaultAdapter
- Test
WaypointUtils.extractWaypointBlock():- Valid waypoint with links
- No waypoint in content
- Unclosed waypoint
- Empty waypoint
- Test
WaypointUtils.hasWaypointMarker():- Content with both markers
- Content missing markers
- Test
WaypointUtils.isFolderNote():- Basename match (reason: basename_match)
- Waypoint marker (reason: waypoint_marker)
- Both (reason: both)
- Neither (reason: none)
- File read errors
- Test
WaypointUtils.wouldAffectWaypoint():- Waypoint removed
- Waypoint content changed
- Waypoint moved but content same
- No waypoint in either version
- Test pure helper methods:
getParentFolderPath()getBasename()
- Run coverage to verify 100%
- Commit: "test: add comprehensive waypoint-utils tests"
Files to create:
tests/waypoint-utils.test.ts
Expected outcome: waypoint-utils.ts at 100% coverage
Task 11: Verify 100% coverage on all utilities
Objective: Confirm all utilities at 100% coverage
Steps:
- Run
npm run test:coverage - Check coverage report for:
- glob-utils.ts: 100%
- frontmatter-utils.ts: 100%
- search-utils.ts: 100%
- link-utils.ts: 100%
- waypoint-utils.ts: 100%
- If any gaps remain, identify uncovered lines
- Add tests to cover any remaining gaps
- Commit any additional tests
- Final coverage verification
Expected outcome: All utilities at 100% coverage
Task 12: Run full test suite and build
Objective: Verify all tests pass and build succeeds
Steps:
- Run
npm testto verify all tests pass - Run
npm run buildto verify no type errors - Check test count increased significantly
- Verify no regressions in existing tests
- Document final test counts and coverage
Expected outcome: All tests passing, build successful
Task 13: Create summary and merge to master
Objective: Document work and integrate to master
Steps:
- Create summary document with:
- Coverage improvements
- Test counts before/after
- Architecture changes (adapter pattern in utils)
- Use finishing-a-development-branch skill
- Merge to master
- Clean up worktree
Expected outcome: Work merged, worktree cleaned up
Success Criteria
- All utilities at 100% statement coverage
- All tests passing (expected 300+ tests)
- Build succeeds with no type errors
- Adapter pattern consistently applied
- Work merged to master branch