dify/cli/src/store/manager.test.ts
Stephen Zhou a84c2d36a3
style: format with vp fmt (#38803)
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
2026-07-12 15:57:46 +00:00

119 lines
3.8 KiB
TypeScript

import type { TokenStore } from './token-store'
import { describe, expect, it, vi } from 'vitest'
import { detectTokenStore, getTokenStore } from './manager'
function memStore(label: string): TokenStore & { _label: string } {
const map = new Map<string, string>()
const k = (h: string, e: string): string => `${h} ${e}`
return {
_label: label,
async read(host: string, email: string): Promise<string> {
return map.get(k(host, email)) ?? ''
},
async write(host: string, email: string, bearer: string): Promise<void> {
map.set(k(host, email), bearer)
},
async remove(host: string, email: string): Promise<void> {
map.delete(k(host, email))
},
}
}
describe('detectTokenStore', () => {
it('returns keychain store when probe succeeds', async () => {
const k = memStore('keyring')
const f = memStore('file')
const result = await detectTokenStore({
factory: { keyring: () => k, file: () => f },
})
expect(result.mode).toBe('keychain')
expect(result.store).toBe(k)
})
it('falls back to file when keyring set throws', async () => {
const k = memStore('keyring')
const f = memStore('file')
k.write = vi.fn(() => {
throw new Error('locked')
}) as TokenStore['write']
const result = await detectTokenStore({
factory: { keyring: () => k, file: () => f },
})
expect(result.mode).toBe('file')
expect(result.store).toBe(f)
})
it('falls back to file when probe round-trip mismatches', async () => {
const k = memStore('keyring')
const f = memStore('file')
k.read = vi.fn(async () => 'something-else') as TokenStore['read']
const result = await detectTokenStore({
factory: { keyring: () => k, file: () => f },
})
expect(result.mode).toBe('file')
expect(result.store).toBe(f)
})
it('falls back to file when keyring constructor throws', async () => {
const f = memStore('file')
const result = await detectTokenStore({
factory: {
keyring: () => {
throw new Error('no backend')
},
file: () => f,
},
})
expect(result.mode).toBe('file')
expect(result.store).toBe(f)
})
it('cleans up probe entry after successful probe', async () => {
const k = memStore('keyring')
const f = memStore('file')
await detectTokenStore({
factory: { keyring: () => k, file: () => f },
})
expect(await k.read('__difyctl_probe__', '__difyctl_probe__')).toBe('')
})
it('removes the probe entry even when the probe read throws', async () => {
const k = memStore('keyring')
const f = memStore('file')
const removeSpy = vi.spyOn(k, 'remove')
k.read = vi.fn(() => {
throw new Error('read boom')
}) as TokenStore['read']
const result = await detectTokenStore({
factory: { keyring: () => k, file: () => f },
})
expect(removeSpy).toHaveBeenCalledWith('__difyctl_probe__', '__difyctl_probe__')
expect(result.mode).toBe('file')
expect(result.store).toBe(f)
})
})
describe('getTokenStore', () => {
it('constructs the keychain backend without probing when mode is keychain', () => {
const k = memStore('keyring')
const f = memStore('file')
k.write = vi.fn(() => {
throw new Error('probe must never run on the read path')
}) as TokenStore['write']
const store = getTokenStore('keychain', {
factory: { keyring: () => k, file: () => f },
})
expect(store).toBe(k)
})
it('constructs the file backend when mode is file, never touching the keyring', () => {
const keyringFactory = vi.fn(() => memStore('keyring'))
const f = memStore('file')
const store = getTokenStore('file', {
factory: { keyring: keyringFactory, file: () => f },
})
expect(store).toBe(f)
expect(keyringFactory).not.toHaveBeenCalled()
})
})