You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

226 lines
6.8 KiB

import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:path/path.dart' as path;
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
import '../../../lib/data/local/local_storage_service.dart';
import '../../../lib/data/recipes/recipe_service.dart';
import '../../../lib/data/recipes/models/recipe_model.dart';
void main() {
// Initialize Flutter bindings for path_provider to work in tests
TestWidgetsFlutterBinding.ensureInitialized();
// Initialize FFI for testing
setUpAll(() {
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
});
group('RecipeService', () {
late LocalStorageService localStorageService;
late RecipeService recipeService;
late String testDbPath;
setUp(() async {
// Create a temporary directory for test database
final tempDir = await Directory.systemTemp.createTemp('recipe_test_');
testDbPath = path.join(tempDir.path, 'test_recipes.db');
final localStorageDbPath = path.join(tempDir.path, 'test_local_storage.db');
final cacheDir = Directory(path.join(tempDir.path, 'cache'));
localStorageService = LocalStorageService(
testDbPath: localStorageDbPath,
testCacheDir: cacheDir,
);
await localStorageService.initialize(
sessionDbPath: localStorageDbPath,
sessionCacheDir: cacheDir,
);
recipeService = RecipeService(
localStorage: localStorageService,
testDbPath: testDbPath,
);
await recipeService.initialize(sessionDbPath: testDbPath);
});
tearDown(() async {
// Clean up test database
try {
final dbFile = File(testDbPath);
if (await dbFile.exists()) {
await dbFile.delete();
}
} catch (e) {
// Ignore cleanup errors
}
});
test('creates a recipe successfully', () async {
final recipe = RecipeModel(
id: 'test-recipe-1',
title: 'Test Recipe',
description: 'A test recipe',
tags: ['test', 'recipe'],
rating: 4,
isFavourite: true,
imageUrls: ['https://example.com/image.jpg'],
);
final created = await recipeService.createRecipe(recipe);
expect(created.id, equals(recipe.id));
expect(created.title, equals(recipe.title));
expect(created.description, equals(recipe.description));
expect(created.tags, equals(recipe.tags));
expect(created.rating, equals(recipe.rating));
expect(created.isFavourite, equals(recipe.isFavourite));
expect(created.imageUrls, equals(recipe.imageUrls));
expect(created.createdAt, greaterThan(0));
expect(created.updatedAt, greaterThan(0));
});
test('gets a recipe by ID', () async {
final recipe = RecipeModel(
id: 'test-recipe-2',
title: 'Test Recipe 2',
tags: ['test'],
);
await recipeService.createRecipe(recipe);
final retrieved = await recipeService.getRecipe('test-recipe-2');
expect(retrieved, isNotNull);
expect(retrieved!.id, equals(recipe.id));
expect(retrieved.title, equals(recipe.title));
});
test('returns null for non-existent recipe', () async {
final retrieved = await recipeService.getRecipe('non-existent');
expect(retrieved, isNull);
});
test('updates a recipe successfully', () async {
final recipe = RecipeModel(
id: 'test-recipe-3',
title: 'Original Title',
tags: ['original'],
);
await recipeService.createRecipe(recipe);
final updated = recipe.copyWith(
title: 'Updated Title',
tags: ['updated'],
rating: 5,
);
final result = await recipeService.updateRecipe(updated);
expect(result.title, equals('Updated Title'));
expect(result.tags, equals(['updated']));
expect(result.rating, equals(5));
expect(result.updatedAt, greaterThan(recipe.updatedAt));
});
test('deletes a recipe (marks as deleted)', () async {
final recipe = RecipeModel(
id: 'test-recipe-4',
title: 'Recipe to Delete',
tags: ['delete'],
);
await recipeService.createRecipe(recipe);
await recipeService.deleteRecipe('test-recipe-4');
// Recipe should not be found (soft delete)
final retrieved = await recipeService.getRecipe('test-recipe-4');
expect(retrieved, isNull);
// But should exist when including deleted
final allRecipes = await recipeService.getAllRecipes(includeDeleted: true);
final deletedRecipe = allRecipes.firstWhere((r) => r.id == 'test-recipe-4');
expect(deletedRecipe.isDeleted, isTrue);
});
test('gets all recipes', () async {
await recipeService.createRecipe(RecipeModel(
id: 'recipe-1',
title: 'Recipe 1',
tags: ['tag1'],
));
await recipeService.createRecipe(RecipeModel(
id: 'recipe-2',
title: 'Recipe 2',
tags: ['tag2'],
));
final allRecipes = await recipeService.getAllRecipes();
expect(allRecipes.length, equals(2));
expect(allRecipes.map((r) => r.id), containsAll(['recipe-1', 'recipe-2']));
});
test('gets recipes by tag', () async {
await recipeService.createRecipe(RecipeModel(
id: 'recipe-tag1',
title: 'Recipe with Tag1',
tags: ['tag1', 'common'],
));
await recipeService.createRecipe(RecipeModel(
id: 'recipe-tag2',
title: 'Recipe with Tag2',
tags: ['tag2', 'common'],
));
final tag1Recipes = await recipeService.getRecipesByTag('tag1');
expect(tag1Recipes.length, equals(1));
expect(tag1Recipes.first.id, equals('recipe-tag1'));
final commonRecipes = await recipeService.getRecipesByTag('common');
expect(commonRecipes.length, equals(2));
});
test('gets favourite recipes', () async {
await recipeService.createRecipe(RecipeModel(
id: 'favourite-1',
title: 'Favourite Recipe 1',
tags: ['favourite'],
isFavourite: true,
));
await recipeService.createRecipe(RecipeModel(
id: 'not-favourite',
title: 'Not Favourite',
tags: ['normal'],
isFavourite: false,
));
final favourites = await recipeService.getFavouriteRecipes();
expect(favourites.length, equals(1));
expect(favourites.first.id, equals('favourite-1'));
});
test('throws exception when updating non-existent recipe', () async {
final recipe = RecipeModel(
id: 'non-existent',
title: 'Non Existent',
tags: [],
);
expect(
() => recipeService.updateRecipe(recipe),
throwsA(isA<Exception>()),
);
});
test('throws exception when deleting non-existent recipe', () async {
expect(
() => recipeService.deleteRecipe('non-existent'),
throwsA(isA<Exception>()),
);
});
});
}

Powered by TurnKey Linux.