mirror of
https://github.com/fawney19/Aether.git
synced 2026-10-08 18:37:46 +08:00
feat(usage): enrich audit metadata and detail views
This commit is contained in:
@@ -0,0 +1,38 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { getMock } = vi.hoisted(() => ({ getMock: vi.fn() }))
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
default: {
|
||||
get: getMock,
|
||||
},
|
||||
}))
|
||||
|
||||
import { getProviderKeysPage } from '@/api/endpoints/keys'
|
||||
|
||||
describe('getProviderKeysPage', () => {
|
||||
beforeEach(() => {
|
||||
getMock.mockReset()
|
||||
})
|
||||
|
||||
it('normalizes a legacy array response for the provider drawer', async () => {
|
||||
getMock.mockResolvedValue({
|
||||
data: [{ id: 'key-1' }, { id: 'key-2' }],
|
||||
})
|
||||
|
||||
const result = await getProviderKeysPage('provider-demo', { page: 1, page_size: 1 })
|
||||
|
||||
expect(result).toMatchObject({ total: 2, page: 1, page_size: 1 })
|
||||
expect(result.keys).toEqual([{ id: 'key-1' }])
|
||||
})
|
||||
|
||||
it('normalizes a malformed object without exposing a non-array keys field', async () => {
|
||||
getMock.mockResolvedValue({
|
||||
data: { total: null, page: null, page_size: null, keys: {} },
|
||||
})
|
||||
|
||||
const result = await getProviderKeysPage('provider-demo', { page: 2, page_size: 3 })
|
||||
|
||||
expect(result).toEqual({ total: 0, page: 2, page_size: 3, keys: [] })
|
||||
})
|
||||
})
|
||||
@@ -0,0 +1,101 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { getMock } = vi.hoisted(() => ({ getMock: vi.fn() }))
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
default: {
|
||||
get: getMock,
|
||||
},
|
||||
}))
|
||||
|
||||
import { getProviderMappingPreview, getProvidersSummary } from '@/api/endpoints/providers'
|
||||
|
||||
const provider = {
|
||||
id: 'provider-1',
|
||||
name: 'Provider 1',
|
||||
provider_type: 'openai',
|
||||
is_active: true,
|
||||
endpoints: [],
|
||||
}
|
||||
|
||||
describe('getProvidersSummary', () => {
|
||||
beforeEach(() => {
|
||||
getMock.mockReset()
|
||||
})
|
||||
|
||||
it('normalizes the paginated summary response', async () => {
|
||||
getMock.mockResolvedValue({
|
||||
data: { total: 1, page: 1, page_size: 20, items: [provider] },
|
||||
})
|
||||
|
||||
const result = await getProvidersSummary({ page: 1, page_size: 20, search: 'paged' })
|
||||
|
||||
expect(result.total).toBe(1)
|
||||
expect(result.items).toHaveLength(1)
|
||||
expect(result.items[0]?.kiro_simulated_cache_enabled).toBe(false)
|
||||
})
|
||||
|
||||
it('supports the legacy array response without reading an undefined items field', async () => {
|
||||
getMock.mockResolvedValue({ data: [provider] })
|
||||
|
||||
const result = await getProvidersSummary({ page: 2, page_size: 20, search: 'legacy' })
|
||||
|
||||
expect(result).toMatchObject({ total: 1, page: 2, page_size: 20 })
|
||||
expect(result.items).toHaveLength(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('getProviderMappingPreview', () => {
|
||||
beforeEach(() => {
|
||||
getMock.mockReset()
|
||||
})
|
||||
|
||||
it('normalizes a non-contract response instead of exposing missing arrays to the UI', async () => {
|
||||
getMock.mockResolvedValue({
|
||||
data: { message: '演示模式:该接口暂未模拟', demo_mode: true },
|
||||
})
|
||||
|
||||
const result = await getProviderMappingPreview('provider-demo')
|
||||
|
||||
expect(result).toEqual({
|
||||
provider_id: 'provider-demo',
|
||||
provider_name: '',
|
||||
keys: [],
|
||||
total_keys: 0,
|
||||
total_matches: 0,
|
||||
truncated: false,
|
||||
truncated_keys: 0,
|
||||
truncated_models: 0,
|
||||
})
|
||||
})
|
||||
|
||||
it('normalizes missing nested mapping arrays', async () => {
|
||||
getMock.mockResolvedValue({
|
||||
data: {
|
||||
provider_id: 'provider-nested',
|
||||
provider_name: 'Nested Provider',
|
||||
keys: [{
|
||||
key_id: 'key-1',
|
||||
key_name: 'Primary',
|
||||
masked_key: 'sk-***',
|
||||
is_active: true,
|
||||
allowed_models: null,
|
||||
matching_global_models: [{
|
||||
global_model_id: 'model-1',
|
||||
global_model_name: 'gpt-5',
|
||||
display_name: 'GPT-5',
|
||||
is_active: true,
|
||||
matched_models: null,
|
||||
}],
|
||||
}],
|
||||
},
|
||||
})
|
||||
|
||||
const result = await getProviderMappingPreview('provider-nested')
|
||||
|
||||
expect(result.keys[0]?.allowed_models).toEqual([])
|
||||
expect(result.keys[0]?.matching_global_models[0]?.matched_models).toEqual([])
|
||||
expect(result.total_keys).toBe(1)
|
||||
expect(result.total_matches).toBe(1)
|
||||
})
|
||||
})
|
||||
@@ -1,13 +1,15 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest'
|
||||
|
||||
const { getMock, cachedRequestMock } = vi.hoisted(() => ({
|
||||
const { getMock, postMock, cachedRequestMock } = vi.hoisted(() => ({
|
||||
getMock: vi.fn(),
|
||||
postMock: vi.fn(),
|
||||
cachedRequestMock: vi.fn(async (_key: string, fn: () => Promise<unknown>) => fn()),
|
||||
}))
|
||||
|
||||
vi.mock('@/api/client', () => ({
|
||||
default: {
|
||||
get: getMock,
|
||||
post: postMock,
|
||||
},
|
||||
}))
|
||||
|
||||
@@ -20,6 +22,7 @@ import { usersApi } from '@/api/users'
|
||||
describe('usersApi admin list query', () => {
|
||||
beforeEach(() => {
|
||||
getMock.mockReset()
|
||||
postMock.mockReset()
|
||||
cachedRequestMock.mockClear()
|
||||
getMock.mockResolvedValue({
|
||||
data: {
|
||||
@@ -49,4 +52,55 @@ describe('usersApi admin list query', () => {
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
it('keeps user management renderable when the group response has no items array', async () => {
|
||||
getMock.mockResolvedValueOnce({
|
||||
data: {
|
||||
message: '演示模式:该接口暂未模拟',
|
||||
demo_mode: true,
|
||||
},
|
||||
})
|
||||
|
||||
await expect(usersApi.listUserGroups()).resolves.toEqual({
|
||||
message: '演示模式:该接口暂未模拟',
|
||||
demo_mode: true,
|
||||
items: [],
|
||||
})
|
||||
})
|
||||
|
||||
it('creates a managed key through the selected target user route', async () => {
|
||||
postMock.mockResolvedValueOnce({
|
||||
data: {
|
||||
id: 'target-key',
|
||||
key: 'sk-target',
|
||||
},
|
||||
})
|
||||
const payload = {
|
||||
name: 'target key',
|
||||
feature_settings: {
|
||||
chat_pii_redaction: { enabled: true },
|
||||
},
|
||||
}
|
||||
|
||||
await usersApi.createApiKey('target-user', payload)
|
||||
|
||||
expect(postMock).toHaveBeenCalledWith(
|
||||
'/api/admin/users/target-user/api-keys',
|
||||
payload,
|
||||
)
|
||||
})
|
||||
|
||||
it('reads managed keys from the production api_keys envelope', async () => {
|
||||
getMock.mockResolvedValueOnce({
|
||||
data: {
|
||||
api_keys: [{ id: 'target-key', created_at: '2026-07-17T00:00:00Z' }],
|
||||
total: 1,
|
||||
},
|
||||
})
|
||||
|
||||
await expect(usersApi.getUserApiKeys('target-user')).resolves.toEqual([
|
||||
{ id: 'target-key', created_at: '2026-07-17T00:00:00Z' },
|
||||
])
|
||||
expect(getMock).toHaveBeenCalledWith('/api/admin/users/target-user/api-keys')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user