Remote sync of webdav
This commit is contained in:
parent
f01186df79
commit
483fbeafb6
44 changed files with 4842 additions and 975 deletions
|
|
@ -6,7 +6,7 @@ import 'package:path/path.dart' as p;
|
|||
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
||||
|
||||
/// Exercises the exact ATTACH + INSERT OR REPLACE merge SQL the app runs
|
||||
/// (see [db_schema.dart] / DriveSyncService._pullAndMerge), against real
|
||||
/// (see [db_schema.dart] / CloudSyncService._pullAndMerge), against real
|
||||
/// temporary SQLite files via sqflite_common_ffi — the pure-Dart backend
|
||||
/// that lets sqflite run on the Dart VM for tests, without a real device.
|
||||
void main() {
|
||||
|
|
@ -43,18 +43,19 @@ void main() {
|
|||
|
||||
group('vehicle merge', () {
|
||||
test('pulls in a vehicle that only exists on remote', () async {
|
||||
await remote.insert('vehicles', _vehicleRow(id: 'v1', updatedAt: 1000));
|
||||
await remote.insert('vehicles', _vehicleRow(id: 'v1', vin: 'VIN1', updatedAt: 1000));
|
||||
|
||||
await merge();
|
||||
|
||||
final rows = await local.query('vehicles');
|
||||
expect(rows, hasLength(1));
|
||||
expect(rows.first['id'], 'v1');
|
||||
expect(rows.first['vin'], 'VIN1');
|
||||
expect(rows.first['dirty'], 0);
|
||||
});
|
||||
|
||||
test('leaves a local-only dirty vehicle untouched', () async {
|
||||
await local.insert('vehicles', _vehicleRow(id: 'v1', updatedAt: 1000, dirty: 1));
|
||||
await local.insert('vehicles', _vehicleRow(id: 'v1', vin: 'VIN1', updatedAt: 1000, dirty: 1));
|
||||
|
||||
await merge();
|
||||
|
||||
|
|
@ -64,30 +65,60 @@ void main() {
|
|||
});
|
||||
|
||||
test('remote wins when strictly newer than local', () async {
|
||||
await local.insert(
|
||||
'vehicles', _vehicleRow(id: 'v1', make: 'OldMake', updatedAt: 1000, dirty: 1));
|
||||
await remote.insert('vehicles', _vehicleRow(id: 'v1', make: 'NewMake', updatedAt: 2000));
|
||||
await local.insert('vehicles',
|
||||
_vehicleRow(id: 'v1', vin: 'VIN1', nickname: 'OldNickname', updatedAt: 1000, dirty: 1));
|
||||
await remote.insert('vehicles',
|
||||
_vehicleRow(id: 'v1', vin: 'VIN1', nickname: 'NewNickname', updatedAt: 2000));
|
||||
|
||||
await merge();
|
||||
|
||||
final rows = await local.query('vehicles');
|
||||
expect(rows, hasLength(1));
|
||||
expect(rows.first['make'], 'NewMake');
|
||||
expect(rows.first['nickname'], 'NewNickname');
|
||||
expect(rows.first['dirty'], 0);
|
||||
});
|
||||
|
||||
test('local wins on a tie or when strictly newer', () async {
|
||||
await local.insert(
|
||||
'vehicles', _vehicleRow(id: 'v1', make: 'MineNewer', updatedAt: 2000, dirty: 1));
|
||||
await remote.insert('vehicles', _vehicleRow(id: 'v1', make: 'TheirsOlder', updatedAt: 1000));
|
||||
await local.insert('vehicles',
|
||||
_vehicleRow(id: 'v1', vin: 'VIN1', nickname: 'MineNewer', updatedAt: 2000, dirty: 1));
|
||||
await remote.insert('vehicles',
|
||||
_vehicleRow(id: 'v1', vin: 'VIN1', nickname: 'TheirsOlder', updatedAt: 1000));
|
||||
|
||||
await merge();
|
||||
|
||||
final rows = await local.query('vehicles');
|
||||
expect(rows, hasLength(1));
|
||||
expect(rows.first['make'], 'MineNewer');
|
||||
expect(rows.first['nickname'], 'MineNewer');
|
||||
expect(rows.first['dirty'], 1, reason: 'still pending push since local was not overwritten');
|
||||
});
|
||||
|
||||
test('a null nickname merges in fine (nickname is optional)', () async {
|
||||
await remote.insert(
|
||||
'vehicles', _vehicleRow(id: 'v1', vin: 'VIN1', nickname: null, updatedAt: 1000));
|
||||
|
||||
await merge();
|
||||
|
||||
final rows = await local.query('vehicles');
|
||||
expect(rows, hasLength(1));
|
||||
expect(rows.first['nickname'], isNull);
|
||||
});
|
||||
|
||||
test('the same vehicle edited on two devices merges by id, not vin', () async {
|
||||
// VIN is user-editable, so it can't be the merge key — this is the
|
||||
// scenario that motivated switching the merge key to a hidden id:
|
||||
// the local device renamed the VIN (a legitimate edit) while the
|
||||
// remote copy still has the old VIN and an older updated_at.
|
||||
await local.insert('vehicles',
|
||||
_vehicleRow(id: 'v1', vin: 'VIN1-CORRECTED', nickname: 'Mine', updatedAt: 2000, dirty: 1));
|
||||
await remote.insert(
|
||||
'vehicles', _vehicleRow(id: 'v1', vin: 'VIN1-TYPO', nickname: 'Mine', updatedAt: 1000));
|
||||
|
||||
await merge();
|
||||
|
||||
final rows = await local.query('vehicles');
|
||||
expect(rows, hasLength(1));
|
||||
expect(rows.first['vin'], 'VIN1-CORRECTED', reason: 'local was newer, so its VIN edit wins');
|
||||
});
|
||||
});
|
||||
|
||||
group('fuel entry merge', () {
|
||||
|
|
@ -134,17 +165,16 @@ Future<Database> _openFreshDb(String path) async {
|
|||
|
||||
Map<String, Object?> _vehicleRow({
|
||||
required String id,
|
||||
String make = 'Ford',
|
||||
required String vin,
|
||||
String? nickname = 'Test Vehicle',
|
||||
int updatedAt = 0,
|
||||
int? deletedAt,
|
||||
int dirty = 0,
|
||||
}) =>
|
||||
{
|
||||
'id': id,
|
||||
'make': make,
|
||||
'model': 'F-150',
|
||||
'color': 'Red',
|
||||
'license_plate': 'ABC123',
|
||||
'vin': vin,
|
||||
'nickname': nickname,
|
||||
'updated_at': updatedAt,
|
||||
'deleted_at': deletedAt,
|
||||
'dirty': dirty,
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue