Files
English/kouyu_english/test/ai_prompt_cache_test.dart
T
2026-09-19 19:21:41 -07:00

128 lines
4.3 KiB
Dart

import 'dart:convert';
import 'package:flutter_test/flutter_test.dart';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
import 'package:kouyu_english/core/ai_service.dart';
import 'package:kouyu_english/core/models.dart';
void main() {
TestWidgetsFlutterBinding.ensureInitialized();
test(
'lexicon requests keep a stable system prefix and dynamic JSON last',
() async {
AiService.instance.setFallbackApiKey('test-key');
final captured = <List<dynamic>>[];
Future<void> request(String text) async {
await http.runWithClient(
() => AiService.instance.temporaryDefinition(
provider: AiProviderType.compatible,
endpoint: 'https://api.deepseek.com',
model: 'deepseek-flash',
text: text,
),
() => MockClient((request) async {
final body = jsonDecode(request.body) as Map<String, dynamic>;
captured.add(body['messages'] as List<dynamic>);
return http.Response(
jsonEncode({
'choices': [
{
'message': {'content': '{"definition":"测试"}'},
},
],
}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
}),
);
}
await request('apple');
await request('airport');
expect(captured, hasLength(2));
expect(captured[0], hasLength(2));
expect(captured[0][0]['role'], 'system');
expect(captured[0][0]['content'], captured[1][0]['content']);
expect(captured[0][0]['content'], isNot(contains('apple')));
expect(jsonDecode(captured[0][1]['content'] as String), {
'text': 'apple',
});
expect(jsonDecode(captured[1][1]['content'] as String), {
'text': 'airport',
});
},
);
test(
'dialogue evaluation keeps learner data out of the cached prefix',
() async {
AiService.instance.setFallbackApiKey('test-key');
final captured = <List<dynamic>>[];
Future<void> request({
required String learnerText,
required String turnId,
}) async {
await http.runWithClient(
() => AiService.instance.checkDialogueIntervention(
provider: AiProviderType.compatible,
endpoint: 'https://api.deepseek.com',
model: 'deepseek-flash',
partnerLine: 'How are you?',
taskLabel: 'Say how you feel.',
learnerText: learnerText,
turnId: turnId,
),
() => MockClient((request) async {
final body = jsonDecode(request.body) as Map<String, dynamic>;
captured.add(body['messages'] as List<dynamic>);
return http.Response(
jsonEncode({
'choices': [
{
'message': {
'content': jsonEncode({
'schemaVersion': 'dialogue-intervention-2',
'turnId': turnId,
'accepted': true,
'goalSatisfied': true,
'verdict': 'accepted',
'reasonCode': 'goal-met',
'suggestion': null,
'explanation': '表达自然。',
}),
},
},
],
}),
200,
headers: {'content-type': 'application/json; charset=utf-8'},
);
}),
);
}
await request(learnerText: 'I am fine.', turnId: 'turn-1');
await request(learnerText: 'I feel tired.', turnId: 'turn-2');
final firstSystem = captured[0][0]['content'] as String;
final secondSystem = captured[1][0]['content'] as String;
expect(firstSystem, secondSystem);
expect(firstSystem, isNot(contains('I am fine.')));
expect(firstSystem, isNot(contains('turn-1')));
final firstInput = jsonDecode(captured[0][1]['content'] as String);
final secondInput = jsonDecode(captured[1][1]['content'] as String);
expect(firstInput['learnerText'], 'I am fine.');
expect(firstInput['turnId'], 'turn-1');
expect(secondInput['learnerText'], 'I feel tired.');
expect(secondInput['turnId'], 'turn-2');
},
);
}