ReviewOS

also looking at this

stacks/system-cleaner

feat(orphans): scan Application Support and Containers

#29
Merged glennmichael123 wants to merge feat/orphan-scan-application-support into main
3 files +242 -19
tests/orphans.test.tsadded+68-0
Changes to tests/orphans.test.ts
@@ -0,0 +1,68 @@
1import { describe, expect, it } from 'bun:test'
2import { 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 */
13describe('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
36describe('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})