fix: address latest coderabbit review findings

This commit is contained in:
Cita
2026-03-23 15:24:00 +08:00
parent 426d9a538d
commit 764b9aa0e6
21 changed files with 472 additions and 46 deletions
+91
View File
@@ -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) {
+1 -1
View File
@@ -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;');
});
});