diff --git a/lib/models/drink.dart b/lib/models/drink.dart index 2486e1a1..cdb29a50 100644 --- a/lib/models/drink.dart +++ b/lib/models/drink.dart @@ -27,8 +27,8 @@ class Producer { } return Producer( - id: json['id'].toString(), - name: json['name'].toString(), + id: (json['id'] as String?) ?? '', + name: (json['name'] as String?) ?? '', location: (json['location'] ?? '').toString(), yearFounded: yearFounded, notes: json['notes']?.toString(), @@ -139,8 +139,8 @@ class Product { } return Product( - id: json['id'].toString(), - name: json['name'].toString(), + id: (json['id'] as String?) ?? '', + name: (json['name'] as String?) ?? '', category: (json['category'] ?? 'beer').toString(), style: json['style']?.toString(), dispense: (json['dispense'] ?? 'cask').toString(), diff --git a/lib/services/beer_api_service.dart b/lib/services/beer_api_service.dart index 5919a664..4941d8b6 100644 --- a/lib/services/beer_api_service.dart +++ b/lib/services/beer_api_service.dart @@ -109,7 +109,9 @@ class BeerApiService { for (final producerJson in producers) { final producer = Producer.fromJson(producerJson as Map); + if (producer.id.isEmpty) continue; for (final product in producer.products) { + if (product.id.isEmpty) continue; drinks.add(Drink( product: product, producer: producer, diff --git a/test/beer_api_service_test.dart b/test/beer_api_service_test.dart index 92bbb776..f4ec66be 100644 --- a/test/beer_api_service_test.dart +++ b/test/beer_api_service_test.dart @@ -241,6 +241,104 @@ void main() { expect(drinks.first.festivalId, 'my-festival-id'); }); + + test('skips products with missing ids', () async { + service = BeerApiService(client: mockClient); + + const festival = Festival( + id: 'cbf2025', + name: 'Test Festival', + dataBaseUrl: 'https://example.com', + availableBeverageTypes: ['beer'], + ); + + final responseBody = json.encode({ + 'producers': [ + { + 'id': 'brewery-1', + 'name': 'Test Brewery', + 'location': 'Cambridge', + 'products': [ + { + 'id': null, + 'name': 'No Id Beer', + 'category': 'beer', + 'dispense': 'cask', + 'abv': '4.0', + }, + { + 'id': 'drink-2', + 'name': 'Valid Beer', + 'category': 'beer', + 'dispense': 'cask', + 'abv': '4.5', + }, + ], + }, + ], + }); + + when(mockClient.get(Uri.parse('https://example.com/beer.json'))) + .thenAnswer((_) async => http.Response(responseBody, 200)); + + final drinks = await service.fetchDrinks(festival, 'beer'); + + expect(drinks.length, 1); + expect(drinks.first.id, 'drink-2'); + }); + + test('skips producers with missing ids', () async { + service = BeerApiService(client: mockClient); + + const festival = Festival( + id: 'cbf2025', + name: 'Test Festival', + dataBaseUrl: 'https://example.com', + availableBeverageTypes: ['beer'], + ); + + final responseBody = json.encode({ + 'producers': [ + { + 'id': null, + 'name': 'Missing Id Brewery', + 'location': 'Cambridge', + 'products': [ + { + 'id': 'drink-ignored', + 'name': 'Ignored Beer', + 'category': 'beer', + 'dispense': 'cask', + 'abv': '4.0', + }, + ], + }, + { + 'id': 'brewery-2', + 'name': 'Valid Brewery', + 'location': 'Cambridge', + 'products': [ + { + 'id': 'drink-2', + 'name': 'Valid Beer', + 'category': 'beer', + 'dispense': 'cask', + 'abv': '4.5', + }, + ], + }, + ], + }); + + when(mockClient.get(Uri.parse('https://example.com/beer.json'))) + .thenAnswer((_) async => http.Response(responseBody, 200)); + + final drinks = await service.fetchDrinks(festival, 'beer'); + + expect(drinks.length, 1); + expect(drinks.first.id, 'drink-2'); + expect(drinks.first.producer.id, 'brewery-2'); + }); }); group('fetchAllDrinks', () { diff --git a/test/models_test.dart b/test/models_test.dart index ba04f1e8..91997af4 100644 --- a/test/models_test.dart +++ b/test/models_test.dart @@ -52,6 +52,19 @@ void main() { expect(product.allergens, isEmpty); }); + test('fromJson maps missing id and name to empty strings', () { + final product = Product.fromJson({ + 'id': null, + 'name': null, + 'category': 'beer', + 'dispense': 'cask', + 'abv': '4.0', + }); + + expect(product.id, ''); + expect(product.name, ''); + }); + test('availabilityStatus returns correct values', () { expect( Product.fromJson({ @@ -722,6 +735,18 @@ void main() { expect(producer.location, ''); }); + test('fromJson maps missing id and name to empty strings', () { + final producer = Producer.fromJson({ + 'id': null, + 'name': null, + 'location': 'Somewhere', + 'products': [], + }); + + expect(producer.id, ''); + expect(producer.name, ''); + }); + group('toJson', () { test('converts producer to JSON correctly', () { const product = Product(