Skip to content

Commit 56b75ea

Browse files
test(privacy): prove category deletion stays scoped
1 parent 44dbcb3 commit 56b75ea

3 files changed

Lines changed: 42 additions & 7 deletions

File tree

docs/FUNCTIONAL_TEST_STRATEGY.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ really a test of a third-party framework or engine we merely depend on:
2828
75% branches / 77% functions / 79% lines on the TESTABLE surface (ratchet floor 77/74/76/78,
2929
climbing toward the 85% goal). ~1200 unit/integration tests.
3030
- **Swift, `npm run test:swift`:** 37 XCTest cases over the text-extractor pure classifiers.
31-
- **DB integration, `npm run test:db`:** 104 cases over core and Pro persistence against real
31+
- **DB integration, `npm run test:db`:** 105 cases over core and Pro persistence against real
3232
temp SQLite. Kept OUT of the default gate because it needs `better-sqlite3` rebuilt for the
3333
node ABI (the app builds it for Electron's ABI); the script rebuilds + restores. KNOWN GAP:
3434
wire `test:db` into CI as an isolated step so database coverage is enforced there.

docs/P0_P2_INTEGRATION_COVERAGE.md

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@ manual claim does not count as complete integration coverage.
88
## Current status - 2026-07-17
99

1010
- Scope: 155 journeys - 74 P0, 71 P1, and 10 P2.
11-
- Covered by direct integration or E2E evidence: 21 - 14 P0, 6 P1, and 1 P2.
12-
- Left: 134 - including partially covered journeys whose exact release behavior is not yet proven.
11+
- Covered by direct integration or E2E evidence: 22 - 15 P0, 6 P1, and 1 P2.
12+
- Left: 133 - including partially covered journeys whose exact release behavior is not yet proven.
1313
- Green gates today:
1414
- `npm test`: 203 files passed, 1 skipped; 2,190 tests passed, 1 skipped.
1515
- `npm run test:coverage`: 96.77% statements, 91.54% branches, 96.25% functions,
1616
97.55% lines.
17-
- `npm run test:db`: 13 files and 104 real SQLite integration tests passed; Electron ABI
17+
- `npm run test:db`: 13 files and 105 real SQLite integration tests passed; Electron ABI
1818
restored afterward.
1919
- `npm run test:e2e`: 22 Playwright Electron tests passed against fresh synthetic temp profiles.
2020
- Both TypeScript projects pass.
@@ -51,6 +51,9 @@ manual claim does not count as complete integration coverage.
5151
attachment persistence across lock and unlock.
5252
- #128 - Vault recovery and backup. `vault-service.test.ts` and `vault-recovery.test.ts` exercise
5353
real KDBX export bytes, recovery setup, wrong phrases, and recovery to a new password.
54+
- #135 - Delete category is scoped. The real SQLite/filesystem integration deletes the Chats
55+
category through `clearCategory` and proves memory, projects, connectors, encrypted tokens,
56+
models, and unrelated personal files remain.
5457
- #136 - Delete all is complete. Core and Pro DB integration tests seed projects, chats, memory,
5558
knowledge, connectors, encrypted tokens, profile data, every registered Pro table, and every
5659
personal-data directory. They run the real delete-all registry, close and reopen the encrypted
@@ -158,8 +161,8 @@ manual claim does not count as complete integration coverage.
158161

159162
- #129 - Runtime residency toggles persist; #130 - Chat residency stays required; #131 - Resource
160163
mode applies; #132 - Settings survive relaunch; #133 - Storage usage is truthful; #134 - Clear
161-
cache preserves user data; #135 - Delete category is scoped; #137 - Core locked Pro tabs; #138 -
162-
Pro license activates; #139 - Invalid or exhausted license fails
164+
cache preserves user data; #137 - Core locked Pro tabs; #138 - Pro license activates; #139 -
165+
Invalid or exhausted license fails
163166
clearly; #140 - Offline entitlement behavior; #141 - Core and Pro override behavior; #142 - Manual
164167
update check; #143 - Update channel persists.
165168

src/main/__tests__/data-privacy-delete-all.dbtest.ts

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ vi.mock('electron', () => ({
4242
vi.mock('@lancedb/lancedb', () => ({ connect: async () => ({}) }))
4343

4444
import * as dbmod from '../database'
45-
import { deleteAllData } from '../data-privacy'
45+
import { clearCategory, deleteAllData } from '../data-privacy'
4646
import { addConnector } from '../mcp'
4747
import { setSecret } from '../secrets'
4848
import { createProject, desktopVectorStore } from '../rag/store'
@@ -138,3 +138,35 @@ describe('deleteAllData — erases EVERY core personal store (D29/D30)', () => {
138138
expect(dbmod.getSetting('theme', '')).toBe('dark')
139139
})
140140
})
141+
142+
describe('clearCategory — erases only the selected personal-data category', () => {
143+
it('clears chats and uploads without touching memory, projects, credentials, or models', async () => {
144+
dbmod.createRagConversation('scoped-chat', 'Scoped chat', null)
145+
dbmod
146+
.getDB()
147+
.prepare('INSERT INTO memories (content, source_app) VALUES (?, ?)')
148+
.run('keep this memory', 'Notes')
149+
createProject({ id: 'keep-project', name: 'Keep project' })
150+
addConnector({ name: 'Keep connector', transport: 'http', url: 'https://mcp.example.com' })
151+
setSecret('keep:oauth:tokens', JSON.stringify({ access_token: 'keep-token' }))
152+
153+
const uploadPath = path.join(TMP_DIR, 'uploads', 'private.txt')
154+
const entityPhotoPath = path.join(TMP_DIR, 'entity-photos', 'keep.jpg')
155+
const modelPath = path.join(TMP_DIR, 'models', 'keep-after-scoped-delete.gguf')
156+
for (const file of [uploadPath, entityPhotoPath, modelPath]) {
157+
fs.mkdirSync(path.dirname(file), { recursive: true })
158+
fs.writeFileSync(file, `content for ${path.basename(file)}`)
159+
}
160+
161+
expect(await clearCategory('chats')).toEqual({ success: true })
162+
163+
expect(count('rag_conversations')).toBe(0)
164+
expect(fs.readdirSync(path.join(TMP_DIR, 'uploads'))).toEqual([])
165+
expect(count('memories')).toBeGreaterThan(0)
166+
expect(count('projects')).toBeGreaterThan(0)
167+
expect(count('connectors')).toBeGreaterThan(0)
168+
expect(count('secrets')).toBeGreaterThan(0)
169+
expect(fs.existsSync(entityPhotoPath)).toBe(true)
170+
expect(fs.existsSync(modelPath)).toBe(true)
171+
})
172+
})

0 commit comments

Comments
 (0)