import 'dart:convert'; import 'package:drift/native.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart' as http; import 'package:http/testing.dart'; import 'package:kooltap/database/app_database.dart'; import 'package:kooltap/models/payment_method.dart'; import 'package:kooltap/models/product.dart'; import 'package:kooltap/services/bar_tab_service.dart'; import 'package:kooltap/services/export_service.dart'; import 'package:package_info_plus/package_info_plus.dart'; const _beer = Product( id: 'beer', name: 'Beer', category: 'Drinks', stockQuantity: 10, lowStockThreshold: 2, priceInCents: 250, ); void main() { TestWidgetsFlutterBinding.ensureInitialized(); late AppDatabase database; late DriftBarTabService tabs; late List requests; late http.Response Function() respond; DefaultExportService exportService({String? token}) { return DefaultExportService( barTabService: tabs, authToken: () async => token, client: MockClient((request) async { requests.add(request); return respond(); }), ); } setUp(() async { PackageInfo.setMockInitialValues( appName: 'KoolTap', packageName: 'com.kooltap', version: '1.2.12', buildNumber: '1', buildSignature: '', ); database = AppDatabase(NativeDatabase.memory()); tabs = DriftBarTabService(database: database); requests = []; respond = () => http.Response('', 201); await database .into(database.products) .insert( ProductsCompanion.insert( id: _beer.id, name: _beer.name, category: _beer.category, stockQuantity: _beer.stockQuantity, lowStockThreshold: _beer.lowStockThreshold, priceInCents: _beer.priceInCents, ), ); }); tearDown(() async { await database.close(); }); test('backs up the totals that were charged', () async { final tab = await tabs.createTab(customerName: 'John'); for (var i = 0; i < 3; i++) { await tabs.addProductToTab(tabId: tab.id, product: _beer); } final item = (await tabs.getTabById(tab.id))!.items.single; await tabs.adjustTabItemQuantity(tabItemId: item.id, delta: -1); await tabs.closeTab(tab.id, paymentMethod: PaymentMethod.payconiq); await exportService().uploadToServer(); final body = jsonDecode(requests.single.body) as Map; final closed = (body['closedTabs'] as List).single as Map; final items = closed['items'] as List; expect(body['appVersion'], '1.2.12'); expect(closed['customerName'], 'John'); expect(closed['paymentMethod'], 'payconiq'); expect(closed['totalInCents'], 500); expect(closed['itemCount'], 2); expect(items, hasLength(3)); expect(items.where((item) => item['isRemoved'] == true), hasLength(1)); expect( items.fold( 0, (sum, item) => sum + (item['lineTotalInCents'] as int), ), closed['totalInCents'], ); }); test('sends the device token when linked', () async { await exportService(token: 'secret').uploadToServer(); expect(requests.single.headers['Authorization'], 'Bearer secret'); }); test('sends no token when not linked', () async { await exportService().uploadToServer(); expect(requests.single.headers.containsKey('Authorization'), false); }); test('never throws when the server is unreachable', () async { respond = () => throw http.ClientException('offline'); await expectLater(exportService().uploadToServer(), completes); }); test('never throws when the server errors', () async { respond = () => http.Response('oops', 500); await expectLater(exportService().uploadToServer(), completes); }); }