Skip to content

Commit d735f48

Browse files
committed
✨ refactor isAssociatedKey to _isAssociatedKey and add tests for box reopening
1 parent 5bc1be7 commit d735f48

File tree

3 files changed

+148
-23
lines changed

3 files changed

+148
-23
lines changed

packages/hyper_storage/lib/src/api/storage_container.dart

Lines changed: 12 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ abstract class StorageContainer extends BaseStorage {
261261
///
262262
/// See also:
263263
/// - [encodeKey] for the reverse operation
264-
/// - [isAssociatedKey] for checking if a key belongs to this container
264+
/// - [_isAssociatedKey] for checking if a key belongs to this container
265265
@visibleForTesting
266266
@protected
267267
@internal
@@ -284,18 +284,7 @@ abstract class StorageContainer extends BaseStorage {
284284
/// * [StateError] if the container name is empty or only whitespace when
285285
/// performing the check. This should never happen in normal operation as
286286
/// the name is validated during construction.
287-
@internal
288-
@protected
289-
bool isAssociatedKey(String rawKey) {
290-
if (name.isEmpty) throw StateError('Container name cannot be empty when checking associated keys.');
291-
if (name.trim().isEmpty) {
292-
// Defensive check; should never happen due to constructor validation.
293-
throw StateError(
294-
'Container name cannot be only whitespace when checking associated keys.',
295-
); // coverage:ignore-line
296-
}
297-
return rawKey.startsWith('$name$delimiter');
298-
}
287+
bool _isAssociatedKey(String rawKey) => rawKey.startsWith('$name$delimiter');
299288

300289
/// Returns a set of all encoded keys in the container.
301290
///
@@ -310,11 +299,11 @@ abstract class StorageContainer extends BaseStorage {
310299
///
311300
/// See also:
312301
/// - [getDecodedKeys] for getting keys without the container prefix
313-
/// - [isAssociatedKey] which is used for filtering
302+
/// - [_isAssociatedKey] which is used for filtering
314303
@protected
315304
Future<Set<String>> getEncodedKeys() async {
316305
final allKeys = await backend.getKeys();
317-
return allKeys.where(isAssociatedKey).toSet();
306+
return allKeys.where(_isAssociatedKey).toSet();
318307
}
319308

320309
/// Returns a set of all decoded keys in the container.
@@ -331,11 +320,11 @@ abstract class StorageContainer extends BaseStorage {
331320
/// See also:
332321
/// - [getEncodedKeys] for getting keys with the container prefix
333322
/// - [decodeKey] which is used to decode each key
334-
/// - [isAssociatedKey] which is used for filtering
323+
/// - [_isAssociatedKey] which is used for filtering
335324
@protected
336325
Future<Set<String>> getDecodedKeys() async {
337326
final allKeys = await backend.getKeys();
338-
return allKeys.where(isAssociatedKey).map(decodeKey).toSet();
327+
return allKeys.where(_isAssociatedKey).map(decodeKey).toSet();
339328
}
340329

341330
/// Clears all data stored in this container.
@@ -361,39 +350,39 @@ abstract class StorageContainer extends BaseStorage {
361350

362351
@override
363352
bool hasKeyListeners(String key) {
364-
if (isAssociatedKey(key)) return super.hasKeyListeners(key);
353+
if (_isAssociatedKey(key)) return super.hasKeyListeners(key);
365354
return super.hasKeyListeners(encodeKey(key));
366355
}
367356

368357
@override
369358
void addKeyListener(String key, ListenableCallback listener) {
370-
if (isAssociatedKey(key)) return super.addKeyListener(key, listener);
359+
if (_isAssociatedKey(key)) return super.addKeyListener(key, listener);
371360
super.addKeyListener(encodeKey(key), listener);
372361
}
373362

374363
@override
375364
void removeKeyListener(String key, ListenableCallback listener) {
376-
if (isAssociatedKey(key)) return super.removeKeyListener(key, listener);
365+
if (_isAssociatedKey(key)) return super.removeKeyListener(key, listener);
377366
super.removeKeyListener(encodeKey(key), listener);
378367
}
379368

380369
@override
381370
void removeAllKeyListeners(String key) {
382-
if (isAssociatedKey(key)) return super.removeAllKeyListeners(key);
371+
if (_isAssociatedKey(key)) return super.removeAllKeyListeners(key);
383372
super.removeAllKeyListeners(encodeKey(key));
384373
}
385374

386375
@override
387376
void notifyKeyListeners(String key) {
388-
if (isAssociatedKey(key)) return super.notifyKeyListeners(key);
377+
if (_isAssociatedKey(key)) return super.notifyKeyListeners(key);
389378
super.notifyKeyListeners(encodeKey(key));
390379
}
391380

392381
@override
393382
void notifyListeners([String? key]) {
394383
if (key == null) return super.notifyListeners(key);
395384

396-
if (isAssociatedKey(key)) return super.notifyListeners(key);
385+
if (_isAssociatedKey(key)) return super.notifyListeners(key);
397386
super.notifyListeners(encodeKey(key));
398387
}
399388
}

packages/hyper_storage/lib/src/storage_base.dart

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ abstract class _HyperStorageImpl extends BaseStorage
3232
@internal
3333
@protected
3434
@override
35+
@visibleForTesting
3536
String encodeKey(String key) => key;
3637

3738
/// Validates that all keys in a collection are acceptable for storage operations.

packages/hyper_storage_hive/test/hive_backend_test.dart

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -459,5 +459,140 @@ void main() {
459459
expect(await backend.getString('key:with:colons'), 'value4');
460460
});
461461
});
462+
463+
group('box reopening', () {
464+
test('getString reopens box if closed', () async {
465+
await backend.setString('key', 'value');
466+
await backend.box.close();
467+
468+
// Should automatically reopen the box
469+
expect(await backend.getString('key'), 'value');
470+
expect(backend.box.isOpen, true);
471+
});
472+
473+
test('setString reopens box if closed', () async {
474+
await backend.box.close();
475+
476+
// Should automatically reopen the box
477+
await backend.setString('newKey', 'newValue');
478+
expect(await backend.getString('newKey'), 'newValue');
479+
expect(backend.box.isOpen, true);
480+
});
481+
482+
test('getInt reopens box if closed', () async {
483+
await backend.setInt('number', 42);
484+
await backend.box.close();
485+
486+
expect(await backend.getInt('number'), 42);
487+
expect(backend.box.isOpen, true);
488+
});
489+
490+
test('setInt reopens box if closed', () async {
491+
await backend.box.close();
492+
493+
await backend.setInt('count', 100);
494+
expect(await backend.getInt('count'), 100);
495+
expect(backend.box.isOpen, true);
496+
});
497+
498+
test('getDouble reopens box if closed', () async {
499+
await backend.setDouble('price', 9.99);
500+
await backend.box.close();
501+
502+
expect(await backend.getDouble('price'), 9.99);
503+
expect(backend.box.isOpen, true);
504+
});
505+
506+
test('setDouble reopens box if closed', () async {
507+
await backend.box.close();
508+
509+
await backend.setDouble('pi', 3.14);
510+
expect(await backend.getDouble('pi'), 3.14);
511+
expect(backend.box.isOpen, true);
512+
});
513+
514+
test('getBool reopens box if closed', () async {
515+
await backend.setBool('flag', true);
516+
await backend.box.close();
517+
518+
expect(await backend.getBool('flag'), true);
519+
expect(backend.box.isOpen, true);
520+
});
521+
522+
test('setBool reopens box if closed', () async {
523+
await backend.box.close();
524+
525+
await backend.setBool('enabled', false);
526+
expect(await backend.getBool('enabled'), false);
527+
expect(backend.box.isOpen, true);
528+
});
529+
530+
test('remove reopens box if closed', () async {
531+
await backend.setString('toRemove', 'value');
532+
await backend.box.close();
533+
534+
await backend.remove('toRemove');
535+
expect(await backend.containsKey('toRemove'), false);
536+
expect(backend.box.isOpen, true);
537+
});
538+
539+
test('removeAll reopens box if closed', () async {
540+
await backend.setString('key1', 'value1');
541+
await backend.setString('key2', 'value2');
542+
await backend.box.close();
543+
544+
await backend.removeAll(['key1', 'key2']);
545+
expect(await backend.containsKey('key1'), false);
546+
expect(await backend.containsKey('key2'), false);
547+
expect(backend.box.isOpen, true);
548+
});
549+
550+
test('containsKey reopens box if closed', () async {
551+
await backend.setString('exists', 'value');
552+
await backend.box.close();
553+
554+
expect(await backend.containsKey('exists'), true);
555+
expect(backend.box.isOpen, true);
556+
});
557+
558+
test('getKeys reopens box if closed', () async {
559+
await backend.setString('key1', 'value1');
560+
await backend.setString('key2', 'value2');
561+
await backend.box.close();
562+
563+
final keys = await backend.getKeys();
564+
expect(keys, containsAll(['key1', 'key2']));
565+
expect(backend.box.isOpen, true);
566+
});
567+
568+
test('getAll reopens box if closed', () async {
569+
await backend.setString('key1', 'value1');
570+
await backend.setInt('key2', 42);
571+
await backend.box.close();
572+
573+
final data = await backend.getAll({'key1', 'key2'});
574+
expect(data, {'key1': 'value1', 'key2': 42});
575+
expect(backend.box.isOpen, true);
576+
});
577+
578+
test('multiple operations after box close work correctly', () async {
579+
// Set up some initial data
580+
await backend.setString('str', 'text');
581+
await backend.setInt('num', 10);
582+
await backend.box.close();
583+
584+
// Multiple operations should all work
585+
await backend.setString('new', 'value');
586+
expect(await backend.getString('str'), 'text');
587+
await backend.setInt('num', 20);
588+
expect(await backend.getInt('num'), 20);
589+
await backend.setBool('bool', true);
590+
expect(await backend.getBool('bool'), true);
591+
592+
final keys = await backend.getKeys();
593+
expect(keys, containsAll(['str', 'num', 'new', 'bool']));
594+
expect(backend.box.isOpen, true);
595+
});
596+
});
462597
});
463598
}

0 commit comments

Comments
 (0)