- Android NotificationListenerService plugin + queue store, MethodChannel bridge, and Flutter ingest worker / access controller - source_apps allowlist (DAO, repo, settings + source_apps screen) - schema v8 migration + decision_gate / dedup / draft_codec updates - l10n strings and tests Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
74 lines
2.7 KiB
Dart
74 lines
2.7 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:drift/drift.dart' hide isNull, isNotNull;
|
|
import 'package:drift/native.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:new_budget/src/core/database/app_database.dart';
|
|
import 'package:new_budget/src/core/database/converters/enum_converters.dart';
|
|
import 'package:new_budget/src/features/notification_parsing/domain/enums.dart';
|
|
|
|
/// Тест миграции v7 → v8 (parse_rules.tx_type для gate-проверки
|
|
/// typeMatchesRule).
|
|
///
|
|
/// Поднимаем актуальную (v8) схему, «откатываем» до v7 (убираем tx_type,
|
|
/// ставим user_version = 7). Повторное открытие запускает onUpgrade(7 → 8),
|
|
/// который должен восстановить колонку.
|
|
void main() {
|
|
late File file;
|
|
|
|
setUp(() {
|
|
final dir = Directory.systemTemp.createTempSync('nb_migration_v8_test');
|
|
file = File('${dir.path}/test.sqlite');
|
|
});
|
|
|
|
tearDown(() {
|
|
if (file.existsSync()) file.deleteSync();
|
|
final parent = file.parent;
|
|
if (parent.existsSync()) parent.deleteSync(recursive: true);
|
|
});
|
|
|
|
test('onUpgrade 7 → 8 добавляет parse_rules.tx_type', () async {
|
|
const userId = 'user-1';
|
|
|
|
// 1. Актуальная схема (v8) + FK-цепочка.
|
|
final dbV8 = AppDatabase.forTesting(NativeDatabase(file));
|
|
await dbV8.usersDao
|
|
.insertUser(UsersTableCompanion.insert(id: userId, name: 'Тест'));
|
|
|
|
// 2. «Откатываем» до v7.
|
|
await dbV8.customStatement('ALTER TABLE parse_rules DROP COLUMN tx_type');
|
|
await dbV8.customStatement('PRAGMA user_version = 7');
|
|
await dbV8.close();
|
|
|
|
// 3. Повторное открытие → onUpgrade(7 → 8).
|
|
final dbMigrated = AppDatabase.forTesting(NativeDatabase(file));
|
|
addTearDown(dbMigrated.close);
|
|
|
|
// 4. Колонка снова доступна: правило с txType проходит round-trip,
|
|
// легаси-правило без txType читается с null.
|
|
await dbMigrated.parseRulesDao.insert(
|
|
ParseRulesTableCompanion.insert(
|
|
id: 'r-typed',
|
|
userId: userId,
|
|
kind: ParseRuleKind.merchantToCategory,
|
|
pattern: 'LENTA',
|
|
txType: const Value(TransactionType.expense),
|
|
),
|
|
);
|
|
await dbMigrated.parseRulesDao.insert(
|
|
ParseRulesTableCompanion.insert(
|
|
id: 'r-legacy',
|
|
userId: userId,
|
|
kind: ParseRuleKind.merchantToCategory,
|
|
pattern: 'OZON',
|
|
),
|
|
);
|
|
|
|
final typed = await dbMigrated.parseRulesDao.findById('r-typed');
|
|
expect(typed!.txType, TransactionType.expense);
|
|
|
|
final legacy = await dbMigrated.parseRulesDao.findById('r-legacy');
|
|
expect(legacy!.txType, isNull);
|
|
});
|
|
}
|