Files
emergency/test/services/location_service_test.dart
rootandClaude Sonnet 5 e3aa088bc2 Notrufnummern-Datenbasis, Ländererkennung, State-Management und Lokalisierung (AP1-AP7)
Implementiert den kompletten Kern-Flow der Emergency App gemäß docs/plan.md:
- Notrufnummern-Datenbasis für 199 Länder + Modelle/Repository (AP1)
- DialerService für tel:-Notrufe ohne Auto-Dial (AP2)
- Statisches UI mit Panik-Button-Fallback (AP3)
- Priorisierte Ländererkennung: SIM/Netz -> GPS/Geocoding -> Cache (AP4)
- EmergencyProvider verdrahtet Repository/LocationService mit der UI (AP5)
- Manuelle Länderauswahl als Fallback samt eigener Priorität (AP6)
- UI-Lokalisierung (de/en/fr/es), an erkanntes Land gekoppelt (AP7)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-24 11:29:38 +02:00

215 lines
6.2 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import 'package:flutter_test/flutter_test.dart';
import 'package:emergency/services/country_code_sources/country_code_source.dart';
import 'package:emergency/services/location_service.dart';
/// Einfache manuelle Fake-Quelle statt Mocking-Package (siehe AP4-Vorgabe:
/// kein neues Mocking-Package einführen).
class _FakeSource implements CountryCodeSource {
_FakeSource(this._result);
final String? _result;
int callCount = 0;
@override
Future<String?> resolve() async {
callCount++;
return _result;
}
}
class _FakeCache implements CountryCodeCache {
_FakeCache({String? initial}) : _stored = initial;
String? _stored;
int resolveCallCount = 0;
final List<String> savedCodes = [];
@override
Future<String?> resolve() async {
resolveCallCount++;
return _stored;
}
@override
Future<void> save(String isoCode) async {
savedCodes.add(isoCode);
_stored = isoCode;
}
}
/// Fake für die manuelle Auswahl (AP6). Ohne explizite Injektion würde
/// `LocationService()` intern ein echtes `ManualCountryCodeSource` (und damit
/// `shared_preferences`) ansprechen, was in diesen reinen Unit-Tests (kein
/// `TestWidgetsFlutterBinding.ensureInitialized()`/Mock-Setup) nicht
/// notwendig sein soll die bestehenden SIM/GPS/Cache-Tests sollen von der
/// manuellen Auswahl unbeeinflusst bleiben.
class _FakeManualSource implements ManualCountryCodeStore {
_FakeManualSource([this._result]);
final String? _result;
int resolveCallCount = 0;
final List<String?> savedCodes = [];
@override
Future<String?> resolve() async {
resolveCallCount++;
return _result;
}
@override
Future<void> save(String? isoCode) async {
savedCodes.add(isoCode);
}
}
void main() {
group('LocationService.resolveCountryCode()', () {
test(
'SIM liefert Ergebnis -> wird verwendet, GPS/Cache werden nicht abgefragt, '
'Ergebnis wird gecacht',
() async {
final sim = _FakeSource('DE');
final gps = _FakeSource('FR');
final cache = _FakeCache(initial: 'US');
final service = LocationService(
simSource: sim,
gpsSource: gps,
cacheSource: cache,
manualSource: _FakeManualSource(),
);
final result = await service.resolveCountryCode();
expect(result, 'DE');
expect(sim.callCount, 1);
expect(gps.callCount, 0);
expect(cache.resolveCallCount, 0);
expect(cache.savedCodes, ['DE']);
},
);
test(
'SIM liefert null, GPS liefert Ergebnis -> wird verwendet, Cache wird nicht '
'gelesen, aber beschrieben',
() async {
final sim = _FakeSource(null);
final gps = _FakeSource('FR');
final cache = _FakeCache(initial: 'US');
final service = LocationService(
simSource: sim,
gpsSource: gps,
cacheSource: cache,
manualSource: _FakeManualSource(),
);
final result = await service.resolveCountryCode();
expect(result, 'FR');
expect(sim.callCount, 1);
expect(gps.callCount, 1);
expect(cache.resolveCallCount, 0);
expect(cache.savedCodes, ['FR']);
},
);
test('SIM und GPS liefern null, Cache liefert Ergebnis -> wird verwendet', () async {
final sim = _FakeSource(null);
final gps = _FakeSource(null);
final cache = _FakeCache(initial: 'US');
final service = LocationService(
simSource: sim,
gpsSource: gps,
cacheSource: cache,
manualSource: _FakeManualSource(),
);
final result = await service.resolveCountryCode();
expect(result, 'US');
expect(cache.resolveCallCount, 1);
// Kein erneutes Schreiben, da das Ergebnis ja bereits aus dem Cache kam.
expect(cache.savedCodes, isEmpty);
});
test('Alle drei automatischen Quellen liefern null -> Ergebnis ist null', () async {
final sim = _FakeSource(null);
final gps = _FakeSource(null);
final cache = _FakeCache();
final service = LocationService(
simSource: sim,
gpsSource: gps,
cacheSource: cache,
manualSource: _FakeManualSource(),
);
final result = await service.resolveCountryCode();
expect(result, isNull);
expect(cache.savedCodes, isEmpty);
});
test(
'manuelle Auswahl gesetzt -> hat Vorrang vor SIM/GPS/Cache, die anderen '
'Quellen werden gar nicht erst abgefragt',
() async {
final sim = _FakeSource('DE');
final gps = _FakeSource('FR');
final cache = _FakeCache(initial: 'US');
final manual = _FakeManualSource('IT');
final service = LocationService(
simSource: sim,
gpsSource: gps,
cacheSource: cache,
manualSource: manual,
);
final result = await service.resolveCountryCode();
expect(result, 'IT');
expect(sim.callCount, 0);
expect(gps.callCount, 0);
expect(cache.resolveCallCount, 0);
// Die manuelle Auswahl wird bewusst nicht in den automatischen Cache
// geschrieben (getrennte Mechanismen, siehe Doc-Kommentar).
expect(cache.savedCodes, isEmpty);
},
);
test('keine manuelle Auswahl gesetzt -> Kette fällt normal auf SIM zurück', () async {
final sim = _FakeSource('DE');
final gps = _FakeSource('FR');
final cache = _FakeCache(initial: 'US');
final manual = _FakeManualSource(null);
final service = LocationService(
simSource: sim,
gpsSource: gps,
cacheSource: cache,
manualSource: manual,
);
final result = await service.resolveCountryCode();
expect(result, 'DE');
expect(manual.resolveCallCount, 1);
});
});
group('LocationService.setManualCountryCode()', () {
test('delegiert an die manuelle Quelle', () async {
final manual = _FakeManualSource();
final service = LocationService(
simSource: _FakeSource(null),
gpsSource: _FakeSource(null),
cacheSource: _FakeCache(),
manualSource: manual,
);
await service.setManualCountryCode('IT');
await service.setManualCountryCode(null);
expect(manual.savedCodes, ['IT', null]);
});
});
}