feat: harden geofence and distance diagnostics
This commit is contained in:
41
test/geofence_logic_test.dart
Normal file
41
test/geofence_logic_test.dart
Normal file
@@ -0,0 +1,41 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:geolocator/geolocator.dart';
|
||||
import 'package:ignis_app/features/homes/geofence_logic.dart';
|
||||
import 'package:ignis_app/features/homes/providers/location_providers.dart';
|
||||
|
||||
void main() {
|
||||
test('distance formatting stays readable across ranges', () {
|
||||
expect(formatDistance(0.42), '420 м');
|
||||
expect(formatDistance(2.34), '2.3 км');
|
||||
expect(formatDistance(12.8), '13 км');
|
||||
expect(formatDistanceMeters(450), '450 м');
|
||||
expect(formatDistanceMeters(1450), '1.4 км');
|
||||
});
|
||||
|
||||
test('distance calculation stays in realistic range', () {
|
||||
final distanceKm = calculateDistanceKm(55.75, 37.61, 55.76, 37.61);
|
||||
expect(distanceKm, closeTo(1.11, 0.15));
|
||||
});
|
||||
|
||||
test('background location access requires always permission', () {
|
||||
expect(hasForegroundLocationAccess(LocationPermission.whileInUse), isTrue);
|
||||
expect(hasBackgroundLocationAccess(LocationPermission.whileInUse), isFalse);
|
||||
expect(hasBackgroundLocationAccess(LocationPermission.always), isTrue);
|
||||
});
|
||||
|
||||
test('retry remaining expires after cooldown window', () {
|
||||
final now = DateTime(2026, 5, 1, 12, 0, 0);
|
||||
final lastFailure = now.subtract(const Duration(minutes: 10));
|
||||
final retryRemaining = geofenceRetryRemaining(lastFailure, now: now);
|
||||
|
||||
expect(retryRemaining, isNotNull);
|
||||
expect(retryRemaining!.inMinutes, 20);
|
||||
expect(
|
||||
geofenceRetryRemaining(
|
||||
now.subtract(const Duration(minutes: 31)),
|
||||
now: now,
|
||||
),
|
||||
isNull,
|
||||
);
|
||||
});
|
||||
}
|
||||
79
test/geofence_runtime_store_test.dart
Normal file
79
test/geofence_runtime_store_test.dart
Normal file
@@ -0,0 +1,79 @@
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:ignis_app/features/homes/services/geofence_runtime_store.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
test('runtime store persists armed home and success markers', () async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
final store = GeofenceRuntimeStore();
|
||||
await store.armForHome('home-1');
|
||||
|
||||
var runtime = await store.load();
|
||||
expect(runtime.armedHomeId, 'home-1');
|
||||
|
||||
runtime = runtime.recordSuccess(
|
||||
'home-1',
|
||||
triggeredAt: DateTime(2026, 5, 1, 18, 30),
|
||||
distanceMeters: 820,
|
||||
);
|
||||
await store.save(runtime);
|
||||
|
||||
final loaded = await store.load();
|
||||
expect(loaded.isTriggeredFor('home-1'), isTrue);
|
||||
expect(loaded.lastSuccessHomeId, 'home-1');
|
||||
expect(loaded.lastDistanceMeters, 820);
|
||||
});
|
||||
|
||||
test(
|
||||
'returning into home radius rearms geofence and clears failure',
|
||||
() async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
final store = GeofenceRuntimeStore();
|
||||
var runtime = await store.armForHome('home-1');
|
||||
runtime = runtime.recordFailure(
|
||||
'home-1',
|
||||
failedAt: DateTime(2026, 5, 1, 19, 0),
|
||||
distanceMeters: 900,
|
||||
message: 'Backend умер по дороге.',
|
||||
);
|
||||
runtime = runtime.recordInsideHome(
|
||||
'home-1',
|
||||
checkedAt: DateTime(2026, 5, 1, 22, 0),
|
||||
distanceMeters: 120,
|
||||
);
|
||||
await store.save(runtime);
|
||||
|
||||
final loaded = await store.load();
|
||||
expect(loaded.failureAtFor('home-1'), isNull);
|
||||
expect(loaded.isTriggeredFor('home-1'), isFalse);
|
||||
expect(loaded.lastDistanceMeters, 120);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'removing home clears armed and historical runtime for that home',
|
||||
() async {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
|
||||
final store = GeofenceRuntimeStore();
|
||||
var runtime = await store.armForHome('home-1');
|
||||
runtime = runtime.recordSuccess(
|
||||
'home-1',
|
||||
triggeredAt: DateTime(2026, 5, 1, 20, 0),
|
||||
distanceMeters: 760,
|
||||
);
|
||||
await store.save(runtime);
|
||||
|
||||
await store.removeHome('home-1');
|
||||
final loaded = await store.load();
|
||||
|
||||
expect(loaded.armedHomeId, isNull);
|
||||
expect(loaded.lastSuccessHomeId, isNull);
|
||||
expect(loaded.triggeredHomeId, isNull);
|
||||
},
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user