import 'package:flutter_test/flutter_test.dart'; import 'package:mocktail/mocktail.dart'; import 'package:kooltab2/models/product.dart'; import 'package:kooltab2/services/product_service.dart'; import 'package:kooltab2/viewmodels/inventory_view_model.dart'; class MockProductService extends Mock implements ProductService {} void main() { late InventoryViewModel viewModel; late MockProductService mockService; setUp(() { mockService = MockProductService(); viewModel = InventoryViewModel(productService: mockService); }); setUpAll(() { registerFallbackValue(const Product( id: 'fallback', name: 'Fallback', category: 'Test', stockQuantity: 0, lowStockThreshold: 0, priceInCents: 0, )); }); final testProducts = [ const Product( id: 'prod-1', name: 'Beer', category: 'Drinks', stockQuantity: 20, lowStockThreshold: 5, priceInCents: 400, ), const Product( id: 'prod-2', name: 'Wine', category: 'Drinks', stockQuantity: 3, lowStockThreshold: 5, priceInCents: 600, ), ]; group('InventoryViewModel', () { group('load', () { test('loads products from service', () async { when(() => mockService.getProducts()) .thenAnswer((_) async => testProducts); await viewModel.load(); expect(viewModel.products, testProducts); verify(() => mockService.getProducts()).called(1); }); test('notifies listeners after load', () async { var notifyCount = 0; viewModel.addListener(() => notifyCount++); when(() => mockService.getProducts()) .thenAnswer((_) async => testProducts); await viewModel.load(); expect(notifyCount, 1); }); }); group('decreaseStock', () { test('decreases stock via service', () async { when(() => mockService.decreaseStock('prod-1', 3)) .thenAnswer((_) async {}); await viewModel.decreaseStock('prod-1', 3); verify(() => mockService.decreaseStock('prod-1', 3)).called(1); }); test('updates local product cache', () async { when(() => mockService.getProducts()) .thenAnswer((_) async => testProducts); when(() => mockService.decreaseStock('prod-1', 5)) .thenAnswer((_) async {}); await viewModel.load(); await viewModel.decreaseStock('prod-1', 5); final product = viewModel.products.firstWhere((p) => p.id == 'prod-1'); expect(product.stockQuantity, 15); }); test('notifies listeners after decrease', () async { var notifyCount = 0; viewModel.addListener(() => notifyCount++); when(() => mockService.decreaseStock('prod-1', 1)) .thenAnswer((_) async {}); await viewModel.decreaseStock('prod-1', 1); expect(notifyCount, 1); }); }); group('increaseStock', () { test('increases stock via service', () async { when(() => mockService.increaseStock('prod-1', 5)) .thenAnswer((_) async {}); await viewModel.increaseStock('prod-1', 5); verify(() => mockService.increaseStock('prod-1', 5)).called(1); }); test('updates local product cache', () async { when(() => mockService.getProducts()) .thenAnswer((_) async => testProducts); when(() => mockService.increaseStock('prod-2', 2)) .thenAnswer((_) async {}); await viewModel.load(); await viewModel.increaseStock('prod-2', 2); final product = viewModel.products.firstWhere((p) => p.id == 'prod-2'); expect(product.stockQuantity, 5); }); test('notifies listeners after increase', () async { var notifyCount = 0; viewModel.addListener(() => notifyCount++); when(() => mockService.increaseStock('prod-1', 1)) .thenAnswer((_) async {}); await viewModel.increaseStock('prod-1', 1); expect(notifyCount, 1); }); }); group('edge cases', () { test('handles decrease for product not in local cache', () async { when(() => mockService.getProducts()) .thenAnswer((_) async => testProducts); when(() => mockService.decreaseStock('unknown', 1)) .thenAnswer((_) async {}); await viewModel.load(); await viewModel.decreaseStock('unknown', 1); verify(() => mockService.decreaseStock('unknown', 1)).called(1); expect(viewModel.products.length, 2); }); }); }); }