import 'package:flutter_test/flutter_test.dart'; import 'package:please_pay_me/core/config/app_config.dart'; import 'package:please_pay_me/core/storage/session_storage.dart'; import 'package:please_pay_me/data/demo/demo_backend.dart'; import 'package:please_pay_me/features/auth/session_controller.dart'; import 'package:please_pay_me/features/budgets/budgets_controller.dart'; import 'package:please_pay_me/features/journal/journal_controller.dart'; import 'package:please_pay_me/features/work/jobs_controller.dart'; void main() { late DemoBackend backend; setUp(() => backend = DemoBackend(today: DateTime(2026, 9, 19))); group('BudgetsController', () { test('loads budgets and exposes the selected one', () async { final controller = BudgetsController( budgets: backend.budgets, expenses: backend.expenses, ); await controller.load(); expect(controller.items, hasLength(2)); expect(controller.selected?.budget.name, 'До аванса'); expect(controller.hasBudgets, isTrue); }); test('adding an expense lowers the remaining amount', () async { final controller = BudgetsController( budgets: backend.budgets, expenses: backend.expenses, ); await controller.load(); final before = controller.selected!.remaining; final error = await controller.addExpense(amount: 500, note: 'Обед'); expect(error, isNull); expect(controller.selected!.remaining, before - 500); expect(controller.isMutating, isFalse); }); test('undo removes the newest expense of the selected budget', () async { final controller = BudgetsController( budgets: backend.budgets, expenses: backend.expenses, ); await controller.load(); await controller.addExpense(amount: 777); final withExtra = controller.selected!.totalSpent; await controller.undoLastExpense(); expect(controller.selected!.totalSpent, withExtra - 777); }); test('select switches the current envelope', () async { final controller = BudgetsController( budgets: backend.budgets, expenses: backend.expenses, ); await controller.load(); await controller.select(2); expect(controller.selected?.budget.id, 2); }); test('surfaces backend errors without crashing', () async { final empty = DemoBackend.empty(); final controller = BudgetsController( budgets: empty.budgets, expenses: empty.expenses, ); await controller.load(); final error = await controller.addExpense(amount: 100); expect(error, 'Сначала создайте бюджет'); expect(controller.hasBudgets, isFalse); }); }); group('JournalController', () { test('groups operations by day, newest first', () async { final controller = JournalController(expenses: backend.expenses); await controller.load(); final groups = controller.groups; expect(groups.first.day, DateTime(2026, 9, 19)); expect(groups.first.items, hasLength(2)); expect(groups.first.total, 2090); expect(groups.map((g) => g.day), isNot(contains(DateTime(2026, 8, 30)))); }); test('scope "all" includes other budgets', () async { final controller = JournalController(expenses: backend.expenses); await controller.load(); final currentCount = controller.items.length; await controller.setScope(JournalScope.all); expect(controller.items.length, greaterThan(currentCount)); expect(controller.scope, JournalScope.all); }); test('loadMore appends the next page', () async { final controller = JournalController(expenses: backend.expenses, pageSize: 2); await controller.load(); expect(controller.items, hasLength(2)); expect(controller.hasMore, isTrue); await controller.loadMore(); expect(controller.items, hasLength(4)); expect(controller.hasMore, isFalse); }); }); group('JobsController', () { test('nextPay picks the closest payday', () async { final controller = JobsController(jobs: backend.jobs); await controller.load(); expect(controller.items, hasLength(1)); expect(controller.nextPay?.date, DateTime(2026, 9, 27)); }); }); group('SessionController', () { test('restores a stored session', () async { final session = SessionController( config: AppConfig.demo, storage: InMemorySessionStorage({ 'token': 'jwt', 'baseUrl': 'https://ppm.example.com', 'user': '{"user_id":7,"username":"vlad"}', }), ); await session.restore(); expect(session.status, SessionStatus.signedIn); expect(session.user?.userId, 7); expect(session.isDemo, isFalse); }); test('without a stored token the app asks to sign in', () async { final session = SessionController( config: AppConfig.demo, storage: InMemorySessionStorage(), ); await session.restore(); expect(session.status, SessionStatus.signedOut); }); test('demo mode serves repositories from the in-memory backend', () async { final session = SessionController( config: AppConfig.demo, storage: InMemorySessionStorage(), demoBackend: backend, )..startDemo(); expect(session.status, SessionStatus.signedIn); expect(await session.budgets.list(), hasLength(2)); expect(session.sessionKey, contains('demo')); }); test('sign out clears the session', () async { final storage = InMemorySessionStorage({ 'token': 'jwt', 'baseUrl': 'https://ppm.example.com', 'user': '{"user_id":7}', }); final session = SessionController(config: AppConfig.demo, storage: storage); await session.restore(); await session.signOut(); expect(session.status, SessionStatus.signedOut); expect(await storage.readAll(), isEmpty); }); }); }