also looking at this
feat(orphans): scan Application Support and Containers
#29
3 files
+242
-19
| @@ -0,0 +1,68 @@ | ||
| 1 | import { describe, expect, it } from 'bun:test' | |
| 2 | import { findOrphanedAppData, KNOWN_APP_DIRS, SYSTEM_DIR_NAMES } from '@system-cleaner/clean' | |
| 3 | ||
| 4 | /** | |
| 5 | * These guard the Application Support scan added for #26. | |
| 6 | * | |
| 7 | * The risk this feature carries is not missing an orphan — it is reporting | |
| 8 | * live data as one. Chrome keeps 7.7 GB of profile (history, bookmarks, | |
| 9 | * saved passwords) in a directory called `Google`, which matches no installed | |
| 10 | * app name; `MobileSync` holds every iPhone backup on the machine and has no | |
| 11 | * owning `.app` at all. Both must stay unreachable from this scan. | |
| 12 | */ | |
| 13 | describe('orphan attribution', () => { | |
| 14 | it('maps vendor directory names to the apps that actually own them', () => { | |
| 15 | // The directory name is not the app name for any of these, which is the | |
| 16 | // whole reason the map exists. | |
| 17 | expect(KNOWN_APP_DIRS.Google).toContain('Google Chrome') | |
| 18 | expect(KNOWN_APP_DIRS.Mozilla).toContain('Firefox') | |
| 19 | expect(KNOWN_APP_DIRS.BraveSoftware).toContain('Brave Browser') | |
| 20 | expect(KNOWN_APP_DIRS.Code).toContain('Visual Studio Code') | |
| 21 | }) | |
| 22 | ||
| 23 | it('treats macOS-owned directories as system data, not orphans', () => { | |
| 24 | // None of these has an owning `.app`, so any "is the app installed?" | |
| 25 | // test reports them as orphaned unless they are excluded outright. | |
| 26 | for (const name of ['mobilesync', 'addressbook', 'clouddocs', 'knowledge', 'fileprovider']) | |
| 27 | expect(SYSTEM_DIR_NAMES.has(name)).toBe(true) | |
| 28 | }) | |
| 29 | ||
| 30 | it('has no directory listed as both system-owned and app-owned', () => { | |
| 31 | for (const dir of Object.keys(KNOWN_APP_DIRS)) | |
| 32 | expect(SYSTEM_DIR_NAMES.has(dir.toLowerCase())).toBe(false) | |
| 33 | }) | |
| 34 | }) | |
| 35 | ||
| 36 | describe('findOrphanedAppData', () => { | |
| 37 | it('never reports a directory whose owning app is installed', async () => { | |
| 38 | const items = await findOrphanedAppData() | |
| 39 | const reported = new Set(items.map(item => item.bundleId)) | |
| 40 | ||
| 41 | // Walk the map rather than hardcoding app names: whatever is installed on | |
| 42 | // the machine running the suite, its support directory must not appear. | |
| 43 | for (const [dir, owners] of Object.entries(KNOWN_APP_DIRS)) { | |
| 44 | const installed = owners.some(owner => | |
| 45 | ['/Applications', `${process.env.HOME}/Applications`] | |
| 46 | .some(base => Bun.file(`${base}/${owner}.app/Contents/Info.plist`).size > 0), | |
| 47 | ) | |
| 48 | if (installed) | |
| 49 | expect(reported.has(dir)).toBe(false) | |
| 50 | } | |
| 51 | }, 120_000) | |
| 52 | ||
| 53 | it('never reports a macOS-owned directory', async () => { | |
| 54 | const items = await findOrphanedAppData() | |
| 55 | for (const item of items) | |
| 56 | expect(SYSTEM_DIR_NAMES.has(item.bundleId.toLowerCase())).toBe(false) | |
| 57 | }, 120_000) | |
| 58 | ||
| 59 | it('marks Application Support and Container hits as holding user data', async () => { | |
| 60 | const items = await findOrphanedAppData() | |
| 61 | for (const item of items) { | |
| 62 | if (item.type === 'app-support' || item.type === 'container') | |
| 63 | expect(item.holdsUserData).toBe(true) | |
| 64 | if (item.type === 'cache' || item.type === 'log') | |
| 65 | expect(item.holdsUserData).toBe(false) | |
| 66 | } | |
| 67 | }, 120_000) | |
| 68 | }) | |