chore: 移除 ultrareview preflight stub 及其测试

- 删除 src/services/api/ultrareviewPreflight.ts(自动生成的 stub)
- 删除 src/commands/review/UltrareviewPreflightDialog.tsx(依赖前者的 UI stub)
- 删除 src/services/api/__tests__/ultrareviewPreflight.test.ts(测试已删代码)
- 同步移除 ultrareviewCommand.test.tsx 中对 UltrareviewPreflightDialog 的 mock

Co-Authored-By: glm-5.2 <zai-org@claude-code-best.win>
This commit is contained in:
claude-code-best
2026-06-20 11:37:22 +08:00
parent 44bcd51500
commit e252c5e8b4
4 changed files with 1 additions and 367 deletions

View File

@@ -1,56 +0,0 @@
import React, { useCallback, useRef, useState } from 'react';
import { Box, Dialog, Text } from '@anthropic/ink';
import { Select } from '../../components/CustomSelect/select.js';
type Props = {
billingNote: string | null;
onConfirm: (signal: AbortSignal) => Promise<void>;
onCancel: () => void;
};
/**
* Dialog shown when /v1/ultrareview/preflight returns action='confirm'.
* Displays the server-provided billing_note (or a generic fallback) and
* gives the user a Proceed / Cancel choice.
*/
export function UltrareviewPreflightDialog({ billingNote, onConfirm, onCancel }: Props): React.ReactNode {
const [isLaunching, setIsLaunching] = useState(false);
const abortControllerRef = useRef(new AbortController());
const handleSelect = useCallback(
(value: string) => {
if (value === 'proceed') {
setIsLaunching(true);
void onConfirm(abortControllerRef.current.signal).catch(() => setIsLaunching(false));
} else {
onCancel();
}
},
[onConfirm, onCancel],
);
const handleCancel = useCallback(() => {
abortControllerRef.current.abort();
onCancel();
}, [onCancel]);
const options = [
{ label: 'Proceed', value: 'proceed' },
{ label: 'Cancel', value: 'cancel' },
];
const displayNote = billingNote ?? 'This run may incur additional cost.';
return (
<Dialog title="Ultrareview — additional cost" onCancel={handleCancel} color="background">
<Box flexDirection="column" gap={1}>
<Text>{displayNote}</Text>
{isLaunching ? (
<Text color="background">Launching</Text>
) : (
<Select options={options} onChange={handleSelect} onCancel={handleCancel} />
)}
</Box>
</Dialog>
);
}

View File

@@ -179,13 +179,10 @@ mock.module('src/components/CustomSelect/select.js', () => ({
Select: 'Select',
}));
// UltrareviewOverageDialog and PreflightDialog — return a simple marker
// UltrareviewOverageDialog — return a simple marker
mock.module('src/commands/review/UltrareviewOverageDialog.js', () => ({
UltrareviewOverageDialog: () => ({ type: 'UltrareviewOverageDialog' }),
}));
mock.module('src/commands/review/UltrareviewPreflightDialog.js', () => ({
UltrareviewPreflightDialog: () => ({ type: 'UltrareviewPreflightDialog' }),
}));
import { call } from '../ultrareviewCommand.js';

View File

@@ -1,226 +0,0 @@
/**
* Regression tests for fetchUltrareviewPreflight.
* Verifies all three action enum states (proceed/confirm/blocked),
* network/HTTP error handling, and Zod schema mismatch fallback.
*/
import { afterAll, beforeAll, describe, expect, mock, test } from 'bun:test'
import { debugMock } from '../../../../tests/mocks/debug.js'
import { logMock } from '../../../../tests/mocks/log.js'
import { setupAxiosMock } from '../../../../tests/mocks/axios.js'
// Mock dependency chain before any subject import
mock.module('src/utils/debug.ts', debugMock)
mock.module('src/utils/log.ts', logMock)
mock.module('src/services/analytics/index.js', () => ({
logEvent: () => {},
}))
// Mock auth utilities
mock.module('src/utils/auth.js', () => ({
isClaudeAISubscriber: () => true,
isTeamSubscriber: () => false,
isEnterpriseSubscriber: () => false,
}))
// Mock OAuth config
mock.module('src/constants/oauth.js', () => ({
getOauthConfig: () => ({ BASE_API_URL: 'https://api.anthropic.com' }),
}))
// Mock prepareApiRequest and getOAuthHeaders
mock.module('src/utils/teleport/api.js', () => ({
prepareApiRequest: async () => ({
accessToken: 'test-token',
orgUUID: 'org-uuid-test',
}),
getOAuthHeaders: (token: string) => ({
Authorization: `Bearer ${token}`,
'Content-Type': 'application/json',
'anthropic-version': '2023-06-01',
}),
}))
// We'll mock axios at module level.
// Typed as any in test code (CLAUDE.md: mock data may use as any).
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const mockAxiosPost = mock(async (..._args: any[]): Promise<any> => {
throw new Error('not configured')
})
const axiosHandle = setupAxiosMock()
axiosHandle.stubs.post = mockAxiosPost
axiosHandle.stubs.isAxiosError = (e: unknown) =>
typeof e === 'object' &&
e !== null &&
(e as { isAxiosError?: boolean }).isAxiosError === true
beforeAll(() => {
axiosHandle.useStubs = true
})
afterAll(() => {
axiosHandle.useStubs = false
})
import {
fetchUltrareviewPreflight,
type UltrareviewPreflightResponse,
} from '../ultrareviewPreflight.js'
describe('fetchUltrareviewPreflight', () => {
test('returns proceed action when server responds with proceed', async () => {
const serverResponse: UltrareviewPreflightResponse = {
action: 'proceed',
billing_note: null,
}
mockAxiosPost.mockImplementationOnce(async () => ({
status: 200,
data: serverResponse,
}))
const result = await fetchUltrareviewPreflight({ repo: 'owner/repo' })
expect(result).not.toBeNull()
expect(result?.action).toBe('proceed')
expect(result?.billing_note).toBeNull()
})
test('returns confirm action with billing_note when server responds with confirm', async () => {
const serverResponse: UltrareviewPreflightResponse = {
action: 'confirm',
billing_note: 'This run will cost approximately $2.50.',
}
mockAxiosPost.mockImplementationOnce(async () => ({
status: 200,
data: serverResponse,
}))
const result = await fetchUltrareviewPreflight({ repo: 'owner/repo' })
expect(result).not.toBeNull()
expect(result?.action).toBe('confirm')
expect(result?.billing_note).toBe('This run will cost approximately $2.50.')
})
test('returns blocked action when server responds with blocked', async () => {
const serverResponse: UltrareviewPreflightResponse = {
action: 'blocked',
billing_note: null,
}
mockAxiosPost.mockImplementationOnce(async () => ({
status: 200,
data: serverResponse,
}))
const result = await fetchUltrareviewPreflight({ repo: 'owner/repo' })
expect(result).not.toBeNull()
expect(result?.action).toBe('blocked')
})
test('returns null on schema mismatch (invalid action value)', async () => {
mockAxiosPost.mockImplementationOnce(async () => ({
status: 200,
data: { action: 'unknown_action', billing_note: null },
}))
const result = await fetchUltrareviewPreflight({ repo: 'owner/repo' })
expect(result).toBeNull()
})
test('returns null on network error (no response)', async () => {
const networkError = new Error('ECONNREFUSED')
;(networkError as unknown as { isAxiosError: boolean }).isAxiosError = true
mockAxiosPost.mockImplementationOnce(async () => {
throw networkError
})
const result = await fetchUltrareviewPreflight({ repo: 'owner/repo' })
expect(result).toBeNull()
})
test('returns null on 401 Unauthorized', async () => {
const authError = new Error('Unauthorized')
;(
authError as unknown as {
isAxiosError: boolean
response: { status: number }
}
).isAxiosError = true
;(authError as unknown as { response: { status: number } }).response = {
status: 401,
}
mockAxiosPost.mockImplementationOnce(async () => {
throw authError
})
const result = await fetchUltrareviewPreflight({ repo: 'owner/repo' })
expect(result).toBeNull()
})
test('returns null on 403 Forbidden', async () => {
const forbiddenError = new Error('Forbidden')
;(
forbiddenError as unknown as {
isAxiosError: boolean
response: { status: number }
}
).isAxiosError = true
;(forbiddenError as unknown as { response: { status: number } }).response =
{ status: 403 }
mockAxiosPost.mockImplementationOnce(async () => {
throw forbiddenError
})
const result = await fetchUltrareviewPreflight({ repo: 'owner/repo' })
expect(result).toBeNull()
})
test('returns null on 5xx server error', async () => {
const serverError = new Error('Internal Server Error')
;(
serverError as unknown as {
isAxiosError: boolean
response: { status: number }
}
).isAxiosError = true
;(serverError as unknown as { response: { status: number } }).response = {
status: 500,
}
mockAxiosPost.mockImplementationOnce(async () => {
throw serverError
})
const result = await fetchUltrareviewPreflight({ repo: 'owner/repo' })
expect(result).toBeNull()
})
test('passes pr_number to request body when provided', async () => {
mockAxiosPost.mockImplementationOnce(
async (_url: unknown, body: unknown) => {
const b = body as { pr_number: number }
expect(b.pr_number).toBe(42)
return { status: 200, data: { action: 'proceed', billing_note: null } }
},
)
const result = await fetchUltrareviewPreflight({
repo: 'owner/repo',
pr_number: 42,
})
expect(result?.action).toBe('proceed')
})
test('passes confirm flag to request body when provided', async () => {
mockAxiosPost.mockImplementationOnce(
async (_url: unknown, body: unknown) => {
const b = body as { confirm: boolean }
expect(b.confirm).toBe(true)
return { status: 200, data: { action: 'proceed', billing_note: null } }
},
)
const result = await fetchUltrareviewPreflight({
repo: 'owner/repo',
confirm: true,
})
expect(result?.action).toBe('proceed')
})
})

View File

@@ -1,81 +0,0 @@
import axios from 'axios'
import z from 'zod/v4'
import { getOauthConfig } from '../../constants/oauth.js'
import { logForDebugging } from '../../utils/debug.js'
import { getOAuthHeaders, prepareApiRequest } from '../../utils/teleport/api.js'
/**
* Zod schema for the /v1/ultrareview/preflight response.
* Based on binary-extracted schema: vq.object({action: vq.enum([...]), billing_note: ...})
*/
const UltrareviewPreflightSchema = z.object({
action: z.enum(['proceed', 'confirm', 'blocked']),
billing_note: z.string().nullable().optional(),
})
export type UltrareviewPreflightResponse = z.infer<
typeof UltrareviewPreflightSchema
>
export type UltrareviewPreflightArgs = {
repo: string
pr_number?: number
pr_url?: string
confirm?: boolean
}
/**
* POST /v1/ultrareview/preflight — server-side gate before launch.
*
* Returns the preflight result (proceed / confirm / blocked) or null on any
* failure (network error, auth error, schema mismatch). Callers must treat
* null as "fallback to direct launch" to preserve existing behavior.
*
* The `confirm` flag should be set to true when the user has already
* acknowledged the billing dialog (or passed --confirm on the CLI), which
* skips the server-side confirm prompt and gets a direct proceed/blocked.
*/
export async function fetchUltrareviewPreflight(
args: UltrareviewPreflightArgs,
): Promise<UltrareviewPreflightResponse | null> {
try {
const { accessToken, orgUUID } = await prepareApiRequest()
const body: Record<string, unknown> = {
repo: args.repo,
}
if (args.pr_number !== undefined) {
body.pr_number = args.pr_number
}
if (args.pr_url !== undefined) {
body.pr_url = args.pr_url
}
if (args.confirm !== undefined) {
body.confirm = args.confirm
}
const response = await axios.post(
`${getOauthConfig().BASE_API_URL}/v1/ultrareview/preflight`,
body,
{
headers: {
...getOAuthHeaders(accessToken),
'x-organization-uuid': orgUUID,
},
timeout: 10000,
},
)
const parsed = UltrareviewPreflightSchema.safeParse(response.data)
if (!parsed.success) {
logForDebugging(
`fetchUltrareviewPreflight: schema mismatch — ${parsed.error.message}`,
)
return null
}
return parsed.data
} catch (error) {
logForDebugging(`fetchUltrareviewPreflight failed: ${error}`)
return null
}
}