import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:motorcycle_dash/database/database.dart'; void main() { TestWidgetsFlutterBinding.ensureInitialized(); test('new rides receive a persisted cloudRideId independent of local id', () async { final db = AppDatabase.forTesting(NativeDatabase.memory()); addTearDown(db.close); final localId = await db.startRide(); final cloudId = await db.getCloudRideId(localId); expect(cloudId, isNotNull); expect(cloudId, startsWith('ride_')); expect(cloudId, isNot(localId.toString())); final secondLocalId = await db.startRide(); final secondCloudId = await db.getCloudRideId(secondLocalId); expect(secondCloudId, isNot(cloudId)); // Re-reading the same local row returns the exact persisted identity. expect(await db.getCloudRideId(localId), cloudId); }); test('ensureCloudRideId is idempotent', () async { final db = AppDatabase.forTesting(NativeDatabase.memory()); addTearDown(db.close); final localId = await db.startRide(); final first = await db.ensureCloudRideId(localId); final second = await db.ensureCloudRideId(localId); expect(second, first); }); test('ensureCloudRideId repairs legacy null ride identity', () async { final db = AppDatabase.forTesting(NativeDatabase.memory()); addTearDown(db.close); final legacyRideId = await db.into(db.rides).insert( RidesCompanion.insert(startTime: DateTime.utc(2026, 9, 1, 10)), ); expect(await db.getCloudRideId(legacyRideId), isNull); final repaired = await db.ensureCloudRideId(legacyRideId); expect(repaired, startsWith('ride_')); expect(await db.getCloudRideId(legacyRideId), repaired); expect(await db.ensureCloudRideId(legacyRideId), repaired); }); test('real v5 rides migrate to their legacy MotorCloud identity', () async { final connection = NativeDatabase.memory(setup: (rawDb) { rawDb.execute(''' CREATE TABLE rides ( id INTEGER PRIMARY KEY AUTOINCREMENT, start_time INTEGER NOT NULL, end_time INTEGER, distance_miles REAL NOT NULL DEFAULT 0.0, top_speed_mph REAL NOT NULL DEFAULT 0.0, max_lean_left REAL NOT NULL DEFAULT 0.0, max_lean_right REAL NOT NULL DEFAULT 0.0, max_accel_g REAL NOT NULL DEFAULT 0.0, max_braking_g REAL NOT NULL DEFAULT 0.0, best0to60_ms REAL, best0to100_ms REAL, distance REAL NOT NULL DEFAULT 0.0, moving_time INTEGER NOT NULL DEFAULT 0, stopped_time INTEGER NOT NULL DEFAULT 0, average_speed REAL NOT NULL DEFAULT 0.0, top_speed REAL NOT NULL DEFAULT 0.0, elevation_gain REAL NOT NULL DEFAULT 0.0, elevation_loss REAL NOT NULL DEFAULT 0.0, motorcycle_id INTEGER, motorcycle_year INTEGER, motorcycle_make TEXT, motorcycle_model TEXT, motorcycle_variant TEXT ) '''); final start = DateTime.utc(2026, 8, 31, 21, 7, 5); rawDb.execute('INSERT INTO rides (id, start_time) VALUES (?, ?)', [42, start.millisecondsSinceEpoch]); rawDb.execute('PRAGMA user_version = 5'); }); final db = AppDatabase.forTesting(connection); addTearDown(db.close); final migrated = await db.getCloudRideId(42); expect(migrated, 'ride_42_20260831_210705'); expect(await db.getCloudOwnerUid(42), isNull); final rows = await db.customSelect('PRAGMA table_info(rides)').get(); expect(rows.any((row) => row.data['name'] == 'cloud_ride_id'), isTrue); final indexes = await db.customSelect('PRAGMA index_list(rides)').get(); expect(indexes.any((row) => row.data['name'] == 'rides_cloud_ride_id_idx'), isTrue); }); test('fresh database creation creates cloud identity column and unique index', () async { final db = AppDatabase.forTesting(NativeDatabase.memory()); addTearDown(db.close); final columns = await db.customSelect('PRAGMA table_info(rides)').get(); expect(columns.any((row) => row.data['name'] == 'cloud_ride_id'), isTrue); final indexes = await db.customSelect('PRAGMA index_list(rides)').get(); expect(indexes.any((row) => row.data['name'] == 'rides_cloud_ride_id_idx'), isTrue); final rideId = await db.startRide(); expect(await db.getCloudRideId(rideId), startsWith('ride_')); }); test('a restored ride keeps its cloud identity even when its local ID changes', () async { const cloudId = 'ride_9f4d8a3e2b1c7d6e5f4a3b2c1d0e9f8a'; final original = AppDatabase.forTesting(NativeDatabase.memory()); final restored = AppDatabase.forTesting(NativeDatabase.memory()); addTearDown(original.close); addTearDown(restored.close); final originalId = await original.startRide(); await original.customStatement( 'UPDATE rides SET cloud_ride_id = ? WHERE id = ?', [cloudId, originalId], ); expect(await original.getCloudRideId(originalId), cloudId); // Insert a dummy ride first so the restored DB assigns a different local ID await restored.startRide(); final restoredId = await restored.startRide(); await restored.customStatement( 'UPDATE rides SET cloud_ride_id = ? WHERE id = ?', [cloudId, restoredId], ); expect(restoredId, isNot(originalId)); expect(await restored.getCloudRideId(restoredId), cloudId); }); }