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:miler/data/miler_api.dart'; import 'package:miler/data/service_profile.dart'; /// ───────────────────────────────────────────────────────────────────────── /// THE TENANT, REFRESHED WITHOUT A SIGN-OUT /// /// `tenantname` was written at verify-pin and nowhere else, so it only reached /// a device by way of a **login**. A rider already signed in when the backend /// began returning the field would never receive it: prefs carry no name, /// resolution falls back to the tenant id, and an id this app has not been told /// about lands him on logistics — no **Start delivery** button, and a parcel /// stranded at `Collected_By_Miler` the moment collected-state is enabled. /// /// `GET /miler/profile` returns the same pair, so the answer is refreshable in /// place. These pin that, and pin the three ways it must fail quietly: a launch /// with no signal, a signed-out device, and a handler that answers without the /// field. /// ───────────────────────────────────────────────────────────────────────── void main() { TestWidgetsFlutterBinding.ensureInitialized(); tearDown(() { MilerApi.client = http.Client(); ServiceProfile.setActive(ServiceProfile.parcel); }); void stubProfile(Map body, {int code = 200}) { MilerApi.client = MockClient( (req) async => http.Response( json.encode(body), code, headers: const {'content-type': 'application/json'}, ), ); } test('a signed-in rider picks up his tenant name in place', () async { SharedPreferences.setMockInitialValues({'authtoken': 'tok'}); stubProfile({ 'success': true, 'data': {'tenantid': 41, 'tenantname': 'DailyGrubs'}, }); await refreshTenantFromProfile(); expect( await resolveServiceProfile(), ServiceProfile.milkMan, reason: 'no sign-out should be required to receive the tenant', ); }); test('the pair is read from inside `user` as well as top level', () async { // The backend returns it both ways; a handler that later moves it must not // silently stop resolving riders. SharedPreferences.setMockInitialValues({'authtoken': 'tok'}); stubProfile({ 'success': true, 'data': { 'user': {'tenantid': 41, 'tenantname': 'Daily Grubs'}, }, }); await refreshTenantFromProfile(); expect(await resolveServiceProfile(), ServiceProfile.milkMan); }); test('a signed-out device asks for nothing', () async { var called = false; MilerApi.client = MockClient((req) async { called = true; return http.Response('{}', 200); }); SharedPreferences.setMockInitialValues(const {}); await refreshTenantFromProfile(); expect(called, isFalse, reason: 'no token, no request'); }); test('a launch with no signal keeps what it already had', () async { SharedPreferences.setMockInitialValues({ 'authtoken': 'tok', TenantController.kTenantName: 'DailyGrubs', }); MilerApi.client = MockClient((req) async => throw const SocketishFailure()); // Must not throw: a launch offline lands on the same screen it always did. await refreshTenantFromProfile(); expect(await resolveServiceProfile(), ServiceProfile.milkMan); }); test('a response without the field does not erase the stored one', () async { SharedPreferences.setMockInitialValues({ 'authtoken': 'tok', TenantController.kTenantName: 'DailyGrubs', }); stubProfile({ 'success': true, 'data': {'displayname': 'Rajan A'}, }); await refreshTenantFromProfile(); // Backgrounded for a known rider, so let it land before asserting. await Future.delayed(const Duration(milliseconds: 50)); expect(await resolveServiceProfile(), ServiceProfile.milkMan); }); } /// A stand-in for a dead network, so the test does not depend on `dart:io` /// error construction details. class SocketishFailure implements Exception { const SocketishFailure(); }