test(app): 前端第一批测试 — EditorBloc 19用例 + JournalElement 11用例 + InMemoryRepo 12用例

添加 mocktail 测试依赖 + 字体文件占位
总计 42 测试通过,覆盖工具/笔画/元素/撤销/重做/序列化/乐观锁/CRUD
This commit is contained in:
iven
2026-06-01 23:02:14 +08:00
parent 3eaf83c79a
commit 4c743e150e
11 changed files with 723 additions and 0 deletions

View File

@@ -0,0 +1,188 @@
// InMemoryJournalRepository 单元测试
//
// 覆盖CRUD 操作、乐观锁冲突、软删除、元素管理、clearAll
import 'dart:math';
import 'package:flutter_test/flutter_test.dart';
import 'package:nuanji_app/data/models/journal_element.dart';
import 'package:nuanji_app/data/models/journal_entry.dart';
import 'package:nuanji_app/data/repositories/journal_repository.dart';
void main() {
group('InMemoryJournalRepository', () {
late InMemoryJournalRepository repo;
setUp(() {
repo = InMemoryJournalRepository();
});
// ===== 辅助方法 =====
JournalEntry _createEntry({String id = 'j-1', String title = '测试日记'}) {
return JournalEntry(
id: id,
authorId: 'user-1',
title: title,
date: DateTime(2026, 6, 1),
mood: Mood.happy,
weather: Weather.sunny,
tags: const [],
isPrivate: false,
sharedToClass: false,
version: 1,
createdAt: DateTime.now(),
updatedAt: DateTime.now(),
);
}
// ===== 创建 =====
test('创建日记', () async {
final entry = _createEntry();
final created = await repo.createJournal(entry);
expect(created.id, entry.id);
expect(created.title, '测试日记');
expect(repo.journalCount, 1);
});
// ===== 读取 =====
test('获取日记', () async {
final entry = _createEntry();
await repo.createJournal(entry);
final fetched = await repo.getJournal('j-1');
expect(fetched, isNotNull);
expect(fetched!.title, '测试日记');
});
test('获取不存在的日记返回 null', () async {
final fetched = await repo.getJournal('non-existent');
expect(fetched, isNull);
});
test('获取日记列表', () async {
await repo.createJournal(_createEntry(id: 'j-1'));
await repo.createJournal(_createEntry(id: 'j-2'));
final list = await repo.getJournals();
expect(list.length, 2);
});
test('空仓库返回空列表', () async {
final list = await repo.getJournals();
expect(list, isEmpty);
});
// ===== 更新 =====
test('更新日记标题', () async {
await repo.createJournal(_createEntry());
final existing = (await repo.getJournal('j-1'))!;
final updated = await repo.updateJournal(
existing.copyWith(title: '更新后的标题'),
);
expect(updated.title, '更新后的标题');
expect(updated.version, 2);
final refetched = (await repo.getJournal('j-1'))!;
expect(refetched.title, '更新后的标题');
expect(refetched.version, 2);
});
test('乐观锁版本冲突抛出异常', () async {
await repo.createJournal(_createEntry());
final v1 = (await repo.getJournal('j-1'))!;
// 先更新一次 → v2
await repo.updateJournal(v1.copyWith(title: 'v2'));
// 再用 v1 更新 → 应抛 StateError
expect(
() => repo.updateJournal(v1.copyWith(title: 'conflict')),
throwsA(isA<StateError>()),
);
});
// ===== 删除 =====
test('软删除日记', () async {
await repo.createJournal(_createEntry());
await repo.deleteJournal('j-1');
final fetched = await repo.getJournal('j-1');
expect(fetched, isNull);
});
// ===== 元素 =====
test('添加元素', () async {
await repo.createJournal(_createEntry());
final element = JournalElement.createText(
journalId: 'j-1',
text: '测试文字',
position: Offset.zero,
);
final added = await repo.addElement(element);
expect(added.journalId, 'j-1');
expect(repo.elementCount, 1);
});
test('获取元素列表(按 journalId 过滤)', () async {
await repo.createJournal(_createEntry(id: 'j-1'));
await repo.createJournal(_createEntry(id: 'j-2'));
await repo.addElement(JournalElement.createText(
journalId: 'j-1', text: '文字1', position: Offset.zero,
));
await repo.addElement(JournalElement.createSticker(
journalId: 'j-1', emoji: '🌸', position: const Offset(50, 50),
));
await repo.addElement(JournalElement.createText(
journalId: 'j-2', text: '文字2', position: Offset.zero,
));
final elementsJ1 = await repo.getElements('j-1');
final elementsJ2 = await repo.getElements('j-2');
expect(elementsJ1.length, 2);
expect(elementsJ2.length, 1);
});
test('删除元素', () async {
await repo.createJournal(_createEntry());
final element = JournalElement.createText(
journalId: 'j-1', text: '待删除', position: Offset.zero,
);
await repo.addElement(element);
await repo.removeElement(element.id);
expect(repo.elementCount, 0);
final elements = await repo.getElements('j-1');
expect(elements, isEmpty);
});
// ===== clearAll =====
test('clearAll 清空所有数据', () async {
await repo.createJournal(_createEntry(id: 'j-1'));
await repo.createJournal(_createEntry(id: 'j-2'));
await repo.addElement(JournalElement.createText(
journalId: 'j-1', text: '文字', position: Offset.zero,
));
repo.clearAll();
expect(repo.journalCount, 0);
expect(repo.elementCount, 0);
expect(await repo.getJournals(), isEmpty);
});
});
}