Skip to content

Commit 504bce3

Browse files
msujawsclaude
andcommitted
fix(test): use constructor injection for key material in tests
Replace navigator mocking with constructor parameter for key derivation. This avoids environment-specific issues with jsdom's navigator object in CI while maintaining the same encryption/decryption behavior. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
1 parent a36f81c commit 504bce3

File tree

2 files changed

+14
-16
lines changed

2 files changed

+14
-16
lines changed

src/lib/storage/api-key-storage.test.ts

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { ApiKeyStorage } from './api-key-storage'
44
describe('ApiKeyStorage', () => {
55
let storage: ApiKeyStorage
66
let localStorageMock: Record<string, string>
7-
const mockUserAgent = 'Mozilla/5.0 (Test) TestBrowser/1.0'
7+
const testKeyMaterial = 'test-key-material-for-encryption'
88

99
beforeEach(() => {
1010
// Mock localStorage
@@ -27,19 +27,8 @@ describe('ApiKeyStorage', () => {
2727
key: vi.fn(),
2828
})
2929

30-
// Mock navigator.userAgent for consistent encryption key derivation
31-
// Use Object.create to preserve the navigator prototype chain for jsdom in CI
32-
const navigatorMock = Object.create(
33-
Object.getPrototypeOf(globalThis.navigator),
34-
Object.getOwnPropertyDescriptors(globalThis.navigator),
35-
)
36-
Object.defineProperty(navigatorMock, 'userAgent', {
37-
value: mockUserAgent,
38-
configurable: true,
39-
})
40-
vi.stubGlobal('navigator', navigatorMock)
41-
42-
storage = new ApiKeyStorage()
30+
// Use constructor parameter for consistent key derivation in tests
31+
storage = new ApiKeyStorage(testKeyMaterial)
4332
})
4433

4534
afterEach(() => {

src/lib/storage/api-key-storage.ts

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,24 @@ interface EncryptedData {
1313
export class ApiKeyStorage {
1414
private encoder = new TextEncoder()
1515
private decoder = new TextDecoder()
16+
private keyMaterialOverride?: string
17+
18+
/**
19+
* @param keyMaterialOverride - Optional override for key derivation (for testing only)
20+
*/
21+
constructor(keyMaterialOverride?: string) {
22+
this.keyMaterialOverride = keyMaterialOverride
23+
}
1624

1725
/**
1826
* Derive a crypto key from the user agent string
1927
* This provides basic obfuscation (not cryptographically secure against determined attackers)
2028
* but protects against casual inspection of localStorage
2129
*/
2230
private async deriveCryptoKey(): Promise<CryptoKey> {
23-
// Use user agent as key material (stable across sessions)
24-
const keyMaterial = this.encoder.encode(navigator.userAgent + 'bugzilla-kanban-salt')
31+
// Use user agent as key material (stable across sessions), or override for testing
32+
const keySource = this.keyMaterialOverride ?? navigator.userAgent
33+
const keyMaterial = this.encoder.encode(keySource + 'bugzilla-kanban-salt')
2534

2635
// Import as raw key material
2736
const importedKey = await crypto.subtle.importKey(

0 commit comments

Comments
 (0)