diff --git a/lib/exceptions/no_such_entry_exception.dart b/lib/exceptions/no_such_entry_exception.dart new file mode 100644 index 00000000..d6b3fea6 --- /dev/null +++ b/lib/exceptions/no_such_entry_exception.dart @@ -0,0 +1 @@ +class NoSuchEntryException implements Exception {} diff --git a/lib/models/measurements/measurement_category.dart b/lib/models/measurements/measurement_category.dart index 79f53140..c58c794d 100644 --- a/lib/models/measurements/measurement_category.dart +++ b/lib/models/measurements/measurement_category.dart @@ -7,7 +7,7 @@ part 'measurement_category.g.dart'; @JsonSerializable(explicitToJson: true) class MeasurementCategory extends Equatable { @JsonKey(required: true) - final int id; + final int? id; @JsonKey(required: true) final String name; @@ -15,10 +15,8 @@ class MeasurementCategory extends Equatable { @JsonKey(required: true) final String unit; - @JsonKey( - defaultValue: [], - ) - List entries; + @JsonKey(defaultValue: [], toJson: _nullValue) + final List entries; MeasurementCategory({ required this.id, @@ -27,6 +25,20 @@ class MeasurementCategory extends Equatable { this.entries = const [], }); + MeasurementCategory copyWith({ + int? id, + String? name, + String? unit, + List? entries, + }) { + return MeasurementCategory( + id: id ?? this.id, + name: name ?? this.name, + unit: unit ?? this.unit, + entries: entries ?? this.entries, + ); + } + // Boilerplate factory MeasurementCategory.fromJson(Map json) => _$MeasurementCategoryFromJson(json); @@ -34,5 +46,8 @@ class MeasurementCategory extends Equatable { Map toJson() => _$MeasurementCategoryToJson(this); @override - List get props => [id, name, unit, entries]; + List get props => [id, name, unit, entries]; + + // Helper function which makes the entries list of the toJson output null, as it isn't needed + static _nullValue(_) => null; } diff --git a/lib/models/measurements/measurement_category.g.dart b/lib/models/measurements/measurement_category.g.dart index ebed3f19..315a40d2 100644 --- a/lib/models/measurements/measurement_category.g.dart +++ b/lib/models/measurements/measurement_category.g.dart @@ -9,7 +9,7 @@ part of 'measurement_category.dart'; MeasurementCategory _$MeasurementCategoryFromJson(Map json) { $checkKeys(json, requiredKeys: const ['id', 'name', 'unit']); return MeasurementCategory( - id: json['id'] as int, + id: json['id'] as int?, name: json['name'] as String, unit: json['unit'] as String, entries: (json['entries'] as List?) @@ -25,5 +25,5 @@ Map _$MeasurementCategoryToJson( 'id': instance.id, 'name': instance.name, 'unit': instance.unit, - 'entries': instance.entries.map((e) => e.toJson()).toList(), + 'entries': MeasurementCategory._nullValue(instance.entries), }; diff --git a/lib/models/measurements/measurement_entry.dart b/lib/models/measurements/measurement_entry.dart index 5609aa48..e46c87d3 100644 --- a/lib/models/measurements/measurement_entry.dart +++ b/lib/models/measurements/measurement_entry.dart @@ -7,7 +7,7 @@ part 'measurement_entry.g.dart'; @JsonSerializable() class MeasurementEntry extends Equatable { @JsonKey(required: true) - final int id; + final int? id; @JsonKey(required: true) final int category; @@ -35,5 +35,5 @@ class MeasurementEntry extends Equatable { Map toJson() => _$MeasurementEntryToJson(this); @override - List get props => [id, category, date, value, notes]; + List get props => [id, category, date, value, notes]; } diff --git a/lib/models/measurements/measurement_entry.g.dart b/lib/models/measurements/measurement_entry.g.dart index cf116952..935487c7 100644 --- a/lib/models/measurements/measurement_entry.g.dart +++ b/lib/models/measurements/measurement_entry.g.dart @@ -10,7 +10,7 @@ MeasurementEntry _$MeasurementEntryFromJson(Map json) { $checkKeys(json, requiredKeys: const ['id', 'category', 'date', 'value', 'notes']); return MeasurementEntry( - id: json['id'] as int, + id: json['id'] as int?, category: json['category'] as int, date: DateTime.parse(json['date'] as String), value: json['value'] as num, diff --git a/lib/providers/measurement.dart b/lib/providers/measurement.dart index 29f84a11..70cc5bac 100644 --- a/lib/providers/measurement.dart +++ b/lib/providers/measurement.dart @@ -16,13 +16,18 @@ * along with this program. If not, see . */ +import 'dart:convert'; + import 'package:flutter/material.dart'; import 'package:http/http.dart' as http; +import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/exceptions/no_result_exception.dart'; +import 'package:wger/exceptions/no_such_entry_exception.dart'; import 'package:wger/models/measurements/measurement_category.dart'; import 'package:wger/models/measurements/measurement_entry.dart'; import 'package:wger/providers/auth.dart'; import 'package:wger/providers/base_provider.dart'; +import 'package:wger/providers/helpers.dart'; class MeasurementProvider with ChangeNotifier { static const _categoryUrl = 'measurement-category'; @@ -67,6 +72,7 @@ class MeasurementProvider with ChangeNotifier { /// Fetches and sets the measurement entries for the given category Future fetchAndSetCategoryEntries(int id) async { final category = findCategoryById(id); + final categoryIndex = _categories.indexOf(category); // Process the response final requestUrl = baseProvider.makeUrl(_entryUrl, query: {'category': category.id.toString()}); @@ -75,7 +81,76 @@ class MeasurementProvider with ChangeNotifier { for (final entry in data['results']) { loadedEntries.add(MeasurementEntry.fromJson(entry)); } - category.entries = loadedEntries; + MeasurementCategory editedCategory = category.copyWith(entries: loadedEntries); + _categories.removeAt(categoryIndex); + _categories.insert(categoryIndex, editedCategory); notifyListeners(); } + + /// Adds a measurement category to the remote db + Future addCategory(MeasurementCategory category) async { + Uri postUri = baseProvider.makeUrl(_categoryUrl); + final Map newCategoryMap = await baseProvider.post(category.toJson(), postUri); + final MeasurementCategory newCategory = MeasurementCategory.fromJson(newCategoryMap); + _categories.add(newCategory); + _categories.sort((a, b) => a.name.compareTo(b.name)); + notifyListeners(); + } + + /// Deletes a measurement category from the remote db + Future deleteCategory(int id) async { + MeasurementCategory category = _categories.firstWhere((element) => element.id == id, + orElse: () => throw NoSuchEntryException()); + int categoryIndex = _categories.indexOf(category); + _categories.remove(category); + notifyListeners(); + + final http.Response response = await baseProvider.deleteRequest(_categoryUrl, id); + + if (response.statusCode >= 400) { + _categories.insert(categoryIndex, category); + notifyListeners(); + throw WgerHttpException(response.body); + } + } + + /// Adds a measurement entry to the remote db + Future addEntry(MeasurementEntry entry) async { + Uri postUri = baseProvider.makeUrl(_entryUrl); + + final Map newEntryMap = await baseProvider.post(entry.toJson(), postUri); + final MeasurementEntry newEntry = MeasurementEntry.fromJson(newEntryMap); + + _categories.firstWhere((categories) { + if (categories.id == newEntry.category) { + categories.entries.add(newEntry); + categories.entries.sort((a, b) => b.date.compareTo(a.date)); + notifyListeners(); + return true; + } + return false; + }, orElse: () => throw NoSuchEntryException()); + } + + /// Deletes a measurement entry from the remote db + Future deleteEntry(int id, int categoryId) async { + final int categoryIndex = _categories.indexWhere((category) => category.id == categoryId); + if (categoryIndex == -1) throw NoSuchEntryException(); + + final MeasurementEntry entry = _categories[categoryIndex] + .entries + .firstWhere((entry) => entry.id == id, orElse: () => throw NoSuchEntryException()); + final int entryIndex = _categories[categoryIndex].entries.indexOf(entry); + + _categories[categoryIndex].entries.removeAt(entryIndex); + notifyListeners(); + + final http.Response response = await baseProvider.deleteRequest(_entryUrl, id); + + if (response.statusCode >= 400) { + _categories[categoryIndex].entries.insert(entryIndex, entry); + notifyListeners(); + throw WgerHttpException(response.body); + } + } } diff --git a/test/fixtures/measurement_categories.json b/test/fixtures/measurement_categories.json index 5635f49f..12f8999f 100644 --- a/test/fixtures/measurement_categories.json +++ b/test/fixtures/measurement_categories.json @@ -1,9 +1,17 @@ { - "count": 2, - "next": null, - "previous": null, - "results": [ - {"id": 1, "name": "Strength", "unit": "kN"}, - {"id": 2, "name": "Biceps", "unit": "cm"} - ] + "count": 2, + "next": null, + "previous": null, + "results": [ + { + "id": 1, + "name": "Strength", + "unit": "kN" + }, + { + "id": 2, + "name": "Biceps", + "unit": "cm" + } + ] } \ No newline at end of file diff --git a/test/fixtures/measurement_category.json b/test/fixtures/measurement_category.json index c6b8b88b..3eff6658 100644 --- a/test/fixtures/measurement_category.json +++ b/test/fixtures/measurement_category.json @@ -1,21 +1,5 @@ { - "count": 6, - "next": null, - "previous": null, - "results": [ - { - "id": 1, - "category": 1, - "date": "2021-07-21", - "value": 10, - "notes": "Some important notes" - }, - { - "id": 2, - "category": 1, - "date": "2021-07-10", - "value": 15.00, - "notes": "" - } - ] + "id": 1, + "name": "Strength", + "unit": "kN" } \ No newline at end of file diff --git a/test/fixtures/measurement_category_entries.json b/test/fixtures/measurement_category_entries.json new file mode 100644 index 00000000..c6b8b88b --- /dev/null +++ b/test/fixtures/measurement_category_entries.json @@ -0,0 +1,21 @@ +{ + "count": 6, + "next": null, + "previous": null, + "results": [ + { + "id": 1, + "category": 1, + "date": "2021-07-21", + "value": 10, + "notes": "Some important notes" + }, + { + "id": 2, + "category": 1, + "date": "2021-07-10", + "value": 15.00, + "notes": "" + } + ] +} \ No newline at end of file diff --git a/test/fixtures/measurement_category_toJson_without_id.json b/test/fixtures/measurement_category_toJson_without_id.json new file mode 100644 index 00000000..4dd9dcf5 --- /dev/null +++ b/test/fixtures/measurement_category_toJson_without_id.json @@ -0,0 +1,6 @@ +{ + "id": null, + "name": "Strength", + "unit": "kN", + "entries": null +} \ No newline at end of file diff --git a/test/fixtures/measurement_entry.json b/test/fixtures/measurement_entry.json new file mode 100644 index 00000000..4b841a25 --- /dev/null +++ b/test/fixtures/measurement_entry.json @@ -0,0 +1,7 @@ +{ + "id": 3, + "category": 1, + "date": "2021-07-09", + "value": 15.00, + "notes": "" +} \ No newline at end of file diff --git a/test/fixtures/measurement_entry_without_id.json b/test/fixtures/measurement_entry_without_id.json new file mode 100644 index 00000000..34115721 --- /dev/null +++ b/test/fixtures/measurement_entry_without_id.json @@ -0,0 +1,7 @@ +{ + "id": null, + "category": 1, + "date": "2021-07-09", + "value": 15.00, + "notes": "" +} \ No newline at end of file diff --git a/test/fixtures/measurement_entry_wrong_category.json b/test/fixtures/measurement_entry_wrong_category.json new file mode 100644 index 00000000..ac55ed5f --- /dev/null +++ b/test/fixtures/measurement_entry_wrong_category.json @@ -0,0 +1,7 @@ +{ + "id": 3, + "category": 83, + "date": "2021-07-09", + "value": 15.00, + "notes": "" +} \ No newline at end of file diff --git a/test/measurements/measurement_category_test.dart b/test/measurements/measurement_category_test.dart index 17d072ab..6a439029 100644 --- a/test/measurements/measurement_category_test.dart +++ b/test/measurements/measurement_category_test.dart @@ -35,6 +35,13 @@ void main() { 'entries': [tMeasurementEntryMap], }; + Map tMeasurementCategoryMaptoJson = { + 'id': 123, + 'name': 'Bizeps', + 'unit': 'cm', + 'entries': null, + }; + test('should convert a JSON map to a MeasurementCategory object', () { // act final result = MeasurementCategory.fromJson(tMeasurementCategoryMap); @@ -48,6 +55,28 @@ void main() { final result = tMeasurementCategory.toJson(); // assert - expect(result, tMeasurementCategoryMap); + expect(result, tMeasurementCategoryMaptoJson); + }); + + test('should copyWith objects of this class', () { + // arrange + + MeasurementCategory tMeasurementCategoryCopied = MeasurementCategory( + id: 1234, + name: 'Coolness', + unit: 'lp', + entries: tMeasurementEntries, + ); + + // act + final result = tMeasurementCategory.copyWith( + id: 1234, + name: 'Coolness', + unit: 'lp', + entries: tMeasurementEntries, + ); + + // assert + expect(result, tMeasurementCategoryCopied); }); } diff --git a/test/measurements/measurement_provider_test.dart b/test/measurements/measurement_provider_test.dart index 40577ef6..2979a934 100644 --- a/test/measurements/measurement_provider_test.dart +++ b/test/measurements/measurement_provider_test.dart @@ -1,9 +1,12 @@ import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; +import 'package:http/http.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; +import 'package:wger/exceptions/http_exception.dart'; import 'package:wger/exceptions/no_result_exception.dart'; +import 'package:wger/exceptions/no_such_entry_exception.dart'; import 'package:wger/models/measurements/measurement_category.dart'; import 'package:wger/models/measurements/measurement_entry.dart'; import 'package:wger/providers/auth.dart'; @@ -21,8 +24,24 @@ main() { late MeasurementProvider measurementProvider; late MockWgerBaseProvider mockWgerBaseProvider; - Uri tCategoriesUri = Uri(); + String categoryUrl = 'measurement-category'; + String entryUrl = 'measurement'; + Uri tCategoryUri = Uri(); + Map tMeasurementCategoryMap = + jsonDecode(fixture('measurement_category_entries.json')); + Uri tCategoryEntriesUri = Uri( + scheme: 'http', + host: 'tedmosbyisajerk.com', + path: 'api/v2/' + entryUrl + '/', + query: 'category=1'); + int tCategoryId = 1; + MeasurementCategory tMeasurementCategory = + MeasurementCategory(id: 1, name: 'Strength', unit: 'kN'); + List tMeasurementCategories = [ + MeasurementCategory(id: 1, name: 'Strength', unit: 'kN'), + MeasurementCategory(id: 2, name: 'Biceps', unit: 'cm') + ]; Map tMeasurementCategoriesMap = jsonDecode(fixture('measurement_categories.json')); @@ -30,9 +49,14 @@ main() { mockWgerBaseProvider = MockWgerBaseProvider(); measurementProvider = MeasurementProvider(mockWgerBaseProvider); - when(mockWgerBaseProvider.makeUrl(any)).thenReturn(tCategoriesUri); + when(mockWgerBaseProvider.makeUrl(any)).thenReturn(tCategoryUri); when(mockWgerBaseProvider.fetch(any)) .thenAnswer((realInvocation) => Future.value(tMeasurementCategoriesMap)); + + when(mockWgerBaseProvider.makeUrl(entryUrl, query: anyNamed('query'))) + .thenReturn(tCategoryEntriesUri); + when(mockWgerBaseProvider.fetch(tCategoryEntriesUri)) + .thenAnswer((realInvocation) => Future.value(tMeasurementCategoryMap)); }); group('clear()', () { @@ -54,8 +78,7 @@ main() { group('findCategoryById()', () { test('should return a category for an id', () async { // arrange - MeasurementCategory tMeasurementCategory = - MeasurementCategory(id: 1, name: 'Strength', unit: 'kN'); + await measurementProvider.fetchAndSetCategories(); // act @@ -72,13 +95,6 @@ main() { }); group('fetchAndSetCategories()', () { - String categoryUrl = 'measurement-category'; - - List tMeasurementCategories = [ - MeasurementCategory(id: 1, name: 'Strength', unit: 'kN'), - MeasurementCategory(id: 2, name: 'Biceps', unit: 'cm') - ]; - test('should make a request url', () async { // act await measurementProvider.fetchAndSetCategories(); @@ -92,7 +108,7 @@ main() { await measurementProvider.fetchAndSetCategories(); // assert - verify(mockWgerBaseProvider.fetch(tCategoriesUri)); + verify(mockWgerBaseProvider.fetch(tCategoryUri)); }); test('should set categories', () async { @@ -105,21 +121,8 @@ main() { }); group('fetchAndSetCategoryEntries()', () { - String entryUrl = 'measurement'; - Uri tCategoryEntriesUri = Uri( - scheme: 'http', - host: 'tedmosbyisajerk.com', - path: 'api/v2/' + entryUrl + '/', - query: 'category=1'); - Map tMeasurementCategoryMap = jsonDecode(fixture('measurement_category.json')); - setUp(() async { await measurementProvider.fetchAndSetCategories(); - - when(mockWgerBaseProvider.makeUrl(any, query: anyNamed('query'))) - .thenReturn(tCategoryEntriesUri); - when(mockWgerBaseProvider.fetch(any)) - .thenAnswer((realInvocation) => Future.value(tMeasurementCategoryMap)); }); test('should make a uri from a category id', () async { @@ -167,4 +170,274 @@ main() { expect(measurementProvider.categories, tMeasurementCategories); }); }); + + group('addCategory()', () { + MeasurementCategory tMeasurementCategoryWithoutId = + MeasurementCategory(id: null, name: 'Strength', unit: 'kN'); + Map tMeasurementCategoryMap = jsonDecode(fixture('measurement_category.json')); + Map tMeasurementCategoryMapWithoutId = + jsonDecode(fixture('measurement_category_toJson_without_id.json')); + List tMeasurementCategoriesAdded = [ + MeasurementCategory(id: 2, name: 'Biceps', unit: 'cm'), + MeasurementCategory(id: 1, name: 'Strength', unit: 'kN'), + MeasurementCategory(id: 1, name: 'Strength', unit: 'kN'), + ]; + setUp(() { + when(mockWgerBaseProvider.post(any, any)) + .thenAnswer((realInvocation) => Future.value(tMeasurementCategoryMap)); + }); + + test('should post the MeasurementCategorie\'s Map', () async { + // act + await measurementProvider.addCategory(tMeasurementCategoryWithoutId); + + // assert + verify(mockWgerBaseProvider.post(tMeasurementCategoryMapWithoutId, tCategoryUri)); + }); + + test( + 'should add the result from the post call to the categories List and sort the list by alphabetical order', + () async { + // arrange + await measurementProvider.fetchAndSetCategories(); + + // act + await measurementProvider.addCategory(tMeasurementCategoryWithoutId); + + // assert + expect(measurementProvider.categories, tMeasurementCategoriesAdded); + }); + }); + + group('deleteCategory()', () { + setUp(() async { + await measurementProvider.fetchAndSetCategories(); + }); + test( + 'should remove a MeasurementCategory from the categories list for an id and call the api to remove the MeasurementCategory', + () async { + // arrange + when(mockWgerBaseProvider.deleteRequest(any, any)) + .thenAnswer((realInvocation) => Future.value(Response('', 200))); + + List tMeasurementCategoriesOneDeleted = [ + MeasurementCategory(id: 2, name: 'Biceps', unit: 'cm') + ]; + + // act + await measurementProvider.deleteCategory(tCategoryId); + + // assert + verify(mockWgerBaseProvider.deleteRequest('measurement-category', tCategoryId)); + expect(measurementProvider.categories, tMeasurementCategoriesOneDeleted); + }); + + test('should throw a NoSuchEntryException if no category is found', () { + // act & assert + expect(() async => await measurementProvider.deleteCategory(83), + throwsA(isA())); + }); + + test('should re-add the "removed" MeasurementCategory if call to api fails', () async { + // arrange + when(mockWgerBaseProvider.deleteRequest(any, any)) + .thenAnswer((realInvocation) => Future.value(Response('{}', 400))); + + // act & assert + expect(() async => await measurementProvider.deleteCategory(tCategoryId), + throwsA(isA())); + // can't await the expect call above + await Future.delayed(Duration(milliseconds: 1)); + + expect(measurementProvider.categories, tMeasurementCategories); + }); + }); + + group('addEntry()', () { + MeasurementEntry tMeasurementEntry = MeasurementEntry( + id: 3, + category: 1, + date: DateTime(2021, 7, 9), + value: 15.00, + notes: '', + ); + + MeasurementEntry tMeasurementEntryWithoutId = MeasurementEntry( + id: null, + category: 1, + date: DateTime(2021, 7, 9), + value: 15.0, + notes: '', + ); + + List tMeasurementCategories = [ + MeasurementCategory(id: 1, name: 'Strength', unit: 'kN', entries: [ + MeasurementEntry( + id: 1, + category: 1, + date: DateTime(2021, 7, 21), + value: 10, + notes: 'Some important notes', + ), + MeasurementEntry( + id: 2, + category: 1, + date: DateTime(2021, 7, 10), + value: 15.00, + notes: '', + ), + tMeasurementEntry + ]), + MeasurementCategory(id: 2, name: 'Biceps', unit: 'cm') + ]; + + setUp(() async { + await measurementProvider.fetchAndSetCategories(); + + Map measurementEntryMap = jsonDecode(fixture('measurement_entry.json')); + when(mockWgerBaseProvider.post(any, any)) + .thenAnswer((realInvocation) => Future.value(measurementEntryMap)); + }); + + test('should make the post url', () async { + // act + await measurementProvider.addEntry(tMeasurementEntryWithoutId); + + // assert + verify(mockWgerBaseProvider.makeUrl(entryUrl)); + }); + + test('should post the MeasurementEntryMap', () async { + // arrange + Map measurementEntryMapWithoutId = + jsonDecode(fixture('measurement_entry_without_id.json')); + + // act + await measurementProvider.addEntry(tMeasurementEntryWithoutId); + + // assert + verify(mockWgerBaseProvider.post(measurementEntryMapWithoutId, tCategoryEntriesUri)); + }); + + test( + 'should add MeasurementEntry to its MeasurementCategory in the categories List and sort the category\'s list by date', + () async { + // arrange + await measurementProvider.fetchAndSetCategoryEntries(tCategoryId); + + // act + await measurementProvider.addEntry(tMeasurementEntryWithoutId); + + // assert + expect(measurementProvider.categories, tMeasurementCategories); + }); + + test('should throw a NoSuchEntryException if no category is found', () { + // arrange + MeasurementEntry tMeasurementEntryWrongCategory = MeasurementEntry( + id: 3, + category: 83, + date: DateTime(2021, 7, 9), + value: 15.00, + notes: '', + ); + Map measurementEntryMapWrongCategory = + jsonDecode(fixture('measurement_entry_wrong_category.json')); + when(mockWgerBaseProvider.post(any, any)) + .thenAnswer((realInvocation) => Future.value(measurementEntryMapWrongCategory)); + + // act & assert + expect(() async => await measurementProvider.addEntry(tMeasurementEntryWrongCategory), + throwsA(isA())); + }); + }); + + group('deleteEntry()', () { + int tEntryId = 2; + List tMeasurementCategories = [ + MeasurementCategory(id: 1, name: 'Strength', unit: 'kN', entries: [ + MeasurementEntry( + id: 1, + category: 1, + date: DateTime(2021, 7, 21), + value: 10, + notes: 'Some important notes', + ), + ]), + MeasurementCategory(id: 2, name: 'Biceps', unit: 'cm') + ]; + + setUp(() async { + await measurementProvider.fetchAndSetCategories(); + await measurementProvider.fetchAndSetCategoryEntries(tCategoryId); + + when(mockWgerBaseProvider.deleteRequest(any, any)) + .thenAnswer((realInvocation) => Future.value(Response('', 200))); + }); + + test('should remove a MeasurementEntry from the category\'s entries List for an id', () async { + // act + await measurementProvider.deleteEntry(tEntryId, tCategoryId); + + // assert + expect(measurementProvider.categories, tMeasurementCategories); + }); + + test('should throw a NoSuchEntryException if the category isn\'t found', () { + // act & assert + expect(() async => await measurementProvider.deleteEntry(tEntryId, 83), + throwsA(isA())); + }); + + test( + 'should throw a NoSuchEntryException if the entry in the categories entries List isn\'t found', + () { + // act & assert + expect(() async => await measurementProvider.deleteEntry(83, tCategoryId), + throwsA(isA())); + }); + + test('should call the api to remove the MeasurementEntry', () async { + // act + await measurementProvider.deleteEntry(tEntryId, tCategoryId); + + // assert + verify(mockWgerBaseProvider.deleteRequest(entryUrl, tEntryId)); + }); + + test( + 'should re-add the "removed" MeasurementEntry and throw a WgerHttpException if the api call fails', + () async { + // arrange + List tMeasurementCategories = [ + MeasurementCategory(id: 1, name: 'Strength', unit: 'kN', entries: [ + MeasurementEntry( + id: 1, + category: 1, + date: DateTime(2021, 7, 21), + value: 10, + notes: 'Some important notes', + ), + MeasurementEntry( + id: 2, + category: 1, + date: DateTime(2021, 7, 10), + value: 15.00, + notes: '', + ), + ]), + MeasurementCategory(id: 2, name: 'Biceps', unit: 'cm') + ]; + when(mockWgerBaseProvider.deleteRequest(any, any)) + .thenAnswer((realInvocation) => Future.value(Response('{}', 404))); + + // act & assert + expect(() async => await measurementProvider.deleteEntry(tEntryId, tCategoryId), + throwsA(isA())); + // can't await the expect call above + await Future.delayed(Duration(milliseconds: 1)); + + expect(measurementProvider.categories, tMeasurementCategories); + }); + }); }