Files
orca/src/main/ipc/opencode-go-credentials.test.ts
T
53f9ea7839 fix(opencode): keep Go credentials private and resolve backend keys (#24615)
Preserve the complete credential storage, migration, IPC, Settings and rate-limit refresh change alongside standalone GLM plans, current-main database diagnostics and the reviewed unknown-backend environment correction. Keep native discovery cancellation third and selected environment fourth.

Original-topic-commit: 7903f1cddb
Original-topic-commit: 588117b5cf
Original-topic-commit: dedd4f8c86
Original-topic-commit: 6645dae104
Original-topic-commit: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-from: a25b80c02c1af7830b0e6a65e72d965b3ad98276
Restacked-onto: b032867021

Co-authored-by: kespineira <kespineira@users.noreply.github.com>
Co-authored-by: kevimux <kevimux@users.noreply.github.com>
Reported-by: pullfrog
Reviewed-full-source: 849fe093073f4c1606bd65d79a0c725d955d0d1f
Native-helper-source: 80dbe23237


Reviewed-full-current-source: 36acb57d44adb3d378c0289c8c15f7da0fda214c
2026-10-02 23:21:27 -07:00

88 lines
3.1 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest'
import { createEmptyRateLimitState } from '../../shared/rate-limit-state-factory'
import { registerOpenCodeGoCredentialsHandlers } from './opencode-go-credentials'
const mocks = vi.hoisted(() => ({
handlers: new Map<string, (event: unknown, key?: unknown) => unknown>(),
has: vi.fn(() => false),
save: vi.fn(),
clear: vi.fn()
}))
vi.mock('electron', () => ({
ipcMain: {
handle: (channel: string, handler: (event: unknown, key?: unknown) => unknown) => {
mocks.handlers.set(channel, handler)
}
}
}))
vi.mock('../opencode/opencode-go-api-key-store', () => ({
hasOpenCodeGoApiKey: mocks.has,
saveOpenCodeGoApiKey: mocks.save,
clearOpenCodeGoApiKey: mocks.clear
}))
function invoke(action: string, value?: unknown): unknown {
const handler = mocks.handlers.get(`opencodeGoCredentials:${action}`)
if (!handler) {
throw new Error('Missing credential handler')
}
return handler({}, value)
}
beforeEach(() => {
vi.resetAllMocks()
mocks.handlers.clear()
})
describe('OpenCode Go write-only credentials', () => {
it('returns only boolean status and provides no key reader', () => {
registerOpenCodeGoCredentialsHandlers(null)
mocks.has.mockReturnValue(true)
expect(invoke('getStatus')).toEqual({ apiKeyConfigured: true })
expect([...mocks.handlers.keys()]).toEqual([
'opencodeGoCredentials:getStatus',
'opencodeGoCredentials:saveApiKey',
'opencodeGoCredentials:clearApiKey'
])
})
it.each(['saveApiKey', 'clearApiKey'])('invalidates before refreshing on %s', (action) => {
const invalidate = vi.fn()
const refresh = vi.fn(async () => {
expect(invalidate).toHaveBeenCalledOnce()
return createEmptyRateLimitState()
})
registerOpenCodeGoCredentialsHandlers({
invalidateOpenCodeGoCredentialState: invalidate,
refresh
})
mocks.has.mockReturnValue(action === 'saveApiKey')
expect(invoke(action, 'fake-key')).toEqual({ apiKeyConfigured: action === 'saveApiKey' })
expect(action === 'saveApiKey' ? mocks.save : mocks.clear).toHaveBeenCalledOnce()
expect(refresh).toHaveBeenCalledOnce()
// Why: only clearing the saved key may hide the chip; another key source can still exist after a save.
expect(invalidate).toHaveBeenCalledWith({ apiKeyCleared: action === 'clearApiKey' })
})
it.each([null, undefined, 42, {}])('rejects a non-string key', (key) => {
registerOpenCodeGoCredentialsHandlers(null)
expect(() => invoke('saveApiKey', key)).toThrow('OpenCode Go API key must be a string')
expect(mocks.save).not.toHaveBeenCalled()
})
it('does not refresh after a failed save', () => {
const invalidate = vi.fn()
const refresh = vi.fn()
registerOpenCodeGoCredentialsHandlers({
invalidateOpenCodeGoCredentialState: invalidate,
refresh
})
mocks.save.mockImplementation(() => {
throw new Error('Could not save credential')
})
expect(() => invoke('saveApiKey', 'fake-key')).toThrow('Could not save credential')
expect(invalidate).not.toHaveBeenCalled()
expect(refresh).not.toHaveBeenCalled()
})
})