189 lines
5.9 KiB
Dart
189 lines
5.9 KiB
Dart
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);
|
|
});
|
|
});
|
|
}
|