openpencil/tests/engine/app/settings/credential-migration.test.ts
Danila Poyarkov 95a920197e feat(app): add cross-platform credential storage
- Store desktop secrets in native OS credential services through a narrow Tauri bridge

- Provide session-only and encrypted remembered browser stores with explicit switching

- Migrate legacy plaintext keys only after verified writes and cover failure recovery
2026-07-26 14:07:26 +03:00

69 lines
2.2 KiB
TypeScript

import { describe, expect, test } from 'bun:test'
import { MemoryCredentialStore } from '@/app/settings/credentials/memory'
import {
migrateLegacyCredentials,
PEXELS_CREDENTIAL,
providerCredentialRef
} from '@/app/settings/credentials/migration'
class TestStorage implements Storage {
readonly #values = new Map<string, string>()
get length(): number {
return this.#values.size
}
clear(): void {
this.#values.clear()
}
getItem(key: string): string | null {
return this.#values.get(key) ?? null
}
key(index: number): string | null {
return [...this.#values.keys()][index] ?? null
}
removeItem(key: string): void {
this.#values.delete(key)
}
setItem(key: string, value: string): void {
this.#values.set(key, value)
}
}
class UnavailableCredentialStore extends MemoryCredentialStore {
override async availability() {
return 'unavailable' as const
}
}
describe('legacy credential migration', () => {
test('verifies encrypted destinations before removing plaintext keys', async () => {
const storage = new TestStorage()
const store = new MemoryCredentialStore()
storage.setItem('open-pencil:openrouter-api-key', 'old-openrouter-key')
storage.setItem('open-pencil:ai-key:anthropic', 'anthropic-key')
storage.setItem('open-pencil:pexels-api-key', 'pexels-key')
expect(await migrateLegacyCredentials(storage, store)).toBeTrue()
expect(await store.read(providerCredentialRef('openrouter'))).toBe('old-openrouter-key')
expect(await store.read(providerCredentialRef('anthropic'))).toBe('anthropic-key')
expect(await store.read(PEXELS_CREDENTIAL)).toBe('pexels-key')
expect(storage.getItem('open-pencil:openrouter-api-key')).toBeNull()
expect(storage.getItem('open-pencil:ai-key:anthropic')).toBeNull()
expect(storage.getItem('open-pencil:pexels-api-key')).toBeNull()
})
test('retains plaintext when the destination is unavailable', async () => {
const storage = new TestStorage()
storage.setItem('open-pencil:ai-key:openrouter', 'keep-me')
expect(await migrateLegacyCredentials(storage, new UnavailableCredentialStore())).toBeFalse()
expect(storage.getItem('open-pencil:ai-key:openrouter')).toBe('keep-me')
})
})