fix(versions): normalize folder paths before the foldersInUse check - #153
Conversation
The folder-in-use guard used Array.includes(), which does raw string equality. A trailing slash, different casing on Windows, or mixed separators let the user register two versions pointing at the same physical directory. Introduce normalizeFolderForComparison() in src/domain/paths.ts: strips trailing separators and lowercases on Windows (detected by drive letter or backslash presence). Replace the raw .includes() in both installGameVersion and createInstallation with folderIsInUse(), which normalizes both sides before comparing. 12 new unit tests cover the normalizer and the comparison helper. Fixes #147
Pixnop
left a comment
There was a problem hiding this comment.
Right direction and the right home for it, a pure domain module both call sites share. But I probed the implementation against the two cases the body itself names, and one of them is still open while a new one appeared.
Mixed separators are not handled: folderIsInUse('C:/Games/VS', ['C:\Games\VS']) returns false on this branch, probed directly. Stripping trailing separators and lowercasing does nothing about the separators in the middle, so the same physical directory registered once through the picker (backslashes) and once typed with forward slashes still slips the guard, which is the exact scenario issue #147 describes. Unifying separators when the path is Windows-like (replace both kinds with one) closes it, and two test rows pin it.
The looksLikeWindows heuristic misfires on Linux: a backslash is a legal filename character there, so '/home/a/dir\x' and '/home/a/DIR\x', two genuinely distinct folders, get lowercased into a false in-use refusal, also probed. The heuristic is not needed at the install call site: installGameVersion already receives platform in its input, so folderIsInUse can take platform as an argument and only fold case on win32. If createInstallation lacks the platform today, threading it through is a smaller wart than deciding the OS from string shape.
With the separator unification, the platform parameter, and tests for both probed cases, this is an approve.
…mparison Replace the looksLikeWindows heuristic with an explicit platform parameter (defaults to auto-detect from drive letter). Unify backslashes to forward slashes before comparison, so C:/Games/VS and C:\Games\VS match correctly. On posix, a backslash in a filename is normalized the same way (unified to /) but case is preserved, preventing the false-positive where two genuinely distinct Linux paths got lowercased into a collision. Tests cover: mixed separators on Windows, explicit posix with backslash filename chars, and the platform param threading through folderIsInUse.
|
Addressed in 5af7eed.
|
Pixnop
left a comment
There was a problem hiding this comment.
Approving. Re-probed everything from the previous round on the new commit: the mixed-separator pair is now detected as the same folder, and with the explicit posix parameter two Linux folders differing only by case stay distinct, so both misfires are closed. The platform parameter threaded through install and create is the right shape, with the shape-detection heuristic surviving only as a fallback default. Sixteen tests pass including the new separator rows.
One residual quirk for the record, not blocking: the separator unification runs unconditionally, so on posix a folder with a literal backslash in its name ('dir\x') normalizes to the same string as a nested 'dir/x' and produces a false in-use refusal. Probed real, but it needs a backslash-named folder AND its slash twin to coexist, which I am comfortable calling theoretical. If paths.ts is ever touched again, making the backslash replacement win32-only removes it.
The folder-in-use guard used
Array.includes(), which does raw string equality. A trailing slash, different casing on Windows, or mixed separators let the user register two versions pointing at the same physical directory.What changed
src/domain/paths.ts: exportsnormalizeFolderForComparison()(strips trailing separators, lowercases on Windows) andfolderIsInUse()(normalizes both sides before comparing).src/domain/versions/install.ts: replace raw.includes()withfolderIsInUse().src/domain/installations/create.ts: same replacement.tests/domain/paths.test.ts: 12 tests covering trailing slashes, case insensitivity on Windows paths, Linux case preservation, and edge cases.What was tested
npm run typecheckpassesnpm run lint:cipassesnpm run test:coveragepasses (1122 tests, up from 1110)Fixes #147