mirror of
https://github.com/wger-project/flutter.git
synced 2026-02-18 00:17:48 +01:00
Add tests for db cache
This commit is contained in:
@@ -83,6 +83,8 @@ class $IngredientsTable extends Ingredients
|
||||
class IngredientTable extends DataClass implements Insertable<IngredientTable> {
|
||||
final int id;
|
||||
final String data;
|
||||
|
||||
/// The date when the ingredient was last fetched from the server
|
||||
final DateTime lastFetched;
|
||||
const IngredientTable(
|
||||
{required this.id, required this.data, required this.lastFetched});
|
||||
|
||||
@@ -46,7 +46,7 @@ class NutritionPlansProvider with ChangeNotifier {
|
||||
final WgerBaseProvider baseProvider;
|
||||
late IngredientDatabase database;
|
||||
List<NutritionalPlan> _plans = [];
|
||||
List<Ingredient> _ingredients = [];
|
||||
List<Ingredient> ingredients = [];
|
||||
|
||||
NutritionPlansProvider(this.baseProvider, List<NutritionalPlan> entries,
|
||||
{IngredientDatabase? database})
|
||||
@@ -58,14 +58,10 @@ class NutritionPlansProvider with ChangeNotifier {
|
||||
return [..._plans];
|
||||
}
|
||||
|
||||
set ingredients(items) {
|
||||
_ingredients = items;
|
||||
}
|
||||
|
||||
/// Clears all lists
|
||||
void clear() {
|
||||
_plans = [];
|
||||
_ingredients = [];
|
||||
ingredients = [];
|
||||
}
|
||||
|
||||
/// Returns the current active nutritional plan. At the moment this is just
|
||||
@@ -299,11 +295,12 @@ class NutritionPlansProvider with ChangeNotifier {
|
||||
/// Fetch and return an ingredient
|
||||
///
|
||||
/// If the ingredient is not known locally, it is fetched from the server
|
||||
Future<Ingredient> fetchIngredient(int ingredientId) async {
|
||||
Future<Ingredient> fetchIngredient(int ingredientId, {IngredientDatabase? database}) async {
|
||||
database ??= this.database;
|
||||
Ingredient ingredient;
|
||||
|
||||
try {
|
||||
ingredient = _ingredients.firstWhere((e) => e.id == ingredientId);
|
||||
ingredient = ingredients.firstWhere((e) => e.id == ingredientId);
|
||||
} on StateError {
|
||||
final ingredientDb = await (database.select(database.ingredients)
|
||||
..where((e) => e.id.equals(ingredientId)))
|
||||
@@ -312,6 +309,7 @@ class NutritionPlansProvider with ChangeNotifier {
|
||||
// Try to fetch from local db
|
||||
if (ingredientDb != null) {
|
||||
ingredient = Ingredient.fromJson(jsonDecode(ingredientDb.data));
|
||||
ingredients.add(ingredient);
|
||||
log("Loaded ingredient '${ingredient.name}' from db cache");
|
||||
|
||||
// Prune old entries
|
||||
@@ -324,7 +322,7 @@ class NutritionPlansProvider with ChangeNotifier {
|
||||
baseProvider.makeUrl(_ingredientInfoPath, id: ingredientId),
|
||||
);
|
||||
ingredient = Ingredient.fromJson(data);
|
||||
_ingredients.add(ingredient);
|
||||
ingredients.add(ingredient);
|
||||
|
||||
database.into(database.ingredients).insert(
|
||||
IngredientsCompanion.insert(
|
||||
|
||||
@@ -422,16 +422,6 @@ class MockExercisesProvider extends _i1.Mock implements _i9.ExercisesProvider {
|
||||
)),
|
||||
) as _i10.Future<_i4.Exercise>);
|
||||
|
||||
@override
|
||||
_i10.Future<void> checkExerciseCacheVersion() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#checkExerciseCacheVersion,
|
||||
[],
|
||||
),
|
||||
returnValue: _i10.Future<void>.value(),
|
||||
returnValueForMissingStub: _i10.Future<void>.value(),
|
||||
) as _i10.Future<void>);
|
||||
|
||||
@override
|
||||
_i10.Future<void> initCacheTimesLocalPrefs({dynamic forceInit = false}) =>
|
||||
(super.noSuchMethod(
|
||||
|
||||
@@ -381,7 +381,7 @@ void main() {
|
||||
});
|
||||
});
|
||||
|
||||
group('Exercises', () {
|
||||
group('Exercise cache DB', () {
|
||||
test('that if there is already valid data in the DB, the API is not hit', () async {
|
||||
// Arrange
|
||||
final prefs = await SharedPreferences.getInstance();
|
||||
|
||||
@@ -13,18 +13,26 @@ import '../measurements/measurement_provider_test.mocks.dart';
|
||||
void main() {
|
||||
late NutritionPlansProvider nutritionProvider;
|
||||
late MockWgerBaseProvider mockWgerBaseProvider;
|
||||
late IngredientDatabase database;
|
||||
late Map<String, dynamic> ingredient59887Response;
|
||||
|
||||
// Needs to be configured here, setUp runs on every test, setUpAll only once
|
||||
setUpAll(() {
|
||||
database = IngredientDatabase.inMemory(NativeDatabase.memory());
|
||||
});
|
||||
|
||||
setUp(() {
|
||||
mockWgerBaseProvider = MockWgerBaseProvider();
|
||||
nutritionProvider = NutritionPlansProvider(
|
||||
mockWgerBaseProvider,
|
||||
[],
|
||||
database: IngredientDatabase.inMemory(NativeDatabase.memory()),
|
||||
database: database,
|
||||
);
|
||||
|
||||
const String planInfoUrl = 'nutritionplaninfo';
|
||||
const String planUrl = 'nutritionplan';
|
||||
const String diaryUrl = 'nutritiondiary';
|
||||
const String ingredientInfoUrl = 'ingredientinfo';
|
||||
|
||||
final Map<String, dynamic> nutritionalPlanInfoResponse = jsonDecode(
|
||||
fixture('nutrition/nutritional_plan_info_detail_response.json'),
|
||||
@@ -35,7 +43,7 @@ void main() {
|
||||
final List<dynamic> nutritionDiaryResponse = jsonDecode(
|
||||
fixture('nutrition/nutrition_diary_response.json'),
|
||||
)['results'];
|
||||
final Map<String, dynamic> ingredient59887Response = jsonDecode(
|
||||
ingredient59887Response = jsonDecode(
|
||||
fixture('nutrition/ingredientinfo_59887.json'),
|
||||
);
|
||||
final Map<String, dynamic> ingredient10065Response = jsonDecode(
|
||||
@@ -68,9 +76,16 @@ void main() {
|
||||
host: 'localhost',
|
||||
path: 'api/v2/$diaryUrl',
|
||||
);
|
||||
final Uri ingredientUri = Uri(
|
||||
scheme: 'http',
|
||||
host: 'localhost',
|
||||
path: 'api/v2/$ingredientInfoUrl',
|
||||
);
|
||||
when(mockWgerBaseProvider.makeUrl(planInfoUrl, id: anyNamed('id'))).thenReturn(planInfoUri);
|
||||
when(mockWgerBaseProvider.makeUrl(planUrl, id: anyNamed('id'))).thenReturn(planUri);
|
||||
when(mockWgerBaseProvider.makeUrl(diaryUrl, query: anyNamed('query'))).thenReturn(diaryUri);
|
||||
when(mockWgerBaseProvider.makeUrl(ingredientInfoUrl, id: anyNamed('id')))
|
||||
.thenReturn(ingredientUri);
|
||||
when(mockWgerBaseProvider.fetch(planInfoUri)).thenAnswer(
|
||||
(realInvocation) => Future.value(nutritionalPlanInfoResponse),
|
||||
);
|
||||
@@ -80,6 +95,9 @@ void main() {
|
||||
when(mockWgerBaseProvider.fetchPaginated(diaryUri)).thenAnswer(
|
||||
(realInvocation) => Future.value(nutritionDiaryResponse),
|
||||
);
|
||||
when(mockWgerBaseProvider.fetch(ingredientUri)).thenAnswer(
|
||||
(realInvocation) => Future.value(ingredient10065Response),
|
||||
);
|
||||
});
|
||||
|
||||
group('fetchAndSetPlanFull', () {
|
||||
@@ -91,4 +109,48 @@ void main() {
|
||||
expect(nutritionProvider.items.isEmpty, false);
|
||||
});
|
||||
});
|
||||
|
||||
group('Ingredient cache DB', () {
|
||||
test('that if there is already valid data in the DB, the API is not hit', () async {
|
||||
// Arrange
|
||||
nutritionProvider.ingredients = [];
|
||||
await database.into(database.ingredients).insert(
|
||||
IngredientsCompanion.insert(
|
||||
id: ingredient59887Response['id'],
|
||||
data: json.encode(ingredient59887Response),
|
||||
lastFetched: DateTime.now(),
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await nutritionProvider.fetchIngredient(59887, database: database);
|
||||
|
||||
// Assert
|
||||
expect(nutritionProvider.ingredients.length, 1);
|
||||
expect(nutritionProvider.ingredients.first.id, 59887);
|
||||
expect(nutritionProvider.ingredients.first.name, 'Baked Beans');
|
||||
verifyNever(mockWgerBaseProvider.fetchPaginated(any));
|
||||
});
|
||||
|
||||
test('fetching an ingredient not present in the DB, the API is hit', () async {
|
||||
// Arrange
|
||||
nutritionProvider.ingredients = [];
|
||||
await database.into(database.ingredients).insert(
|
||||
IngredientsCompanion.insert(
|
||||
id: ingredient59887Response['id'],
|
||||
data: json.encode(ingredient59887Response),
|
||||
lastFetched: DateTime.now(),
|
||||
),
|
||||
);
|
||||
|
||||
// Act
|
||||
await nutritionProvider.fetchIngredient(10065, database: database);
|
||||
|
||||
// Assert
|
||||
expect(nutritionProvider.ingredients.length, 1);
|
||||
expect(nutritionProvider.ingredients.first.id, 10065);
|
||||
expect(nutritionProvider.ingredients.first.name, "'Old Times' Orange Fine Cut Marmalade");
|
||||
verify(mockWgerBaseProvider.fetch(any));
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -128,21 +128,27 @@ class MockNutritionPlansProvider extends _i1.Mock
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
List<_i7.Ingredient> get ingredients => (super.noSuchMethod(
|
||||
Invocation.getter(#ingredients),
|
||||
returnValue: <_i7.Ingredient>[],
|
||||
) as List<_i7.Ingredient>);
|
||||
|
||||
@override
|
||||
set ingredients(List<_i7.Ingredient>? _ingredients) => super.noSuchMethod(
|
||||
Invocation.setter(
|
||||
#ingredients,
|
||||
_ingredients,
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
List<_i4.NutritionalPlan> get items => (super.noSuchMethod(
|
||||
Invocation.getter(#items),
|
||||
returnValue: <_i4.NutritionalPlan>[],
|
||||
) as List<_i4.NutritionalPlan>);
|
||||
|
||||
@override
|
||||
set ingredients(dynamic items) => super.noSuchMethod(
|
||||
Invocation.setter(
|
||||
#ingredients,
|
||||
items,
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get hasListeners => (super.noSuchMethod(
|
||||
Invocation.getter(#hasListeners),
|
||||
@@ -367,17 +373,22 @@ class MockNutritionPlansProvider extends _i1.Mock
|
||||
) as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
_i9.Future<_i7.Ingredient> fetchIngredient(int? ingredientId) =>
|
||||
_i9.Future<_i7.Ingredient> fetchIngredient(
|
||||
int? ingredientId, {
|
||||
_i3.IngredientDatabase? database,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#fetchIngredient,
|
||||
[ingredientId],
|
||||
{#database: database},
|
||||
),
|
||||
returnValue: _i9.Future<_i7.Ingredient>.value(_FakeIngredient_5(
|
||||
this,
|
||||
Invocation.method(
|
||||
#fetchIngredient,
|
||||
[ingredientId],
|
||||
{#database: database},
|
||||
),
|
||||
)),
|
||||
) as _i9.Future<_i7.Ingredient>);
|
||||
|
||||
@@ -128,21 +128,27 @@ class MockNutritionPlansProvider extends _i1.Mock
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
List<_i7.Ingredient> get ingredients => (super.noSuchMethod(
|
||||
Invocation.getter(#ingredients),
|
||||
returnValue: <_i7.Ingredient>[],
|
||||
) as List<_i7.Ingredient>);
|
||||
|
||||
@override
|
||||
set ingredients(List<_i7.Ingredient>? _ingredients) => super.noSuchMethod(
|
||||
Invocation.setter(
|
||||
#ingredients,
|
||||
_ingredients,
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
List<_i4.NutritionalPlan> get items => (super.noSuchMethod(
|
||||
Invocation.getter(#items),
|
||||
returnValue: <_i4.NutritionalPlan>[],
|
||||
) as List<_i4.NutritionalPlan>);
|
||||
|
||||
@override
|
||||
set ingredients(dynamic items) => super.noSuchMethod(
|
||||
Invocation.setter(
|
||||
#ingredients,
|
||||
items,
|
||||
),
|
||||
returnValueForMissingStub: null,
|
||||
);
|
||||
|
||||
@override
|
||||
bool get hasListeners => (super.noSuchMethod(
|
||||
Invocation.getter(#hasListeners),
|
||||
@@ -367,17 +373,22 @@ class MockNutritionPlansProvider extends _i1.Mock
|
||||
) as _i9.Future<void>);
|
||||
|
||||
@override
|
||||
_i9.Future<_i7.Ingredient> fetchIngredient(int? ingredientId) =>
|
||||
_i9.Future<_i7.Ingredient> fetchIngredient(
|
||||
int? ingredientId, {
|
||||
_i3.IngredientDatabase? database,
|
||||
}) =>
|
||||
(super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#fetchIngredient,
|
||||
[ingredientId],
|
||||
{#database: database},
|
||||
),
|
||||
returnValue: _i9.Future<_i7.Ingredient>.value(_FakeIngredient_5(
|
||||
this,
|
||||
Invocation.method(
|
||||
#fetchIngredient,
|
||||
[ingredientId],
|
||||
{#database: database},
|
||||
),
|
||||
)),
|
||||
) as _i9.Future<_i7.Ingredient>);
|
||||
|
||||
@@ -629,16 +629,6 @@ class MockExercisesProvider extends _i1.Mock implements _i12.ExercisesProvider {
|
||||
)),
|
||||
) as _i11.Future<_i6.Exercise>);
|
||||
|
||||
@override
|
||||
_i11.Future<void> checkExerciseCacheVersion() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#checkExerciseCacheVersion,
|
||||
[],
|
||||
),
|
||||
returnValue: _i11.Future<void>.value(),
|
||||
returnValueForMissingStub: _i11.Future<void>.value(),
|
||||
) as _i11.Future<void>);
|
||||
|
||||
@override
|
||||
_i11.Future<void> initCacheTimesLocalPrefs({dynamic forceInit = false}) =>
|
||||
(super.noSuchMethod(
|
||||
|
||||
@@ -557,16 +557,6 @@ class MockExercisesProvider extends _i1.Mock implements _i19.ExercisesProvider {
|
||||
)),
|
||||
) as _i20.Future<_i4.Exercise>);
|
||||
|
||||
@override
|
||||
_i20.Future<void> checkExerciseCacheVersion() => (super.noSuchMethod(
|
||||
Invocation.method(
|
||||
#checkExerciseCacheVersion,
|
||||
[],
|
||||
),
|
||||
returnValue: _i20.Future<void>.value(),
|
||||
returnValueForMissingStub: _i20.Future<void>.value(),
|
||||
) as _i20.Future<void>);
|
||||
|
||||
@override
|
||||
_i20.Future<void> initCacheTimesLocalPrefs({dynamic forceInit = false}) =>
|
||||
(super.noSuchMethod(
|
||||
|
||||
Reference in New Issue
Block a user