feat(proj): init

This commit is contained in:
vl.arkhangelskii
2026-09-21 04:06:43 +03:00
commit c956b94983
1076 changed files with 50876 additions and 0 deletions
+188
View File
@@ -0,0 +1,188 @@
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);
});
});
}