import 'package:flutter_test/flutter_test.dart'; import 'package:qiangbao_accounting/models/ai_persona.dart'; /// AiPersonaItem 的解析与预览测试。 /// /// 重点是**不崩**:服务端字段缺失(老版本后端、网络截断)时 fromJson /// 不能抛异常,否则整个配置页会白屏。 void main() { group('AiPersonaItem 解析', () { test('完整字段正常解析', () { final item = AiPersonaItem.fromJson({ 'id': 5, 'name': '花花', 'systemPrompt': '你是助手。', 'promptLength': 6, 'isDefault': false, 'editable': true, 'defaultPrompt': null, }); expect(item.id, 5); expect(item.name, '花花'); expect(item.systemPrompt, '你是助手。'); expect(item.promptLength, 6); expect(item.isDefault, isFalse); expect(item.editable, isTrue); expect(item.defaultPrompt, isNull); }); test('默认项:id 为 null、不可编辑、带 defaultPrompt', () { final item = AiPersonaItem.fromJson({ 'id': null, 'name': '默认人设', 'systemPrompt': '内置文本', 'isDefault': true, 'editable': false, 'defaultPrompt': '内置文本', }); expect(item.id, isNull); expect(item.isDefault, isTrue); expect(item.editable, isFalse); expect(item.defaultPrompt, '内置文本'); }); test('★ 字段全缺也不抛异常(老后端 / 网络截断)', () { final item = AiPersonaItem.fromJson(const {}); expect(item.id, isNull); expect(item.name, '未命名'); expect(item.systemPrompt, ''); expect(item.promptLength, 0); expect(item.isDefault, isFalse); // editable 默认 true —— 缺字段时倾向「让用户能改」,而不是锁死 expect(item.editable, isTrue); expect(item.defaultPrompt, isNull); }); test('selectionKey:默认项是 -1,具体项是它的 id', () { final def = AiPersonaItem.fromJson({'id': null, 'isDefault': true}); final mine = AiPersonaItem.fromJson({'id': 42}); expect(def.selectionKey, -1); expect(mine.selectionKey, 42); }); test('defaultPrompt 为空串时归一成 null(不能拿空串去判断「有没有」)', () { final item = AiPersonaItem.fromJson({'id': 1, 'defaultPrompt': ''}); expect(item.defaultPrompt, isNull); }); test('promptLength 缺失时按 0,不会让界面显示 null', () { final item = AiPersonaItem.fromJson({'id': 1}); expect(item.promptLength, 0); }); }); group('preview(列表里的两行预览)', () { test('短文本原样返回', () { final item = AiPersonaItem.fromJson({ 'id': 1, 'systemPrompt': '你是助手。', }); expect(item.preview, '你是助手。'); }); test('换行被压成空格 —— 否则列表项会被撑成好几行', () { final item = AiPersonaItem.fromJson({ 'id': 1, 'systemPrompt': '第一行\n第二行\n\n第三行', }); expect(item.preview, '第一行 第二行 第三行'); expect(item.preview.contains('\n'), isFalse); }); test('连续空白(含缩进)被压成一个空格', () { final item = AiPersonaItem.fromJson({ 'id': 1, 'systemPrompt': '甲 乙\t丙', }); expect(item.preview, '甲 乙 丙'); }); test('超长文本截断并加省略号', () { final long = '啊' * 100; final item = AiPersonaItem.fromJson({'id': 1, 'systemPrompt': long}); expect(item.preview.length, 51); // 50 + 省略号 expect(item.preview.endsWith('…'), isTrue); }); test('刚好 50 字不截断', () { final exact = '啊' * 50; final item = AiPersonaItem.fromJson({'id': 1, 'systemPrompt': exact}); expect(item.preview, exact); expect(item.preview.endsWith('…'), isFalse); }); test('空提示词不会崩', () { final item = AiPersonaItem.fromJson({'id': 1, 'systemPrompt': ''}); expect(item.preview, ''); }); test('只有空白字符时预览是空的', () { final item = AiPersonaItem.fromJson({'id': 1, 'systemPrompt': ' \n\t '}); expect(item.preview, ''); }); test('首尾空白被去掉', () { final item = AiPersonaItem.fromJson({ 'id': 1, 'systemPrompt': ' 你是助手。 ', }); expect(item.preview, '你是助手。'); }); }); }