Files
doormile_milderapp/test/tenant_refresh_test.dart
2026-08-28 11:13:15 +05:30

122 lines
4.4 KiB
Dart

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<String, dynamic> 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<void>.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();
}