diff --git a/packages/logging_cloudwatch/aws_logging_cloudwatch/lib/src/queued_item_store/dart_queued_item_store.stub.dart b/packages/logging_cloudwatch/aws_logging_cloudwatch/lib/src/queued_item_store/dart_queued_item_store.stub.dart index fd2bd1fee4..5db1a8d647 100644 --- a/packages/logging_cloudwatch/aws_logging_cloudwatch/lib/src/queued_item_store/dart_queued_item_store.stub.dart +++ b/packages/logging_cloudwatch/aws_logging_cloudwatch/lib/src/queued_item_store/dart_queued_item_store.stub.dart @@ -3,7 +3,7 @@ import 'dart:async'; -import 'package:aws_common/aws_common.dart'; +import 'package:amplify_core/amplify_core.dart'; // ignore: implementation_imports import 'package:aws_logging_cloudwatch/src/queued_item_store/queued_item_store.dart'; import 'package:meta/meta.dart'; @@ -14,7 +14,7 @@ import 'package:meta/meta.dart'; class DartQueuedItemStore implements QueuedItemStore, Closeable { /// {@macro amplify_logging_cloudwatch.dart_queued_item_store} // ignore: avoid_unused_constructor_parameters - DartQueuedItemStore(String? storagePath) { + DartQueuedItemStore(AppPathProvider appPathProvider) { throw UnimplementedError('constructor has not been implemented.'); } diff --git a/packages/logging_cloudwatch/aws_logging_cloudwatch/lib/src/queued_item_store/dart_queued_item_store.web.dart b/packages/logging_cloudwatch/aws_logging_cloudwatch/lib/src/queued_item_store/dart_queued_item_store.web.dart index 36e55ab226..54abb7dbf6 100644 --- a/packages/logging_cloudwatch/aws_logging_cloudwatch/lib/src/queued_item_store/dart_queued_item_store.web.dart +++ b/packages/logging_cloudwatch/aws_logging_cloudwatch/lib/src/queued_item_store/dart_queued_item_store.web.dart @@ -15,7 +15,7 @@ class DartQueuedItemStore implements QueuedItemStore, Closeable { /// {@macro amplify_logging_cloudwatch.index_db_queued_item_store} // ignore: avoid_unused_constructor_parameters - DartQueuedItemStore(String? storagePath); + DartQueuedItemStore(AppPathProvider appPathProvider); late final QueuedItemStore _database = () { if (IndexedDbAdapter.checkIsIndexedDBSupported()) { diff --git a/packages/logging_cloudwatch/aws_logging_cloudwatch/test/queued_item_store/in_memory_queued_item_store_test.dart b/packages/logging_cloudwatch/aws_logging_cloudwatch/test/queued_item_store/in_memory_queued_item_store_test.dart new file mode 100644 index 0000000000..cfaae96fa6 --- /dev/null +++ b/packages/logging_cloudwatch/aws_logging_cloudwatch/test/queued_item_store/in_memory_queued_item_store_test.dart @@ -0,0 +1,262 @@ +// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved. +// SPDX-License-Identifier: Apache-2.0 + +import 'package:amplify_core/src/types/app_path_provider/app_path_provider.dart'; +import 'package:aws_logging_cloudwatch/src/queued_item_store/dart_queued_item_store.dart'; +import 'package:aws_logging_cloudwatch/src/queued_item_store/queued_item_store.dart'; +import 'package:test/test.dart'; + +class MyPathProvider implements AppPathProvider { + @override + Future getApplicationSupportPath() { + return Future.value('/tmp'); + } + + @override + Future getTemporaryPath() { + return Future.value('/tmp'); + } +} + +void main() { + late DartQueuedItemStore db; + + group('DartQueuedItemStore ', () { + setUpAll(() { + final pathProvider = MyPathProvider(); + db = DartQueuedItemStore(pathProvider); + }); + + tearDownAll(() async { + await db.clear(); + }); + + setUp(() async { + await db.clear(); + }); + + Future> getAll() async { + return db.getCount(100); + } + + test('writes values to storage', () async { + const values = ['0', '1', '2', '3', '4', '5']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + final readItems = await db.getCount(values.length); + final readValues = readItems.map((e) => e.value); + expect(readValues, equals(values)); + }); + + test('writes values to storage with enable queue rotation', () async { + const values = ['0', '1', '2', '3', '4', '5']; + for (final value in values) { + await db.addItem( + value, + DateTime.now().toIso8601String(), + enableQueueRotation: true, + ); + } + + final readItems = await getAll(); + expect(readItems.length, 1); + expect(readItems.first.value, values.last); + }); + + test('returns first n items in storage', () async { + const values = ['0', '1', '2', '3', '4', '5']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + final readItems = await db.getCount(3); + final readValues = readItems.map((e) => e.value); + expect(readValues, equals(values.sublist(0, 3))); + }); + + test( + 'returns all stored items when get request size exceeds stored item count', + () async { + const values = ['0', '1', '2', '3', '4', '5']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + final readItems = await db.getCount(100); + final readValues = readItems.map((e) => e.value); + expect(readValues, values); + }); + + test('deletes all items in storage', () async { + const values = ['0', '1', '2', '3', '4', '5']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + await db.deleteItems(await getAll()); + + final readItems = await getAll(); + expect(readItems, isEmpty); + }); + + test('deletes first subset of stored items', () async { + const values = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + await db.deleteItems(await db.getCount(3)); + + final readItems = await getAll(); + final readValues = readItems.map((e) => e.value); + expect(readValues, values.sublist(3)); + }); + + test('deletes middle subset of stored items', () async { + const values = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + final itemsToDelete = (await getAll()).toList().sublist(3, 7); + await db.deleteItems(itemsToDelete); + + final readItems = await getAll(); + final readValues = readItems.map((e) => e.value); + expect(readValues, const ['0', '1', '2', '7', '8', '9']); + }); + + test('deletes last subset of stored items', () async { + const values = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + final itemsToDelete = (await getAll()).toList().sublist(7); + await db.deleteItems(itemsToDelete); + + final readItems = await getAll(); + final readValues = readItems.map((e) => e.value); + expect(readValues, values.sublist(0, 7)); + }); + + test('deletes first, middle, and last subsets of stored items', () async { + const values = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + const valuesToDelete = ['0', '1', '4', '5', '8', '9']; + final itemsToDelete = (await getAll()).where((item) { + return valuesToDelete.contains(item.value); + }); + await db.deleteItems(itemsToDelete); + + final readItems = await getAll(); + final readValues = readItems.map((e) => e.value); + expect(readValues, const ['2', '3', '6', '7']); + }); + + test('deletes inner left and right subsets of stored items', () async { + const values = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + const valuesToDelete = ['1', '2', '4', '5', '7', '8']; + final itemsToDelete = (await getAll()).where((item) { + return valuesToDelete.contains(item.value); + }); + await db.deleteItems(itemsToDelete); + + final readItems = await getAll(); + final readValues = readItems.map((e) => e.value); + expect(readValues, const ['0', '3', '6', '9']); + }); + + test('deletes the first stored item', () async { + const values = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + final itemsToDelete = (await getAll()).toList().sublist(0, 1); + await db.deleteItems(itemsToDelete); + + final readItems = await getAll(); + final readValues = readItems.map((e) => e.value); + expect(readValues, values.sublist(1)); + }); + + test('deletes the last stored item', () async { + const values = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + final itemsToDelete = (await getAll()).toList().sublist(9); + await db.deleteItems(itemsToDelete); + + final readItems = await getAll(); + final readValues = readItems.map((e) => e.value); + expect(readValues, values.sublist(0, 9)); + }); + + test('throws no error when deleting all items twice', () async { + const values = ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + var readValues = await getAll(); + + await db.deleteItems(readValues); + await db.deleteItems(readValues); + + readValues = await getAll(); + + expect(readValues, isEmpty); + }); + + test('returns all stored items', () async { + const values = ['0', '1', '2', '3', '4', '5']; + for (final value in values) { + await db.addItem(value, DateTime.now().toIso8601String()); + } + + final readItems = await getAll(); + final readValues = readItems.map((e) => e.value); + expect(readValues, values); + }); + + test('returns empty list when no items are stored', () async { + final readItems = await getAll(); + expect(readItems, isEmpty); + }); + + test( + 'checks if storage is full', + () async { + const capacityLimit = 1; + + final largeItem = '0' * 10240; // 10KB for example + + for (var i = 0; i < 5; i++) { + await db.addItem(largeItem, DateTime.now().toIso8601String()); + } + + var result = db.isFull(capacityLimit); + expect(result, isFalse); + + for (var i = 0; i < 100; i++) { + await db.addItem(largeItem, DateTime.now().toIso8601String()); + } + + result = db.isFull(capacityLimit); + expect(result, isTrue); + }, + ); + }); +}