forked from boos/BoosAPI
fix: address latest coderabbit review findings
This commit is contained in:
@@ -360,6 +360,97 @@ describe('DownstreamKeys page', () => {
|
||||
}
|
||||
});
|
||||
|
||||
it('clears stale drawer overview and trend data when switching to another key', async () => {
|
||||
apiMock.getDownstreamApiKeysSummary.mockResolvedValue({
|
||||
success: true,
|
||||
items: [
|
||||
buildSummaryItem(),
|
||||
buildSummaryItem({
|
||||
id: 2,
|
||||
name: 'batch-key',
|
||||
keyMasked: 'sk-b****0315',
|
||||
groupName: '项目B',
|
||||
rangeUsage: {
|
||||
totalRequests: 1,
|
||||
successRequests: 1,
|
||||
failedRequests: 0,
|
||||
successRate: 100,
|
||||
totalTokens: 12,
|
||||
totalCost: 0.01,
|
||||
},
|
||||
}),
|
||||
],
|
||||
});
|
||||
apiMock.getDownstreamApiKeys.mockResolvedValue({
|
||||
success: true,
|
||||
items: [
|
||||
buildRawItem(),
|
||||
buildRawItem({
|
||||
id: 2,
|
||||
name: 'batch-key',
|
||||
key: 'sk-batch-0315',
|
||||
keyMasked: 'sk-b****0315',
|
||||
groupName: '项目B',
|
||||
}),
|
||||
],
|
||||
});
|
||||
apiMock.getDownstreamApiKeyOverview
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
item: buildSummaryItem(),
|
||||
usage: {
|
||||
last24h: { totalRequests: 3, successRequests: 2, failedRequests: 1, successRate: 66.7, totalTokens: 4200, totalCost: 0.42 },
|
||||
last7d: { totalRequests: 9, successRequests: 8, failedRequests: 1, successRate: 88.9, totalTokens: 12400, totalCost: 1.24 },
|
||||
all: { totalRequests: 20, successRequests: 18, failedRequests: 2, successRate: 90, totalTokens: 55200, totalCost: 5.52 },
|
||||
},
|
||||
})
|
||||
.mockImplementationOnce(() => new Promise(() => {}));
|
||||
apiMock.getDownstreamApiKeyTrend
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
buckets: [
|
||||
{ startUtc: '2026-03-15T08:00:00.000Z', totalRequests: 2, totalTokens: 1200, totalCost: 0.12, successRate: 100 },
|
||||
{ startUtc: '2026-03-15T09:00:00.000Z', totalRequests: 1, totalTokens: 3000, totalCost: 0.3, successRate: 0 },
|
||||
],
|
||||
})
|
||||
.mockImplementationOnce(() => new Promise(() => {}));
|
||||
|
||||
let root: ReturnType<typeof create> | null = null;
|
||||
try {
|
||||
await act(async () => {
|
||||
root = create(
|
||||
<MemoryRouter initialEntries={['/downstream-keys']}>
|
||||
<ToastProvider>
|
||||
<DownstreamKeys />
|
||||
</ToastProvider>
|
||||
</MemoryRouter>,
|
||||
);
|
||||
});
|
||||
await flushMicrotasks();
|
||||
|
||||
const rows = root!.root.findAll((node) => node.type === 'tr' && typeof node.props.onClick === 'function');
|
||||
const smokeRow = rows.find((node) => collectText(node).includes('smoke-key'));
|
||||
const batchRow = rows.find((node) => collectText(node).includes('batch-key'));
|
||||
await act(async () => {
|
||||
smokeRow!.props.onClick();
|
||||
});
|
||||
await flushMicrotasks();
|
||||
|
||||
expect(collectText(root!.root)).toContain('固定窗口对比');
|
||||
expect(collectText(root!.root)).toContain('trend:2');
|
||||
|
||||
await act(async () => {
|
||||
batchRow!.props.onClick();
|
||||
});
|
||||
await flushMicrotasks();
|
||||
|
||||
expect(collectText(root!.root)).not.toContain('固定窗口对比');
|
||||
expect(collectText(root!.root)).not.toContain('trend:2');
|
||||
} finally {
|
||||
root?.unmount();
|
||||
}
|
||||
});
|
||||
|
||||
it('separates exact models from group routes in advanced config and uses single-column layout', async () => {
|
||||
let root: ReturnType<typeof create> | null = null;
|
||||
try {
|
||||
|
||||
@@ -49,6 +49,7 @@ export default function DownstreamKeyDrawer({
|
||||
useEffect(() => {
|
||||
if (!open || !item?.id) return;
|
||||
let cancelled = false;
|
||||
setOverview(null);
|
||||
setOverviewLoading(true);
|
||||
api.getDownstreamApiKeyOverview(item.id)
|
||||
.then((res: any) => {
|
||||
@@ -71,6 +72,7 @@ export default function DownstreamKeyDrawer({
|
||||
useEffect(() => {
|
||||
if (!open || !item?.id) return;
|
||||
let cancelled = false;
|
||||
setBuckets([]);
|
||||
setTrendLoading(true);
|
||||
api.getDownstreamApiKeyTrend(item.id, { range: trendRange })
|
||||
.then((res: any) => {
|
||||
|
||||
@@ -126,6 +126,7 @@ export default function ConversationComposer({
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => onRemoveConversationFile(file.localId)}
|
||||
aria-label={`移除附件 ${file.name || file.localId || '附件'}`}
|
||||
style={{
|
||||
border: 'none',
|
||||
background: 'transparent',
|
||||
@@ -148,6 +149,7 @@ export default function ConversationComposer({
|
||||
value={input}
|
||||
onChange={(event) => onInputChange(event.target.value)}
|
||||
onKeyDown={(event) => {
|
||||
if (event.nativeEvent.isComposing) return;
|
||||
if (event.key === 'Enter' && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
if (sending) {
|
||||
|
||||
@@ -15,7 +15,7 @@ type DebugPanelPresence = {
|
||||
type DebugPanelProps = {
|
||||
presence: DebugPanelPresence;
|
||||
isMobile: boolean;
|
||||
debugTimestamp: number | null;
|
||||
debugTimestamp: string | null;
|
||||
activeDebugTab: DebugTab;
|
||||
onTabChange: (tab: DebugTab) => void;
|
||||
debugTabContent: string;
|
||||
|
||||
@@ -9,4 +9,11 @@ describe('ModelTester conversation composer extraction', () => {
|
||||
expect(source).toContain("import ConversationComposer from './model-tester/ConversationComposer.js'");
|
||||
expect(source).not.toContain('添加文件');
|
||||
});
|
||||
|
||||
it('keeps accessibility and IME safeguards inside the dedicated composer component', () => {
|
||||
const source = readFileSync(resolve(process.cwd(), 'src/web/pages/model-tester/ConversationComposer.tsx'), 'utf8').replace(/\r\n/g, '\n');
|
||||
|
||||
expect(source).toContain('aria-label={`移除附件');
|
||||
expect(source).toContain('event.nativeEvent.isComposing');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -9,4 +9,10 @@ describe('ModelTester debug panel extraction', () => {
|
||||
expect(source).toContain("import DebugPanel from './model-tester/DebugPanel.js'");
|
||||
expect(source).not.toContain('时间线');
|
||||
});
|
||||
|
||||
it('keeps the debug timestamp prop aligned with the ISO string state used by ModelTester', () => {
|
||||
const source = readFileSync(resolve(process.cwd(), 'src/web/pages/model-tester/DebugPanel.tsx'), 'utf8').replace(/\r\n/g, '\n');
|
||||
|
||||
expect(source).toContain('debugTimestamp: string | null;');
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user