import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_bloc/flutter_bloc.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:hl_lieferservice/data/network/auth_session_event.dart'; import 'package:hl_lieferservice/data/network/keycloak_oidc_token_provider.dart'; import 'package:hl_lieferservice/domain/entity/payment_method.dart'; import 'package:hl_lieferservice/domain/repository/payment_methods_repository.dart'; import 'package:hl_lieferservice/feature/authentication/bloc/auth_bloc.dart'; import 'package:hl_lieferservice/feature/car_selection/bloc/bloc.dart'; import 'package:hl_lieferservice/feature/delivery/bloc/tour_date_cubit.dart'; import 'package:hl_lieferservice/feature/payment_methods/bloc/payment_methods_cubit.dart'; import 'package:hl_lieferservice/main.dart' show locator; import 'package:hl_lieferservice/widget/operations/bloc/operation_bloc.dart'; import 'package:hl_lieferservice/widget/session_scope.dart'; import 'package:holzleitner_api/holzleitner_api.dart' show HolzleitnerApi; /// Token-Provider ohne Keycloak: der Test steuert Login/Logout über [emit]. class _FakeTokenProvider implements KeycloakOidcTokenProvider { final _events = StreamController.broadcast(); void emit(AuthSessionEvent event) => _events.add(event); @override Stream get events => _events.stream; @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } class _CountingPaymentMethods implements PaymentMethodsRepository { int loads = 0; @override Future> list({bool includeInactive = false}) async { loads++; return const []; } @override dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation); } AuthLoggedIn _login(int personalnummer) => AuthLoggedIn({'personalnummer': personalnummer, 'name': 'Fahrer'}); void main() { // Session-Blocs bauen ihre Repositories gegen den API-Client; im Test // reicht ein unbenutzter Client (es wird nichts geladen). setUpAll(() { if (!locator.isRegistered()) { locator.registerSingleton(HolzleitnerApi()); } }); late _FakeTokenProvider provider; late _CountingPaymentMethods payments; late List carSelections; late List tourDates; Future pumpApp(WidgetTester tester) async { provider = _FakeTokenProvider(); payments = _CountingPaymentMethods(); carSelections = []; tourDates = []; await tester.pumpWidget( MultiBlocProvider( providers: [ BlocProvider(create: (_) => OperationBloc()), BlocProvider( create: (context) => AuthBloc( tokenProvider: provider, operationBloc: context.read(), ), ), BlocProvider( create: (_) => PaymentMethodsCubit(repository: payments), ), ], child: SessionScope( environmentId: 'test', child: MaterialApp( home: Builder( builder: (context) { carSelections.add(context.read()); tourDates.add(context.read()); return const SizedBox.shrink(); }, ), ), ), ), ); } Future send(WidgetTester tester, AuthSessionEvent event) async { provider.emit(event); await tester.pumpAndSettle(); } testWidgets('jede Anmeldung bekommt frische Fahrer-Blocs', (tester) async { await pumpApp(tester); await send(tester, _login(423)); final fuer423 = carSelections.last; expect(payments.loads, 1); // 423 schaut sich einen anderen Tag an … tourDates.last.pick(DateTime(2026, 9, 2)); // Abmelden und anderer Fahrer: nichts von 423 darf übrig bleiben. await send(tester, const AuthLoggedOut()); await send(tester, _login(414)); expect(carSelections.last, isNot(same(fuer423))); expect(tourDates.last, isNot(same(tourDates.first))); // … der nächste Fahrer startet trotzdem auf „heute". expect(tourDates.last.state, isNull); expect(payments.loads, 2); // Auch derselbe Fahrer nach erneutem Login lädt neu. final fuer414 = carSelections.last; await send(tester, const AuthLoggedOut()); await send(tester, _login(414)); expect(carSelections.last, isNot(same(fuer414))); expect(payments.loads, 3); }); testWidgets('Token-Refresh desselben Fahrers behält die Blocs', (tester) async { await pumpApp(tester); await send(tester, _login(423)); final vorher = carSelections.last; await send(tester, _login(423)); expect(carSelections.last, same(vorher)); expect(payments.loads, 1); }); }