feat: add device authentication, place gates support, and PiP navigation features
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
import 'dart:convert';
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:http/testing.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:siro_maps/core/constants/api_constants.dart';
|
||||
import 'package:siro_maps/core/services/device_fingerprint_service.dart';
|
||||
import 'package:siro_maps/data/models/place_gate.dart';
|
||||
import 'package:siro_maps/data/models/place_model.dart';
|
||||
|
||||
void main() {
|
||||
TestWidgetsFlutterBinding.ensureInitialized();
|
||||
|
||||
group('PlaceGate & PlaceModel Tests', () {
|
||||
test('PlaceGate parses json correctly', () {
|
||||
final json = {
|
||||
'name_ar': 'بوابة رقم 1 - كارفور',
|
||||
'name_en': 'Gate 1 - Carrefour',
|
||||
'latitude': 31.9712,
|
||||
'longitude': 35.8456,
|
||||
'is_main_gate': true,
|
||||
};
|
||||
|
||||
final gate = PlaceGate.fromJson(json);
|
||||
|
||||
expect(gate.nameAr, equals('بوابة رقم 1 - كارفور'));
|
||||
expect(gate.nameEn, equals('Gate 1 - Carrefour'));
|
||||
expect(gate.latitude, equals(31.9712));
|
||||
expect(gate.longitude, equals(35.8456));
|
||||
expect(gate.isMainGate, isTrue);
|
||||
|
||||
final outJson = gate.toJson();
|
||||
expect(outJson['name_ar'], equals('بوابة رقم 1 - كارفور'));
|
||||
expect(outJson['is_main_gate'], isTrue);
|
||||
});
|
||||
|
||||
test('PlaceModel parses gates array from API payload', () {
|
||||
final rawPlace = {
|
||||
'id': 'poi_city_mall',
|
||||
'name': 'سيتي مول عمان',
|
||||
'category': 'mall',
|
||||
'latitude': 31.9823,
|
||||
'longitude': 35.8344,
|
||||
'elevation_meters': 980.5,
|
||||
'gates': [
|
||||
{
|
||||
'name_ar': 'بوابة 1 - الطوارئ والخدمات',
|
||||
'latitude': 31.9820,
|
||||
'longitude': 35.8340,
|
||||
'is_main_gate': false,
|
||||
},
|
||||
{
|
||||
'name_ar': 'بوابة 2 - المدخل الرئيسي والزوار',
|
||||
'latitude': 31.9825,
|
||||
'longitude': 35.8346,
|
||||
'is_main_gate': true,
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
final place = PlaceModel.fromJson(rawPlace);
|
||||
|
||||
expect(place.id, equals('poi_city_mall'));
|
||||
expect(place.name, equals('سيتي مول عمان'));
|
||||
expect(place.hasGates, isTrue);
|
||||
expect(place.gates.length, equals(2));
|
||||
expect(place.gates.first.nameAr, equals('بوابة 1 - الطوارئ والخدمات'));
|
||||
expect(place.gates.last.isMainGate, isTrue);
|
||||
});
|
||||
|
||||
test('PlaceModel handles empty or null gates gracefully', () {
|
||||
final rawPlace = {
|
||||
'name': 'كافيه عادي',
|
||||
'category': 'cafe',
|
||||
'latitude': 31.95,
|
||||
'longitude': 35.91,
|
||||
};
|
||||
|
||||
final place = PlaceModel.fromJson(rawPlace);
|
||||
|
||||
expect(place.hasGates, isFalse);
|
||||
expect(place.gates, isEmpty);
|
||||
});
|
||||
});
|
||||
|
||||
group('DeviceFingerprintService Tests', () {
|
||||
setUp(() {
|
||||
SharedPreferences.setMockInitialValues({});
|
||||
});
|
||||
|
||||
test('Generates deterministic hardware fingerprint and initializes headers', () async {
|
||||
final service = DeviceFingerprintService.instance;
|
||||
await service.init();
|
||||
|
||||
expect(service.fingerprintId, isNotEmpty);
|
||||
expect(service.fingerprintId.startsWith('siro_'), isTrue);
|
||||
expect(service.shortFingerprint.isNotEmpty, isTrue);
|
||||
|
||||
final headers = service.headers;
|
||||
expect(headers.containsKey('x-device-fingerprint'), isTrue);
|
||||
expect(headers['x-device-fingerprint'], equals(service.fingerprintId));
|
||||
expect(headers.containsKey('x-client-version'), isTrue);
|
||||
expect(headers.containsKey('x-device-platform'), isTrue);
|
||||
expect(headers.containsKey('x-api-key'), isTrue);
|
||||
expect(headers['x-api-key'], isNotEmpty);
|
||||
});
|
||||
|
||||
test('Provisions dedicated consumer API key and switches activeApiKey', () async {
|
||||
final service = DeviceFingerprintService.instance;
|
||||
|
||||
final mockClient = MockClient((request) async {
|
||||
if (request.url.path.contains('/auth/device-provision')) {
|
||||
return http.Response(
|
||||
jsonEncode({
|
||||
'apiKey': 'in_mob_unit_test_dedicated_key_998877',
|
||||
'keyName': 'Siro Mobile - Unit Test Device',
|
||||
'rateLimit': 60,
|
||||
'plan': 'FREE',
|
||||
'deviceFingerprint': service.fingerprintId,
|
||||
'isNew': true,
|
||||
}),
|
||||
200,
|
||||
headers: {'content-type': 'application/json'},
|
||||
);
|
||||
}
|
||||
return http.Response('Not Found', 404);
|
||||
});
|
||||
|
||||
final success = await service.provisionDedicatedApiKey(httpClient: mockClient);
|
||||
|
||||
expect(success, isTrue);
|
||||
expect(service.activeApiKey, equals('in_mob_unit_test_dedicated_key_998877'));
|
||||
expect(service.isDedicatedKeyActive, isTrue);
|
||||
expect(service.rateLimit, equals(60));
|
||||
expect(service.headers['x-api-key'], equals('in_mob_unit_test_dedicated_key_998877'));
|
||||
});
|
||||
|
||||
test('Falls back gracefully to embedded key if provisioning fails or is offline', () async {
|
||||
final service = DeviceFingerprintService.instance;
|
||||
|
||||
final offlineClient = MockClient((request) async {
|
||||
return http.Response('Internal Server Error', 500);
|
||||
});
|
||||
|
||||
service.setMockState(
|
||||
fingerprint: 'siro_test_hw_offline_device',
|
||||
clearApiKey: true,
|
||||
);
|
||||
|
||||
final success = await service.provisionDedicatedApiKey(httpClient: offlineClient);
|
||||
|
||||
expect(success, isFalse);
|
||||
// Falls back to master MapSaaS key without crashing
|
||||
expect(service.activeApiKey, equals(ApiConstants.mapSaasKey));
|
||||
expect(service.isDedicatedKeyActive, isFalse);
|
||||
});
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user