113 lines
5.3 KiB
Dart
113 lines
5.3 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:drift/native.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:motorcycle_dash/database/database.dart';
|
|
// ignore: depend_on_referenced_packages
|
|
import 'package:sqlite3/sqlite3.dart' as sqlite;
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
late Directory directory;
|
|
late File file;
|
|
|
|
setUp(() async {
|
|
directory = await Directory.systemTemp.createTemp('ride-timestamp-test-');
|
|
file = File('${directory.path}/copy.sqlite');
|
|
final db = AppDatabase.forTesting(NativeDatabase(file));
|
|
await db.customSelect('SELECT 1').get();
|
|
await db.close();
|
|
});
|
|
tearDown(() => directory.delete(recursive: true));
|
|
|
|
final backupPath = Platform.environment['MOTORSIGNAL_TEST_BACKUP'];
|
|
if (backupPath != null) {
|
|
test('verified device backup copy preserves every original table row', () async {
|
|
await File(backupPath).copy(file.path);
|
|
final raw = sqlite.sqlite3.open(file.path);
|
|
final names = raw.select("SELECT name FROM sqlite_master WHERE type = 'table'")
|
|
.map((r) => r['name'] as String).toList();
|
|
final before = {
|
|
for (final name in names)
|
|
name: raw.select('SELECT * FROM "$name" ORDER BY rowid')
|
|
.map((r) => Map<String, Object?>.from(r)).toList(),
|
|
};
|
|
raw.close();
|
|
final db = AppDatabase.forTesting(NativeDatabase(file));
|
|
addTearDown(db.close);
|
|
expect(await db.select(db.rides).get(), hasLength(22));
|
|
expect(await db.select(db.telemetrySamples).get(), hasLength(67798));
|
|
for (final name in names) {
|
|
final rows = await db.customSelect('SELECT * FROM "$name" ORDER BY rowid').get();
|
|
expect(rows.map((r) => r.data).toList(), before[name], reason: name);
|
|
}
|
|
expect(await db.customSelect('SELECT * FROM ride_timestamp_archive').get(), isEmpty);
|
|
expect((await db.customSelect('PRAGMA integrity_check').get()).single.data.values.single, 'ok');
|
|
await db.close();
|
|
});
|
|
}
|
|
|
|
test('v7 mixed dates migrate once with exact originals and children preserved', () async {
|
|
final raw = sqlite.sqlite3.open(file.path);
|
|
for (final row in [
|
|
[1, 1791761491, null],
|
|
[2, 1791761491123, 1791761501456],
|
|
[3, 1791761491123456, 1791761501654321],
|
|
[4, 1791761491, 1791761501000],
|
|
]) {
|
|
raw.execute('INSERT INTO rides (id, start_time, end_time, cloud_ride_id) VALUES (?, ?, ?, ?)',
|
|
[...row, 'ride_preserved_${row.first}']);
|
|
}
|
|
raw.execute('INSERT INTO telemetry_samples (ride_id, timestamp, speed) VALUES (2, 1791761491, 17.5)');
|
|
raw.execute('PRAGMA user_version = 7');
|
|
final before = raw.select('SELECT * FROM rides ORDER BY id').map((r) => Map<String, Object?>.from(r)).toList();
|
|
final children = raw.select('SELECT * FROM telemetry_samples').map((r) => Map<String, Object?>.from(r)).toList();
|
|
raw.close();
|
|
|
|
for (var open = 0; open < 2; open++) {
|
|
final db = AppDatabase.forTesting(NativeDatabase(file));
|
|
addTearDown(db.close);
|
|
final rides = await db.select(db.rides).get();
|
|
expect(rides, hasLength(4));
|
|
expect(rides.map((r) => r.startTime.toUtc().millisecondsSinceEpoch), everyElement(1791761491000));
|
|
expect(rides.first.endTime, isNull);
|
|
expect(rides.skip(1).map((r) => r.endTime!.toUtc().millisecondsSinceEpoch), everyElement(1791761501000));
|
|
final archive = await db.customSelect('SELECT * FROM ride_timestamp_archive ORDER BY ride_id').get();
|
|
expect(archive, hasLength(3));
|
|
expect(archive.first.data['original_start_time'], 1791761491123);
|
|
expect(archive[1].data['original_start_time'], 1791761491123456);
|
|
expect(archive[1].data['original_end_time'], 1791761501654321);
|
|
expect((await db.customSelect('SELECT * FROM telemetry_samples').get()).map((r) => r.data).toList(), children);
|
|
final after = (await db.customSelect('SELECT * FROM rides ORDER BY id').get()).map((r) => Map<String, Object?>.from(r.data)).toList();
|
|
for (var i = 0; i < before.length; i++) {
|
|
after[i]['start_time'] = before[i]['start_time'];
|
|
after[i]['end_time'] = before[i]['end_time'];
|
|
}
|
|
expect(after, before);
|
|
await db.close();
|
|
}
|
|
|
|
final restored = sqlite.sqlite3.open(file.path);
|
|
restored.execute(File('tools/rollback_ride_timestamps_v8.sql').readAsStringSync());
|
|
expect(restored.select('SELECT * FROM rides ORDER BY id').map((r) => Map<String, Object?>.from(r)).toList(), before);
|
|
expect(restored.select('PRAGMA user_version').single['user_version'], 7);
|
|
expect(restored.select('SELECT COUNT(*) AS n FROM ride_timestamp_archive').single['n'], 3);
|
|
restored.close();
|
|
});
|
|
|
|
test('unrecognized large timestamp aborts without rewriting records', () async {
|
|
final raw = sqlite.sqlite3.open(file.path);
|
|
raw.execute('INSERT INTO rides (start_time) VALUES (1791761491123), (9223372036854775807)');
|
|
raw.execute('PRAGMA user_version = 7');
|
|
raw.close();
|
|
final db = AppDatabase.forTesting(NativeDatabase(file));
|
|
addTearDown(db.close);
|
|
await expectLater(db.select(db.rides).get(), throwsA(isA<StateError>()));
|
|
await db.close();
|
|
final check = sqlite.sqlite3.open(file.path);
|
|
expect(check.select('SELECT start_time FROM rides ORDER BY id').map((r) => r['start_time']).toList(), [1791761491123, 9223372036854775807]);
|
|
expect(check.select('PRAGMA user_version').single['user_version'], 7);
|
|
check.close();
|
|
});
|
|
}
|