From d04a4903e8dcb30beea1548bb8c2e11e48bf472a Mon Sep 17 00:00:00 2001 From: shenlei Date: Wed, 16 Sep 2026 16:40:54 +0900 Subject: [PATCH] =?UTF-8?q?chore:=20=E8=81=94=E7=BD=91=E6=B5=8B=E8=AF=95?= =?UTF-8?q?=E5=AF=86=E9=92=A5=E6=94=B9=E4=BB=8E=E7=8E=AF=E5=A2=83=E5=8F=98?= =?UTF-8?q?=E9=87=8F=E8=AF=BB=E5=8F=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit live_endpoint_test.dart 里硬编码了一个真实 API key。改为读取 KOUYU_AI_API_KEY(可选 KOUYU_AI_BASE_URL、KOUYU_AI_MODEL),未设置时 整组测试跳过,不再把缺少凭据的联网测试当成通过。 注意:旧 key 仍留在提交历史中,需要在服务端作废并重新签发。 Co-Authored-By: Claude Opus 5 --- kouyu_english/test/live_endpoint_test.dart | 48 +++++++++++++++------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/kouyu_english/test/live_endpoint_test.dart b/kouyu_english/test/live_endpoint_test.dart index 165e232..f838fb6 100644 --- a/kouyu_english/test/live_endpoint_test.dart +++ b/kouyu_english/test/live_endpoint_test.dart @@ -11,56 +11,74 @@ class RealHttpOverrides extends HttpOverrides { } } +/// 读取联网测试用的配置。密钥只来自环境变量,不写进仓库。 +/// +/// 运行方式: +/// KOUYU_AI_API_KEY=sk-xxx flutter test test/live_endpoint_test.dart +/// 可选:KOUYU_AI_BASE_URL(默认 https://codex.slcydia.fun)、KOUYU_AI_MODEL。 +/// 未设置密钥时整组测试自动跳过,不会误报成功。 +String? _env(String name) { + final value = Platform.environment[name]; + return (value == null || value.trim().isEmpty) ? null : value.trim(); +} + void main() { TestWidgetsFlutterBinding.ensureInitialized(); HttpOverrides.global = RealHttpOverrides(); - const testKey = '***REMOVED***'; - AiService.instance.setFallbackApiKey(testKey); + + final apiKey = _env('KOUYU_AI_API_KEY'); + final baseUrl = _env('KOUYU_AI_BASE_URL') ?? 'https://codex.slcydia.fun'; + final model = _env('KOUYU_AI_MODEL') ?? 'gemini-3.7-flash-high'; + final skipReason = apiKey == null + ? '未设置 KOUYU_AI_API_KEY,跳过联网测试。' + : null; + if (apiKey != null) { + AiService.instance.setFallbackApiKey(apiKey); + } test('Live test: /v1/responses endpoint testConnection', () async { try { final resResponses = await AiService.instance.testConnection( provider: AiProviderType.compatible, - endpoint: 'https://codex.slcydia.fun/v1/responses', - model: 'gemini-3.7-flash-high', - explicitApiKey: testKey, + endpoint: '$baseUrl/v1/responses', + model: model, + explicitApiKey: apiKey, ); print('Responses API result: ok=${resResponses.ok}, msg=${resResponses.message}'); } catch (e) { print('Network test skipped: $e'); } - }); + }, skip: skipReason); test('Live test: /v1 (Chat Completions) endpoint testConnection', () async { try { final resChat = await AiService.instance.testConnection( provider: AiProviderType.compatible, - endpoint: 'https://codex.slcydia.fun/v1', - model: 'gemini-3.7-flash-high', - explicitApiKey: testKey, + endpoint: '$baseUrl/v1', + model: model, + explicitApiKey: apiKey, ); print('Chat Completions API result: ok=${resChat.ok}, msg=${resChat.message}'); } catch (e) { print('Network test skipped: $e'); } - }); + }, skip: skipReason); test('Live test: /v1/responses dialogueReply', timeout: const Timeout(Duration(seconds: 60)), () async { - try { final reply = await AiService.instance.dialogueReply( provider: AiProviderType.compatible, - endpoint: 'https://codex.slcydia.fun/v1/responses', - model: 'gemini-3.7-flash-high', + endpoint: '$baseUrl/v1/responses', + model: model, history: [ {'role': 'user', 'content': 'Hello, my name is Alex.'} ], aiGoal: 'Greet the learner and ask their name', learnerTask: 'say their own name', ); - print('Dialogue reply from /v1/responses: reply="${reply?.reply}", slots=${reply?.slots}, suggestsComplete=${reply?.suggestsComplete}'); + print('Dialogue reply from /v1/responses: reply="${reply?.reply}", translation="${reply?.translation}", feedback="${reply?.feedback}"'); } catch (e) { print('Network test skipped: $e'); } - }); + }, skip: skipReason); }