diff --git a/CHANGELOG.md b/CHANGELOG.md index ec7b909..e198a2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,35 +7,44 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Added + +- Method "keys" for listing all cache keys — + [152](https://github.com/dartoos-dev/json_cache/issues/152) + ### Changed -- update linting rules — +- Mehtod "keys" returns an immutable copy of the underlying cache keys — + [165](https://github.com/dartoos-dev/json_cache/issues/152) + +- Update linting rules — [162](https://github.com/dartoos-dev/json_cache/issues/154). - ## [3.0.2] - 2024-08-19 ### Changed -- Bump up dependencies — +- Updated dependencies — [154](https://github.com/dartoos-dev/json_cache/issues/154). ### Fixed -- Removed code warnings and upgraded dart SDK range and each dependency to latest resolvable version available - [148](https://github.com/dartoos-dev/json_cache/issues/148). +- Remove code warnings and upgrade the dart SDK range. In addition, + dependencies were updated to the latest resolvable version — + [148](https://github.com/dartoos-dev/json_cache/issues/148). ## [3.0.1] - 2023-08-10 ### Fixed -- removed unused package 'cross_local_storage' from pubspec — +- Removed unused package 'cross_local_storage' from pubspec — [144](https://github.com/dartoos-dev/json_cache/issues/144). ## [3.0.0] - 2023-08-10 ### Removed -- support for the 'cross_local_storage' package — **BREAKING CHANGE** — +- Support for the 'cross_local_storage' package — **BREAKING CHANGE** — [140](https://github.com/dartoos-dev/json_cache/issues/140). ### Fixed diff --git a/lib/src/json_cache.dart b/lib/src/json_cache.dart index 42cf242..89e1966 100644 --- a/lib/src/json_cache.dart +++ b/lib/src/json_cache.dart @@ -28,8 +28,8 @@ abstract class JsonCache { /// Returns `true` if there is cached data at [key]; `false` otherwise. Future contains(String key); - /// Lists all keys. + /// The cache keys. /// - /// Returns an **unmodifiable** list of cache keys without duplicates. + /// Returns an **unmodifiable** list of all cache keys without duplicates. Future> keys(); } diff --git a/lib/src/json_cache_fake.dart b/lib/src/json_cache_fake.dart index e293f5e..0777837 100644 --- a/lib/src/json_cache_fake.dart +++ b/lib/src/json_cache_fake.dart @@ -55,6 +55,6 @@ class JsonCacheFake implements JsonCache { @override Future> keys() async { - return UnmodifiableListView(_memory.keys); + return UnmodifiableListView(List.unmodifiable(_memory.keys)); } } diff --git a/lib/src/json_cache_hive.dart b/lib/src/json_cache_hive.dart index 1b501fb..fc6e979 100644 --- a/lib/src/json_cache_hive.dart +++ b/lib/src/json_cache_hive.dart @@ -41,6 +41,8 @@ class JsonCacheHive implements JsonCache { @override Future> keys() async { - return UnmodifiableListView(_box.keys.map((k) => k as String)); + return UnmodifiableListView( + _box.keys.map((k) => k as String).toList(growable: false), + ); } } diff --git a/lib/src/json_cache_mem.dart b/lib/src/json_cache_mem.dart index 807cb9f..61e1fcc 100644 --- a/lib/src/json_cache_mem.dart +++ b/lib/src/json_cache_mem.dart @@ -161,7 +161,7 @@ class JsonCacheMem implements JsonCache { @override Future> keys() { return _mutex.protectRead(() async { - return UnmodifiableListView(_memory.keys); + return UnmodifiableListView(_memory.keys.toList(growable: false)); }); } } diff --git a/lib/src/json_cache_safe_local_storage.dart b/lib/src/json_cache_safe_local_storage.dart index b426e3f..0249573 100644 --- a/lib/src/json_cache_safe_local_storage.dart +++ b/lib/src/json_cache_safe_local_storage.dart @@ -47,9 +47,8 @@ class JsonCacheSafeLocalStorage implements JsonCache { @override Future> keys() async { - final data = await _cachedData; return UnmodifiableListView( - data.keys.map((k) => k as String), + (await _cachedData).keys.map((k) => k as String), ); } diff --git a/test/json_cache_fake_test.dart b/test/json_cache_fake_test.dart index f263966..12b9127 100644 --- a/test/json_cache_fake_test.dart +++ b/test/json_cache_fake_test.dart @@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:json_cache/json_cache.dart'; void main() { - group('JsonCacheFake', () { + group('JsonCacheFake:', () { const profKey = 'profile'; const prefKey = 'preferences'; const profData = {'id': 1, 'name': 'John Due'}; @@ -65,13 +65,24 @@ void main() { expect(await fake.contains(prefKey), false); expect(await fake.contains('a key'), false); }); - test('keys', () async { - final fake = JsonCacheFake(); - // update data - await fake.refresh(profKey, profData); - await fake.refresh(prefKey, prefData); - - expect(await fake.keys(), [profKey, prefKey]); + group('method "keys"', () { + late JsonCacheFake fakeCache; + setUp(() async { + fakeCache = JsonCacheFake(); + // update data + await fakeCache.refresh(profKey, profData); + await fakeCache.refresh(prefKey, prefData); + }); + test('should return the inserted keys', () async { + expect(await fakeCache.keys(), [profKey, prefKey]); + }); + test('should keep the returned keys immutable', () async { + final keys = await fakeCache.keys(); + // This should not change the 'keys' variable. + await fakeCache + .refresh('info', {'This is very important information.': true}); + expect(keys, [profKey, prefKey]); + }); }); group('remove', () { test('default ctor', () async { diff --git a/test/json_cache_flutter_secure_storage_test.dart b/test/json_cache_flutter_secure_storage_test.dart index b91dcc1..adb6106 100644 --- a/test/json_cache_flutter_secure_storage_test.dart +++ b/test/json_cache_flutter_secure_storage_test.dart @@ -4,7 +4,7 @@ import 'package:json_cache/json_cache.dart'; import 'flutter_secure_storage_mock.dart'; void main() { - group('JsonCacheFlutterSecureStorage', () { + group('JsonCacheFlutterSecureStorage:', () { const profKey = 'profile'; const prefKey = 'preferences'; const profData = {'id': 1, 'name': 'John Due'}; @@ -50,15 +50,26 @@ void main() { await flutterSecureCache.remove(prefKey); expect(await flutterSecureCache.contains(prefKey), false); }); - test('keys', () async { - final secStorageMock = FlutterSecureStorageMock(); - final JsonCacheFlutterSecureStorage flutterSecureCache = - JsonCacheFlutterSecureStorage(secStorageMock); - // update data - await flutterSecureCache.refresh(profKey, profData); - await flutterSecureCache.refresh(prefKey, prefData); - expect(await flutterSecureCache.keys(), [profKey, prefKey]); + group('method "keys"', () { + late JsonCacheFlutterSecureStorage flutterSecureCache; + setUp(() async { + flutterSecureCache = + JsonCacheFlutterSecureStorage(FlutterSecureStorageMock()); + // update data + await flutterSecureCache.refresh(profKey, profData); + await flutterSecureCache.refresh(prefKey, prefData); + }); + test('should return the inserted keys', () async { + expect(await flutterSecureCache.keys(), [profKey, prefKey]); + }); + test('should keep the returned keys immutable', () async { + final keys = await flutterSecureCache.keys(); + // This should not change the 'keys' variable. + await flutterSecureCache + .refresh('info', {'This is very important information.': true}); + expect(keys, [profKey, prefKey]); + }); }); test('remove', () async { final secStorageMock = FlutterSecureStorageMock(); diff --git a/test/json_cache_hive_test.dart b/test/json_cache_hive_test.dart index bbb0ad9..15f6c65 100644 --- a/test/json_cache_hive_test.dart +++ b/test/json_cache_hive_test.dart @@ -10,7 +10,7 @@ void main() { tearDown(() async { await tearDownTestHive(); }); - group('JsonCacheHive', () { + group('JsonCacheHive:', () { const profKey = 'profile'; const prefKey = 'preferences'; const profData = {'id': 1, 'name': 'John Due'}; @@ -47,15 +47,25 @@ void main() { await hiveCache.remove(prefKey); expect(await hiveCache.contains(prefKey), false); }); - - test('keys', () async { - final box = await Hive.openBox('test-contains-method'); - final JsonCacheHive hiveCache = JsonCacheHive(box); - // update data - await hiveCache.refresh(profKey, profData); - await hiveCache.refresh(prefKey, prefData); - - expect(await hiveCache.keys(), [prefKey, profKey]); + group('method "keys"', () { + late JsonCacheHive hiveCache; + setUp(() async { + final box = await Hive.openBox('test-keys-method'); + hiveCache = JsonCacheHive(box); + // update data + await hiveCache.refresh(profKey, profData); + await hiveCache.refresh(prefKey, prefData); + }); + test('should return the inserted keys', () async { + expect(await hiveCache.keys(), [prefKey, profKey]); + }); + test('should keep the returned keys immutable', () async { + final keys = await hiveCache.keys(); + // This should not change the 'keys' variable. + await hiveCache + .refresh('info', {'This is very important information.': true}); + expect(keys, [prefKey, profKey]); + }); }); test('remove', () async { final box = await Hive.openBox('test-remove'); diff --git a/test/json_cache_hollow_test.dart b/test/json_cache_hollow_test.dart index a54b1f2..66bc98a 100644 --- a/test/json_cache_hollow_test.dart +++ b/test/json_cache_hollow_test.dart @@ -2,7 +2,7 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:json_cache/json_cache.dart'; void main() { - group('JsonCacheHollow', () { + group('JsonCacheHollow:', () { const profKey = 'profile'; const prefKey = 'preferences'; const profData = {'id': 1, 'name': 'John Due'}; @@ -36,11 +36,17 @@ void main() { expect(await fake.contains('a key'), false); }); test('keys', () async { - const fake = JsonCacheHollow(); - await fake.refresh(profKey, profData); - await fake.refresh(prefKey, prefData); + const hollowCache = JsonCacheHollow(); + await hollowCache.refresh(profKey, profData); + await hollowCache.refresh(prefKey, prefData); + + final keys = await hollowCache.keys(); + expect(keys, const []); - expect(await fake.keys(), const []); + // This should not change the 'keys' variable. + await hollowCache + .refresh('info', {'This is very important information.': true}); + expect(keys, const []); }); test('remove', () async { const JsonCacheHollow hollowCache = JsonCacheHollow(); diff --git a/test/json_cache_local_storage_test.dart b/test/json_cache_local_storage_test.dart index b3eae78..0b99ee3 100644 --- a/test/json_cache_local_storage_test.dart +++ b/test/json_cache_local_storage_test.dart @@ -4,8 +4,9 @@ import 'package:json_cache/json_cache.dart'; import 'fake_local_storage.dart'; void main() { - group('JsonCacheLocalStorage', () { + group('JsonCacheLocalStorage:', () { const profKey = 'profile'; + const prefKey = 'preferences'; const profData = {'id': 1, 'name': 'John Due'}; const prefData = { 'theme': 'dark', @@ -40,14 +41,24 @@ void main() { expect(await jsonCache.contains(prefKey), false); }); - test('keys', () async { - const prefKey = 'preferences'; - final JsonCacheLocalStorage jsonCache = _fakeInstance; - // update data - await jsonCache.refresh(profKey, profData); - await jsonCache.refresh(prefKey, prefData); - - expect(await jsonCache.keys(), [profKey, prefKey]); + group('method "keys"', () { + late JsonCacheLocalStorage localStorageCache; + setUp(() async { + localStorageCache = JsonCacheLocalStorage(FakeLocalStorage()); + // update data + await localStorageCache.refresh(profKey, profData); + await localStorageCache.refresh(prefKey, prefData); + }); + test('should return the inserted keys', () async { + expect(await localStorageCache.keys(), [profKey, prefKey]); + }); + test('should keep the returned keys immutable', () async { + final keys = await localStorageCache.keys(); + // This should not change the 'keys' variable. + await localStorageCache + .refresh('info', {'This is very important information.': true}); + expect(keys, [profKey, prefKey]); + }); }); test('remove', () async { const prefKey = 'preferences'; diff --git a/test/json_cache_mem_test.dart b/test/json_cache_mem_test.dart index 49d0659..4bfc6e4 100644 --- a/test/json_cache_mem_test.dart +++ b/test/json_cache_mem_test.dart @@ -23,7 +23,7 @@ class _JsonCacheThrowsAfterN extends JsonCacheWrap { void main() { SharedPreferences.setMockInitialValues({}); - group('JsonCacheMem', () { + group('JsonCacheMem:', () { const profKey = 'profile'; const profData = {'id': 1, 'name': 'John Due'}; const prefKey = 'preferences'; @@ -36,19 +36,19 @@ void main() { prefKey: prefData, }; - group('JsonCacheMem.init constructor', () { + group('"init" constructor', () { const data = ?>{ 'firstRow': {'row1': true}, 'secondRow': {'row2': false}, }; - test('data initialization — deep copy', () async { + test('should perform a deep copy of the input data', () async { final init = ?>{}; final JsonCacheMem initMemCache = JsonCacheMem.init(data, level2: JsonCacheFake.mem(init)); await initMemCache.value('x'); // wait... expect(init, data); }); - test('rollback on error', () async { + test('should rollback on error', () async { final init = ?>{}; final JsonCacheMem initMemCache = JsonCacheMem.init( data, @@ -60,7 +60,7 @@ void main() { /// logic of the init constructor. expect(init.isEmpty, true); }); - test('onInitError callback', () async { + test('should invoke "onInitError" callback on error', () async { final init = ?>{}; bool invoked = false; final JsonCacheMem initMemCache = JsonCacheMem.init( @@ -75,8 +75,8 @@ void main() { expect(invoked, true); }); }); - group('clear', () { - test('default ctor', () async { + group('"clear" method', () { + test('should clear all data when default constructed', () async { final JsonCacheMem memCache = JsonCacheMem(); await memCache.refresh(profKey, profData); await memCache.refresh(prefKey, prefData); @@ -86,7 +86,8 @@ void main() { final cachedPref = await memCache.value(prefKey); expect(cachedPref, isNull); }); - test('init ctor', () async { + test('should clear all data when built with the "init" constructor', + () async { final JsonCacheMem initMemCache = JsonCacheMem.init(data); await initMemCache.refresh(profKey, profData); await initMemCache.refresh(prefKey, prefData); @@ -96,7 +97,8 @@ void main() { final cachedPref = await initMemCache.value(prefKey); expect(cachedPref, isNull); }); - test('mem ctor', () async { + test('should clear all data when built with the "mem" constructor', + () async { final copy = Map?>.of(data); final JsonCacheMem memCache = JsonCacheMem.mem(copy); await memCache.clear(); @@ -110,31 +112,52 @@ void main() { expect(copy.isEmpty, true); }); }); - test('contains', () async { - final memCache = JsonCacheMem(); - // update data - await memCache.refresh(profKey, profData); - await memCache.refresh(prefKey, prefData); - - // test for `true` - expect(await memCache.contains(profKey), true); - expect(await memCache.contains(prefKey), true); - - // test for `false` - await memCache.remove(prefKey); - expect(await memCache.contains(prefKey), false); - expect(await memCache.contains('a key'), false); - }); - test('keys', () async { - final memCache = JsonCacheMem(); - // update data - await memCache.refresh(profKey, profData); - await memCache.refresh(prefKey, prefData); + group('"contains" method', () { + test('“contains” method should test “true” against data keys', () async { + final memCache = JsonCacheMem(); + // update data + await memCache.refresh(profKey, profData); + await memCache.refresh(prefKey, prefData); + + // test for `true` + expect(await memCache.contains(profKey), true); + expect(await memCache.contains(prefKey), true); + }); + test('“contains” method should test “false” against removed keys', + () async { + final memCache = JsonCacheMem(); + // update data + await memCache.refresh(profKey, profData); + await memCache.refresh(prefKey, prefData); - expect(await memCache.keys(), [profKey, prefKey]); + // test for `false` + await memCache.remove(prefKey); + expect(await memCache.contains(prefKey), false); + expect(await memCache.contains('a key'), false); + }); + }); + group('method "keys"', () { + late JsonCacheMem memCache; + setUp(() async { + memCache = JsonCacheMem(); + // update data + await memCache.refresh(profKey, profData); + await memCache.refresh(prefKey, prefData); + }); + test('should return the inserted keys', () async { + expect(await memCache.keys(), [profKey, prefKey]); + }); + test('should keep the returned keys immutable', () async { + final keys = await memCache.keys(); + // This should not change the 'keys' variable. + await memCache + .refresh('info', {'This is very important information.': true}); + expect(keys, [profKey, prefKey]); + }); }); - group('remove', () { - test('default ctor', () async { + group('method "remove"', () { + test('should remove the cache line by key when default constructed', + () async { final JsonCacheMem memCache = JsonCacheMem(); await memCache.refresh(profKey, profData); await memCache.refresh(prefKey, prefData); @@ -145,7 +168,9 @@ void main() { final cachedPref = await memCache.value(prefKey); expect(cachedPref, isNull); }); - test('init ctor', () async { + test( + 'should remove the cache line by key when built with the "init" constructor', + () async { final JsonCacheMem initMemCache = JsonCacheMem.init(data); await initMemCache.refresh(profKey, profData); await initMemCache.refresh(prefKey, prefData); @@ -159,7 +184,8 @@ void main() { }); }); group('refresh and value', () { - test('default ctor', () async { + test('should update and retrieve the new values when default constructed', + () async { final JsonCacheMem memCache = JsonCacheMem(); await memCache.refresh(profKey, profData); await memCache.refresh(prefKey, prefData); @@ -170,7 +196,9 @@ void main() { final cachedPref = await memCache.value(prefKey); expect(cachedPref, prefData); }); - test('init ctor', () async { + test( + 'should update and retrieve the new values when built with the "init" constructor', + () async { final JsonCacheMem initMemCache = JsonCacheMem.init(data); await initMemCache.refresh(profKey, profData); await initMemCache.refresh(prefKey, prefData); @@ -181,7 +209,9 @@ void main() { final cachedPref = await initMemCache.value(prefKey); expect(cachedPref, prefData); }); - test('mem ctor', () async { + test( + 'should update and retrieve the new values when built with the "mem" constructor', + () async { final mem = ?>{}; final JsonCacheMem memCache = JsonCacheMem.mem(mem); await memCache.refresh(profKey, profData); diff --git a/test/json_cache_safe_local_storage_test.dart b/test/json_cache_safe_local_storage_test.dart index 98d7d5f..d5b3123 100644 --- a/test/json_cache_safe_local_storage_test.dart +++ b/test/json_cache_safe_local_storage_test.dart @@ -38,7 +38,7 @@ void main() { ); group( - 'JsonCacheSafeLocalStorage', + 'JsonCacheSafeLocalStorage:', () { test( '"refresh", "value", "remove" with one piece of data', @@ -130,6 +130,23 @@ void main() { expect(await jsonCacheSafeLocalStorage.keys(), [profKey, prefKey]); }); + group('method "keys"', () { + setUp(() async { + // update data + await jsonCacheSafeLocalStorage.refresh(profKey, profData); + await jsonCacheSafeLocalStorage.refresh(prefKey, prefData); + }); + test('should return the inserted keys', () async { + expect(await jsonCacheSafeLocalStorage.keys(), [profKey, prefKey]); + }); + test('should keep the returned keys immutable', () async { + final keys = await jsonCacheSafeLocalStorage.keys(); + // This should not change the 'keys' variable. + await jsonCacheSafeLocalStorage + .refresh('info', {'This is very important information.': true}); + expect(keys, [profKey, prefKey]); + }); + }); test( 'clear', () async { diff --git a/test/json_cache_shared_preferences_test.dart b/test/json_cache_shared_preferences_test.dart index c0172af..3b2cc2c 100644 --- a/test/json_cache_shared_preferences_test.dart +++ b/test/json_cache_shared_preferences_test.dart @@ -4,7 +4,7 @@ import 'package:shared_preferences/shared_preferences.dart'; void main() { SharedPreferences.setMockInitialValues({}); - group('JsonCacheSharedPreferences', () { + group('JsonCacheSharedPreferences:', () { const profKey = 'profile'; const prefKey = 'preferences'; const profData = {'id': 1, 'name': 'John Due'}; @@ -42,14 +42,25 @@ void main() { expect(await prefs.contains(prefKey), false); }); - test('keys', () async { - final JsonCacheSharedPreferences prefs = - JsonCacheSharedPreferences(await SharedPreferences.getInstance()); - // update data - await prefs.refresh(profKey, profData); - await prefs.refresh(prefKey, prefData); - - expect(await prefs.keys(), [profKey, prefKey]); + group('method "keys"', () { + late JsonCacheSharedPreferences prefsCache; + setUp(() async { + prefsCache = + JsonCacheSharedPreferences(await SharedPreferences.getInstance()); + // update data + await prefsCache.refresh(profKey, profData); + await prefsCache.refresh(prefKey, prefData); + }); + test('should return the inserted keys', () async { + expect(await prefsCache.keys(), [profKey, prefKey]); + }); + test('should keep the returned keys immutable', () async { + final keys = await prefsCache.keys(); + // This should not change the 'keys' variable. + await prefsCache + .refresh('info', {'This is very important information.': true}); + expect(keys, [profKey, prefKey]); + }); }); test('remove', () async { final JsonCacheSharedPreferences prefsCache = diff --git a/test/json_cache_try_test.dart b/test/json_cache_try_test.dart index b0f375d..61303fa 100644 --- a/test/json_cache_try_test.dart +++ b/test/json_cache_try_test.dart @@ -5,7 +5,7 @@ import 'package:mocktail/mocktail.dart'; class JsonCacheMock extends Mock implements JsonCache {} void main() { - group('JsonCacheTry', () { + group('JsonCacheTry:', () { final jsonCacheMock = JsonCacheMock(); test('should avoid JsonCacheTry as the wrapped instance', () async { expect( diff --git a/test/json_cache_wrap_test.dart b/test/json_cache_wrap_test.dart index fa24736..4e7ca64 100644 --- a/test/json_cache_wrap_test.dart +++ b/test/json_cache_wrap_test.dart @@ -6,7 +6,7 @@ class JsonCacheTestWrap extends JsonCacheWrap { } void main() { - group('JsonCacheWrap', () { + group('JsonCacheWrap:', () { const profKey = 'profile'; const prefKey = 'preferences'; const profData = {'id': 1, 'name': 'John Due'};