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.
app-boilerplate/test/data/local/local_storage_service_test....

354 lines
11 KiB

import 'dart:io';
import 'package:flutter_test/flutter_test.dart';
import 'package:app_boilerplate/data/local/local_storage_service.dart';
import 'package:app_boilerplate/data/local/models/item.dart';
import 'package:path/path.dart' as path;
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
void main() {
// Initialize Flutter bindings for path_provider to work in tests
TestWidgetsFlutterBinding.ensureInitialized();
// Initialize sqflite for testing (required for VM/desktop tests)
sqfliteFfiInit();
databaseFactory = databaseFactoryFfi;
late LocalStorageService service;
late Directory testDir;
late String testDbPath;
late Directory testCacheDir;
setUp(() async {
// Create a temporary directory for testing
testDir = await Directory.systemTemp.createTemp('local_storage_test_');
testDbPath = path.join(testDir.path, 'test_local_storage.db');
testCacheDir = Directory(path.join(testDir.path, 'image_cache'));
// Create service with test paths (bypasses path_provider platform channels)
service = LocalStorageService(
testDbPath: testDbPath,
testCacheDir: testCacheDir,
);
await service.initialize();
});
tearDown(() async {
await service.close();
// Clean up test files and directory
try {
if (await testDir.exists()) {
await testDir.delete(recursive: true);
}
} catch (_) {
// Ignore cleanup errors
}
});
group('LocalStorageService - CRUD Operations', () {
/// Tests that inserting an item successfully stores it in the database.
test('insertItem - success', () async {
// Arrange
final item = Item(
id: 'test-1',
data: {'name': 'Test Item', 'value': 123},
);
// Act
await service.insertItem(item);
// Assert
final retrieved = await service.getItem('test-1');
expect(retrieved, isNotNull);
expect(retrieved!.id, equals('test-1'));
expect(retrieved.data['name'], equals('Test Item'));
expect(retrieved.data['value'], equals(123));
});
/// Tests that inserting an item with existing ID replaces it.
test('insertItem - replaces existing item', () async {
// Arrange
final item1 = Item(
id: 'test-1',
data: {'name': 'Original'},
);
final item2 = Item(
id: 'test-1',
data: {'name': 'Updated'},
);
// Act
await service.insertItem(item1);
await service.insertItem(item2);
// Assert
final retrieved = await service.getItem('test-1');
expect(retrieved, isNotNull);
expect(retrieved!.data['name'], equals('Updated'));
});
/// Tests that getting a non-existent item returns null.
test('getItem - returns null for non-existent item', () async {
// Act
final result = await service.getItem('non-existent');
// Assert
expect(result, isNull);
});
/// Tests that getting an item returns the correct data.
test('getItem - success', () async {
// Arrange
final item = Item(
id: 'test-2',
data: {'title': 'Test Title', 'count': 42},
);
await service.insertItem(item);
// Act
final retrieved = await service.getItem('test-2');
// Assert
expect(retrieved, isNotNull);
expect(retrieved!.id, equals('test-2'));
expect(retrieved.data['title'], equals('Test Title'));
expect(retrieved.data['count'], equals(42));
});
/// Tests that getAllItems returns all stored items.
test('getAllItems - returns all items', () async {
// Arrange
final item1 = Item(id: 'test-1', data: {'name': 'Item 1'});
final item2 = Item(id: 'test-2', data: {'name': 'Item 2'});
final item3 = Item(id: 'test-3', data: {'name': 'Item 3'});
await service.insertItem(item1);
await service.insertItem(item2);
await service.insertItem(item3);
// Act
final items = await service.getAllItems();
// Assert
expect(items.length, equals(3));
expect(items.map((i) => i.id).toList(), containsAll(['test-1', 'test-2', 'test-3']));
});
/// Tests that getAllItems returns empty list when no items exist.
test('getAllItems - returns empty list when no items', () async {
// Act
final items = await service.getAllItems();
// Assert
expect(items, isEmpty);
});
/// Tests that getAllItems returns items in descending order by creation date.
test('getAllItems - returns items in descending order', () async {
// Arrange
final item1 = Item(id: 'test-1', data: {'name': 'First'});
await Future.delayed(const Duration(milliseconds: 10));
final item2 = Item(id: 'test-2', data: {'name': 'Second'});
await Future.delayed(const Duration(milliseconds: 10));
final item3 = Item(id: 'test-3', data: {'name': 'Third'});
await service.insertItem(item1);
await service.insertItem(item2);
await service.insertItem(item3);
// Act
final items = await service.getAllItems();
// Assert
expect(items.length, equals(3));
expect(items[0].id, equals('test-3')); // Newest first
expect(items[2].id, equals('test-1')); // Oldest last
});
/// Tests that deleting an item removes it from the database.
test('deleteItem - success', () async {
// Arrange
final item = Item(id: 'test-1', data: {'name': 'Test'});
await service.insertItem(item);
// Act
await service.deleteItem('test-1');
// Assert
final retrieved = await service.getItem('test-1');
expect(retrieved, isNull);
});
/// Tests that deleting a non-existent item throws an exception.
test('deleteItem - throws exception for non-existent item', () async {
// Act & Assert
expect(
() => service.deleteItem('non-existent'),
throwsException,
);
});
/// Tests that updating an item modifies it correctly.
test('updateItem - success', () async {
// Arrange
final item = Item(id: 'test-1', data: {'name': 'Original'});
await service.insertItem(item);
final originalTimestamp = item.updatedAt;
await Future.delayed(const Duration(milliseconds: 10));
final updatedItem = item.copyWith(
data: {'name': 'Updated'},
);
// Act
await service.updateItem(updatedItem);
// Assert
final retrieved = await service.getItem('test-1');
expect(retrieved, isNotNull);
expect(retrieved!.data['name'], equals('Updated'));
expect(retrieved.updatedAt, greaterThan(originalTimestamp));
});
/// Tests that updating a non-existent item throws an exception.
test('updateItem - throws exception for non-existent item', () async {
// Arrange
final item = Item(id: 'non-existent', data: {'name': 'Test'});
// Act & Assert
expect(
() => service.updateItem(item),
throwsException,
);
});
});
group('LocalStorageService - Image Caching', () {
/// Tests that getCachedImage throws exception for invalid URL.
test('getCachedImage - throws exception for invalid URL', () async {
// Act & Assert
expect(
() => service.getCachedImage('not-a-valid-url'),
throwsException,
);
});
/// Tests that getCachedImage throws exception when not initialized.
test('getCachedImage - throws exception when not initialized', () async {
// Arrange
final uninitializedService = LocalStorageService();
// Act & Assert
expect(
() => uninitializedService.getCachedImage('https://example.com/image.jpg'),
throwsException,
);
});
/// Tests that clearImageCache removes all cached images.
test('clearImageCache - removes all cached images', () async {
// Act
await service.clearImageCache();
// Assert - should not throw
expect(service, isNotNull);
});
/// Tests cache hit scenario - file already exists in cache.
test('getCachedImage - cache hit when file exists', () async {
// Arrange - create a file in cache directory manually
// Note: Full HTTP download test would require mocking http client
// This test verifies the cache directory structure is correct
// Act & Assert - service should be initialized
expect(service, isNotNull);
// The actual HTTP download test would require:
// 1. Mocking http.get() with mockito or http_mock_adapter
// 2. Verifying file creation in cache directory
// For now, we verify error handling works correctly
});
});
group('LocalStorageService - Error Handling', () {
/// Tests that operations fail when service is not initialized.
test('operations fail when not initialized', () async {
// Arrange
final uninitializedService = LocalStorageService();
final item = Item(id: 'test-1', data: {'name': 'Test'});
// Act & Assert
expect(
() => uninitializedService.insertItem(item),
throwsException,
);
expect(
() => uninitializedService.getItem('test-1'),
throwsException,
);
expect(
() => uninitializedService.deleteItem('test-1'),
throwsException,
);
expect(
() => uninitializedService.updateItem(item),
throwsException,
);
});
/// Tests that getCachedImage fails when cache directory not initialized.
test('getCachedImage fails when not initialized', () async {
// Arrange
final uninitializedService = LocalStorageService();
// Act & Assert
expect(
() => uninitializedService.getCachedImage('https://example.com/image.jpg'),
throwsException,
);
});
/// Tests handling of missing or invalid data.
test('handles missing data gracefully', () async {
// Arrange
final item = Item(id: 'test-1', data: {});
// Act
await service.insertItem(item);
final retrieved = await service.getItem('test-1');
// Assert
expect(retrieved, isNotNull);
expect(retrieved!.data, isEmpty);
});
/// Tests handling of complex nested data structures.
test('handles complex nested data', () async {
// Arrange
final item = Item(
id: 'test-1',
data: {
'metadata': {
'author': 'Test Author',
'tags': ['tag1', 'tag2'],
'nested': {
'deep': 'value',
},
},
},
);
// Act
await service.insertItem(item);
final retrieved = await service.getItem('test-1');
// Assert
expect(retrieved, isNotNull);
expect(retrieved!.data['metadata']['author'], equals('Test Author'));
expect(retrieved.data['metadata']['tags'], isA<List>());
expect(retrieved.data['metadata']['nested']['deep'], equals('value'));
});
});
}

Powered by TurnKey Linux.