Files
wei-shaw--claude-relay-service/tests/requestDetailsRoute.test.js
T
2026-07-13 12:48:39 +08:00

142 lines
3.8 KiB
JavaScript

const mockRouter = {
get: jest.fn(),
post: jest.fn()
}
jest.mock(
'express',
() => ({
Router: () => mockRouter
}),
{ virtual: true }
)
jest.mock('../src/middleware/auth', () => ({
authenticateAdmin: jest.fn((_req, _res, next) => next())
}))
jest.mock('../src/services/requestDetailService', () => ({
listRequestDetails: jest.fn(),
getRequestDetail: jest.fn(),
getRequestBodyPreviewStats: jest.fn(),
purgeRequestBodySnapshots: jest.fn()
}))
jest.mock('../src/utils/logger', () => ({
error: jest.fn(),
warn: jest.fn(),
info: jest.fn(),
debug: jest.fn(),
start: jest.fn()
}))
const requestDetailService = require('../src/services/requestDetailService')
require('../src/routes/admin/requestDetails')
function createResponse() {
const res = {
statusCode: 200,
body: null,
json: jest.fn((payload) => {
res.body = payload
return res
}),
status: jest.fn((code) => {
res.statusCode = code
return res
})
}
return res
}
function findGetHandler(path) {
const route = mockRouter.get.mock.calls.find((call) => call[0] === path)
return route?.[2]
}
function findPostHandler(path) {
const route = mockRouter.post.mock.calls.find((call) => call[0] === path)
return route?.[2]
}
describe('requestDetails admin routes', () => {
beforeEach(() => {
requestDetailService.listRequestDetails.mockReset()
requestDetailService.getRequestDetail.mockReset()
requestDetailService.getRequestBodyPreviewStats.mockReset()
requestDetailService.purgeRequestBodySnapshots.mockReset()
})
test('returns 400 for invalid request detail queries', async () => {
const error = new Error('Invalid date range')
error.statusCode = 400
requestDetailService.listRequestDetails.mockRejectedValue(error)
const handler = findGetHandler('/request-details')
const res = createResponse()
await handler({ query: { startDate: 'bad' } }, res)
expect(res.status).toHaveBeenCalledWith(400)
expect(res.body.success).toBe(false)
expect(res.body.message).toBe('Invalid date range')
})
test('returns retained detail records even when capture is disabled', async () => {
requestDetailService.getRequestDetail.mockResolvedValue({
captureEnabled: false,
retentionHours: 6,
record: {
requestId: 'req_1',
model: 'gpt-5.4'
}
})
const handler = findGetHandler('/request-details/:requestId')
const res = createResponse()
await handler({ params: { requestId: 'req_1' } }, res)
expect(res.status).not.toHaveBeenCalled()
expect(res.body.success).toBe(true)
expect(res.body.data.captureEnabled).toBe(false)
expect(res.body.data.record.requestId).toBe('req_1')
})
test('returns request body preview stats', async () => {
requestDetailService.getRequestBodyPreviewStats.mockResolvedValue({
captureEnabled: true,
retentionHours: 6,
bodyPreviewEnabled: false,
snapshotCount: 3,
hasSnapshots: true
})
const handler = findGetHandler('/request-details/body-preview-stats')
const res = createResponse()
await handler({}, res)
expect(res.status).not.toHaveBeenCalled()
expect(res.body.success).toBe(true)
expect(res.body.data.snapshotCount).toBe(3)
expect(res.body.data.hasSnapshots).toBe(true)
})
test('purges stored request body previews via dedicated route', async () => {
requestDetailService.purgeRequestBodySnapshots.mockResolvedValue({
updatedRecords: 7
})
const handler = findPostHandler('/request-details/body-preview-purge')
const res = createResponse()
await handler({}, res)
expect(res.status).not.toHaveBeenCalled()
expect(res.body.success).toBe(true)
expect(res.body.message).toBe('清理完毕')
expect(res.body.data.updatedRecords).toBe(7)
})
})