Newer
Older
navi-1 / webclient / tests / unit / stores / mcpKeys.test.js
import { describe, it, expect, vi, beforeEach } from 'vitest'
import { createPinia, setActivePinia } from 'pinia'
import { useMcpKeysStore } from '@/stores/mcpKeys.js'

vi.mock('@/api/index.js', () => ({
  getMcpKeys: vi.fn(),
  saveMcpKey: vi.fn(),
  resetMcpKey: vi.fn(),
}))

import * as api from '@/api/index.js'

const ITEMS = [
  {
    server_name: 'http-server',
    transport: 'streamable_http',
    key_type: 'header',
    key_location: 'Authorization',
    prefix: 'Bearer ',
    has_key: false,
    updated_at: null,
  },
  {
    server_name: 'stdio-server',
    transport: 'stdio',
    key_type: 'env',
    key_location: 'API_KEY',
    prefix: null,
    has_key: true,
    updated_at: '2026-10-07T12:00:00Z',
  },
]

describe('MCP keys store', () => {
  beforeEach(() => {
    setActivePinia(createPinia())
    vi.clearAllMocks()
    // Fresh clone per test — the store mutates fetched items in place.
    api.getMcpKeys.mockImplementation(async () => ({ items: structuredClone(ITEMS) }))
  })

  it('fetchServers GETs and unpacks the items array', async () => {
    api.getMcpKeys.mockImplementation(async () => ({ items: structuredClone(ITEMS) }))
    const store = useMcpKeysStore()
    await store.fetchServers()
    expect(api.getMcpKeys).toHaveBeenCalledTimes(1)
    expect(store.servers).toEqual(ITEMS)
    expect(store.loading).toBe(false)
  })

  it('fetchServers handles an empty list', async () => {
    api.getMcpKeys.mockResolvedValue({ items: [] })
    const store = useMcpKeysStore()
    await store.fetchServers()
    expect(store.servers).toEqual([])
  })

  it('saveKey PUTs and patches the matching server entry', async () => {
    api.getMcpKeys.mockImplementation(async () => ({ items: structuredClone(ITEMS) }))
    api.saveMcpKey.mockResolvedValue({
      server_name: 'http-server',
      has_key: true,
      updated_at: '2026-10-08T00:00:00Z',
    })
    const store = useMcpKeysStore()
    await store.fetchServers()

    await store.saveKey('http-server', 'sk-user')
    expect(api.saveMcpKey).toHaveBeenCalledWith('http-server', 'sk-user')
    expect(store.servers[0].has_key).toBe(true)
    expect(store.servers[0].updated_at).toBe('2026-10-08T00:00:00Z')
    expect(store.savingServer).toBe(null)
  })

  it('saveKey rethrows on failure and clears savingServer', async () => {
    api.getMcpKeys.mockImplementation(async () => ({ items: structuredClone(ITEMS) }))
    api.saveMcpKey.mockRejectedValue(new Error('500'))
    const store = useMcpKeysStore()
    await store.fetchServers()

    await expect(store.saveKey('http-server', 'sk-user')).rejects.toThrow('500')
    expect(store.servers[0].has_key).toBe(false)
    expect(store.savingServer).toBe(null)
  })

  it('resetKey DELETEs and clears the matching entry', async () => {
    api.getMcpKeys.mockImplementation(async () => ({ items: structuredClone(ITEMS) }))
    api.resetMcpKey.mockResolvedValue(undefined)
    const store = useMcpKeysStore()
    await store.fetchServers()

    await store.resetKey('stdio-server')
    expect(api.resetMcpKey).toHaveBeenCalledWith('stdio-server')
    expect(store.servers[1].has_key).toBe(false)
    expect(store.servers[1].updated_at).toBe(null)
    expect(store.savingServer).toBe(null)
  })
})