import { describe, expect, it } from 'vitest' import { combineSnapshots, parseSnapshot } from '../src/catalog.js' const now = Date.parse('2026-01-01T00:00:30.000Z') function snapshot(path: string, overrides: Record = {}) { return { version: 1, instanceId: 'vscode-a', ide: 'vscode', processId: 10, workspace: 'F:\\project', updatedAt: '2026-01-01T00:00:00.000Z', documents: [{ path, label: 'main.ts', dirty: false }], ...overrides, } } describe('parseSnapshot', () => { it('accepts valid absolute files', () => { expect(parseSnapshot(snapshot('F:\\project\\main.ts'), now)?.documents).toHaveLength(1) }) it('rejects stale and unknown versions', () => { expect(parseSnapshot(snapshot('F:\\project\\main.ts'), now + 61_000)).toBeUndefined() expect(parseSnapshot(snapshot('F:\\project\\main.ts', { version: 2 }), now)).toBeUndefined() }) it('rejects unknown v1 fields', () => { expect(parseSnapshot(snapshot('F:\\\\project\\\\main.ts', { extra: true }), now)).toBeUndefined() }) it('drops invalid and duplicate documents without dropping the instance', () => { const value = snapshot('F:\\project\\main.ts', { documents: [ { path: 'relative.ts', label: 'relative.ts', dirty: false }, { path: 'F:\\project\\main.ts', label: 'main.ts', dirty: false }, { path: 'f:\\PROJECT\\MAIN.ts', label: 'MAIN.ts', dirty: true }, ], }) expect(parseSnapshot(value, now)?.documents).toHaveLength(1) }) }) describe('combineSnapshots', () => { it('deduplicates paths and prefers dirty metadata', () => { const clean = parseSnapshot(snapshot('F:\\project\\main.ts'), now)! const dirty = parseSnapshot(snapshot('f:\\PROJECT\\main.ts', { instanceId: 'visualstudio-b', ide: 'visualstudio', documents: [{ path: 'f:\\PROJECT\\main.ts', label: 'main.ts', dirty: true }], }), now)! const result = combineSnapshots([clean, dirty]) expect(result.total).toBe(1) expect(result.items[0]?.dirty).toBe(true) }) it('matches basename before path and caps the result', () => { const parsed = parseSnapshot(snapshot('F:\\project\\main.ts', { documents: [ { path: 'F:\\project\\src\\main.ts', label: 'main.ts', dirty: false }, { path: 'F:\\project\\main-helper.ts', label: 'main-helper.ts', dirty: false }, ], }), now)! expect(combineSnapshots([parsed], 'main', 1).items).toHaveLength(1) expect(combineSnapshots([parsed], 'main', 10).total).toBe(2) }) it('excludes Visual Studio project and solution files by default', () => { const parsed = parseSnapshot(snapshot('F:\\project\\main.cs', { documents: [ { path: 'F:\\project\\main.cs', label: 'main.cs', dirty: false }, { path: 'F:\\project\\app.csproj', label: 'app.csproj', dirty: false }, { path: 'F:\\project\\library.fsproj', label: 'library.fsproj', dirty: false }, { path: 'F:\\project\\tools.vbproj', label: 'tools.vbproj', dirty: false }, { path: 'F:\\project\\native.vcxproj', label: 'native.vcxproj', dirty: false }, { path: 'F:\\project\\legacy.vsproj', label: 'legacy.vsproj', dirty: false }, { path: 'F:\\project\\app.sln', label: 'app.sln', dirty: false }, { path: 'F:\\project\\app.slnx', label: 'app.slnx', dirty: false }, ], }), now)! expect(combineSnapshots([parsed]).items.map((item) => item.label)).toEqual(['main.cs']) }) it('matches configured extensions without case sensitivity', () => { const parsed = parseSnapshot(snapshot('F:\\project\\main.cs', { documents: [ { path: 'F:\\project\\main.cs', label: 'main.cs', dirty: false }, { path: 'F:\\project\\APP.CSPROJ', label: 'APP.CSPROJ', dirty: false }, ], }), now)! expect(combineSnapshots([parsed], '', 100, ['.csproj']).items.map((item) => item.label)).toEqual(['main.cs']) }) it('accepts configured extensions without a leading dot', () => { const parsed = parseSnapshot(snapshot('F:\\project\\main.cs', { documents: [ { path: 'F:\\project\\main.cs', label: 'main.cs', dirty: false }, { path: 'F:\\project\\app.slnx', label: 'app.slnx', dirty: false }, ], }), now)! expect(combineSnapshots([parsed], '', 100, ['slnx']).items.map((item) => item.label)).toEqual(['main.cs']) }) it('disables filtering when excludedExtensions is empty', () => { const parsed = parseSnapshot(snapshot('F:\\project\\main.cs', { documents: [ { path: 'F:\\project\\main.cs', label: 'main.cs', dirty: false }, { path: 'F:\\project\\app.csproj', label: 'app.csproj', dirty: false }, ], }), now)! expect(combineSnapshots([parsed], '', 100, []).total).toBe(2) }) it('reports the filtered document count for each instance', () => { const parsed = parseSnapshot(snapshot('F:\\project\\main.cs', { documents: [ { path: 'F:\\project\\main.cs', label: 'main.cs', dirty: false }, { path: 'F:\\project\\app.csproj', label: 'app.csproj', dirty: false }, ], }), now)! expect(combineSnapshots([parsed]).items[0]?.instanceOpenCount).toBe(1) }) })