import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_app/halkyon_app.dart'; import 'package:serverpod_offline_sync_client/offline_sync.dart'; void main() { group('AppCompatibility', () { late int asked; late Object? answer; AppCompatibility compatibility(int? appBuild) => AppCompatibility( appBuild: appBuild, minimumAppBuild: () { asked++; return switch (answer) { final Exception error => Future.error(error), _ => Future.value(answer as int?), }; }, ); setUp(() { asked = 0; answer = null; }); test('lets any build sync when the server names no minimum', () async { await compatibility(1).check(); }); test('lets a build at or above the minimum sync', () async { answer = 3; await compatibility(3).check(); await compatibility(4).check(); }); test('refuses a build below the minimum', () async { answer = 3; await expectLater( compatibility(2).check(), throwsA(isA()), ); }); test('asks the server once per launch', () async { answer = 1; final check = compatibility(1); await check.check(); await check.check(); expect(asked, 1); }); test('asks again after the question failed', () async { answer = Exception('No connection'); final check = compatibility(1); await expectLater(check.check(), throwsException); answer = 1; await check.check(); expect(asked, 2); }); test('never refuses a build without a number, and never asks', () async { answer = 99; await compatibility(null).check(); expect(asked, 0); }); }); group('compatibleSync', () { test('checks before syncing', () async { var synced = false; final sync = compatibleSync( ({required onMerge}) { synced = true; return Future.value(); }, AppCompatibility(appBuild: 1, minimumAppBuild: () => Future.value(2)), ); await expectLater( sync(onMerge: () {}), throwsA(isA()), ); expect(synced, isFalse); }); test("tells when the synced tables don't match the server's", () async { final sync = compatibleSync( ({required onMerge}) => Future.error( const OfflineSyncTablesHashMismatchException( received: 'server', expected: 'app', ), ), AppCompatibility( appBuild: 1, minimumAppBuild: Future.value, ), ); await expectLater( sync(onMerge: () {}), throwsA(isA()), ); }); test('passes other failures on as they are', () async { final failure = Exception('No connection'); final sync = compatibleSync( ({required onMerge}) => Future.error(failure), AppCompatibility( appBuild: 1, minimumAppBuild: Future.value, ), ); await expectLater(sync(onMerge: () {}), throwsA(same(failure))); }); }); }