import 'dart:convert'; import 'package:archive/archive.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:miaoji_zhang/shared/services/local_export_service.dart'; void main() { test('local export creates readable CSV and privacy-safe JSON', () { final bytes = LocalExportService.buildZipFromSnapshot( { 'schemaVersion': 1, 'exportedAt': '2026-07-21T00:00:00.000Z', 'ledgers': [ { 'id': 1, 'name': '日常账本', 'iconKey': 'wallet', 'isDefault': true, 'updatedAt': '2026-07-21T00:00:00.000Z', }, ], 'categories': [ { 'id': 2, 'name': '餐饮', 'iconKey': 'food', 'colorKey': 'coral', 'type': 'expense', 'sortOrder': 10, 'isCustom': false, 'isDeleted': false, 'updatedAt': '2026-07-21T00:00:00.000Z', }, ], 'transactions': [ { 'id': 3, 'ledgerId': 1, 'categoryId': 2, 'categoryName': '餐饮', 'categoryIcon': 'food', 'categoryColor': 'coral', 'type': 'expense', 'amount': 12.5, 'note': '午饭,"套餐"\n已报销', 'paymentMethod': '微信', 'source': 'manual', 'sourceText': null, 'occurredAt': '2026-07-21T04:00:00.000Z', 'isDeleted': false, 'updatedAt': '2026-07-21T04:00:00.000Z', }, ], 'budgets': [ { 'ledgerId': 1, 'period': 202607, 'categoryId': 2, 'amount': 800.0, 'recurring': true, 'updatedAt': '2026-07-21T00:00:00.000Z', }, ], }, profile: const {'mode': 'guest', 'nickname': '游客'}, ); final archive = ZipDecoder().decodeBytes(bytes); expect( archive.map((file) => file.name), containsAll(['transactions.csv', 'budgets.csv', 'jizhi-backup.json']), ); final transactionCsv = utf8.decode( archive.findFile('transactions.csv')!.readBytes()!, ); expect(transactionCsv, contains('12.50')); expect(transactionCsv, contains('"午饭,""套餐""\n已报销"')); final json = utf8.decode( archive.findFile('jizhi-backup.json')!.readBytes()!, ); final document = jsonDecode(json) as Map; expect(document['transactions'], hasLength(1)); expect((document['profile'] as Map)['mode'], 'guest'); expect(json, isNot(contains('passwordHash'))); expect(json, isNot(contains('local_db_key'))); }); }