From da3950810c75c2b117f1abc957d2dac3e92690d3 Mon Sep 17 00:00:00 2001 From: Stefan Date: Tue, 1 Oct 2024 12:18:18 -0400 Subject: [PATCH] wip test updates for last bit of cov --- .../test/src/fluttercon_cache_test.dart | 25 ------ .../fluttercon_cache/test/src/keys_test.dart | 28 ++++++ .../src/utils/try_get_from_cache_test.dart | 29 ++++++ .../lib/src/speakers_repository.dart | 11 +-- .../test/helpers/test_helpers.dart | 88 ++++++++++++++++++- .../test/src/speakers_repository_test.dart | 83 ++++++++--------- .../test/helpers/test_helpers.dart | 57 ++++++++++++ .../test/src/talks_repository_test.dart | 71 +++++++-------- 8 files changed, 274 insertions(+), 118 deletions(-) create mode 100644 api/packages/fluttercon_cache/test/src/keys_test.dart create mode 100644 api/packages/fluttercon_cache/test/src/utils/try_get_from_cache_test.dart diff --git a/api/packages/fluttercon_cache/test/src/fluttercon_cache_test.dart b/api/packages/fluttercon_cache/test/src/fluttercon_cache_test.dart index 5fb777e..8fcad57 100644 --- a/api/packages/fluttercon_cache/test/src/fluttercon_cache_test.dart +++ b/api/packages/fluttercon_cache/test/src/fluttercon_cache_test.dart @@ -1,5 +1,3 @@ -import 'dart:convert'; - import 'package:fluttercon_cache/fluttercon_cache.dart'; import 'package:test/test.dart'; @@ -14,28 +12,5 @@ void main() { tearDown(() { cache.clear(); }); - - group('getOrElse', () { - const cacheData = {'data': 'value'}; - const orElseData = {'data': 'orElse value'}; - test('returns the value if the key is found', () async { - await cache.set('key', jsonEncode(cacheData)); - final value = await cache.getOrElse( - key: 'key', - fromJson: (json) => json, - orElse: () async => orElseData, - ); - expect(value, equals(cacheData)); - }); - - test('calls orElse if the key is not found', () async { - final value = await cache.getOrElse( - key: 'key', - fromJson: (json) => json, - orElse: () async => orElseData, - ); - expect(value, equals(orElseData)); - }); - }); }); } diff --git a/api/packages/fluttercon_cache/test/src/keys_test.dart b/api/packages/fluttercon_cache/test/src/keys_test.dart new file mode 100644 index 0000000..8feff19 --- /dev/null +++ b/api/packages/fluttercon_cache/test/src/keys_test.dart @@ -0,0 +1,28 @@ +import 'package:fluttercon_cache/fluttercon_cache.dart'; +import 'package:test/test.dart'; + +void main() { + group('speakerCacheKey', () { + test('returns the correct cache key', () { + expect(speakerCacheKey('123'), equals('speaker_123')); + }); + }); + + group('speakerTalksCacheKey', () { + test('returns the correct cache key', () { + expect(speakerTalksCacheKey('1,2,3'), equals('speaker_talks_1,2,3')); + }); + }); + + group('favoritesCacheKey', () { + test('returns the correct cache key', () { + expect(favoritesCacheKey('123'), equals('favorites_123')); + }); + }); + + group('talkCacheKey', () { + test('returns the correct cache key', () { + expect(talkCacheKey('123'), equals('talk_123')); + }); + }); +} diff --git a/api/packages/fluttercon_cache/test/src/utils/try_get_from_cache_test.dart b/api/packages/fluttercon_cache/test/src/utils/try_get_from_cache_test.dart new file mode 100644 index 0000000..1560bba --- /dev/null +++ b/api/packages/fluttercon_cache/test/src/utils/try_get_from_cache_test.dart @@ -0,0 +1,29 @@ +import 'dart:convert'; + +import 'package:fluttercon_cache/fluttercon_cache.dart'; +import 'package:test/test.dart'; + +void main() { + group('tryGetFromCache', () { + const cacheData = {'data': 'value'}; + const orElseData = {'data': 'orElse value'}; + test('returns from cache if found', () async { + final result = await tryGetFromCache( + getFromCache: () async => jsonEncode(cacheData), + fromJson: (json) => json, + orElse: () async => orElseData, + ); + + expect(result, equals(cacheData)); + }); + + test('calls orElse if cache returns null', () async { + final result = await tryGetFromCache( + getFromCache: () async => null, + fromJson: (json) => json, + orElse: () async => orElseData, + ); + expect(result, equals(orElseData)); + }); + }); +} diff --git a/api/packages/speakers_repository/lib/src/speakers_repository.dart b/api/packages/speakers_repository/lib/src/speakers_repository.dart index 1d005ef..de8cff8 100644 --- a/api/packages/speakers_repository/lib/src/speakers_repository.dart +++ b/api/packages/speakers_repository/lib/src/speakers_repository.dart @@ -29,7 +29,7 @@ class SpeakersRepository { json, (val) => SpeakerPreview.fromJson((val ?? {}) as Map), ), - orElse: getSpeakersFromApi, + orElse: _getSpeakersFromApi, ); /// Fetches a [SpeakerDetail] with a given [id]. @@ -42,12 +42,11 @@ class SpeakersRepository { return tryGetFromCache( getFromCache: () => _cache.get(speakerCacheKey(id)), fromJson: SpeakerDetail.fromJson, - orElse: () => getSpeakerDetailFromApi(id: id, userId: userId), + orElse: () => _getSpeakerDetailFromApi(id: id, userId: userId), ); } - /// Fetches a paginated list of speakers from the api. - Future> getSpeakersFromApi() async { + Future> _getSpeakersFromApi() async { final speakers = await _dataSource.getSpeakers(); final speakerPreviews = speakers.items @@ -82,9 +81,7 @@ class SpeakersRepository { return result; } - /// Fetches a [SpeakerDetail] from the api with a given [id] and - /// the current [userId]. - Future getSpeakerDetailFromApi({ + Future _getSpeakerDetailFromApi({ required String id, required String userId, }) async { diff --git a/api/packages/speakers_repository/test/helpers/test_helpers.dart b/api/packages/speakers_repository/test/helpers/test_helpers.dart index 2edff4f..736a4eb 100644 --- a/api/packages/speakers_repository/test/helpers/test_helpers.dart +++ b/api/packages/speakers_repository/test/helpers/test_helpers.dart @@ -3,6 +3,9 @@ import 'package:fluttercon_shared_models/fluttercon_shared_models.dart'; class TestHelpers { static const userId = 'userId'; + static const favoritesId = 'favoritesId'; + static final talkDateTimeTemporal = TemporalDateTime(DateTime(2024)); + static final talkDateTime = talkDateTimeTemporal.getDateTimeInUtc(); static final speakers = PaginatedResult( [ @@ -11,18 +14,21 @@ class TestHelpers { name: 'John Doe', title: 'Test Title 1', imageUrl: 'Test Image Url 1', + bio: 'Sample bio', ), Speaker( id: '2', name: 'Jane Doe', title: 'Test Title 2', imageUrl: 'Test Image Url 2', + bio: 'Sample bio', ), Speaker( id: '3', name: 'John Smith', title: 'Test Title 3', imageUrl: 'Test Image Url 3', + bio: 'Sample bio', ), ], null, @@ -78,19 +84,66 @@ class TestHelpers { ], }; - static const speakerDetail = SpeakerDetail( + static final speakerDetail = SpeakerDetail( id: '1', name: 'John Doe', title: 'Test Title 1', imageUrl: 'Test Image Url 1', bio: 'Sample bio', - links: [], - talks: [], + links: const [ + SpeakerLink( + id: '1', + url: 'https://test.com', + type: SpeakerLinkType.other, + ), + ], + talks: [ + TalkPreview( + id: '1', + title: 'Test Talk', + room: 'Test Room', + startTime: talkDateTime, + speakerNames: const ['John Doe'], + isFavorite: true, + ), + ], ); + static final speakerDetailJson = { + 'id': '1', + 'name': 'John Doe', + 'title': 'Test Title 1', + 'imageUrl': 'Test Image Url 1', + 'bio': 'Sample bio', + 'links': [ + { + 'id': '1', + 'speaker': { + 'id': '1', + 'name': 'John Doe', + 'title': 'Test Title 1', + 'imageUrl': 'Test Image Url 1', + }, + 'url': 'https://test.com', + 'type': 'other', + } + ], + 'talks': [ + { + 'id': '1', + 'title': 'Test Talk', + 'room': 'Test Room', + 'startTime': talkDateTime.toIso8601String(), + 'speakerNames': ['John Doe'], + 'isFavorite': true, + }, + ], + }; + static final links = PaginatedResult( [ Link( + id: '1', speaker: speakers.items[0], url: 'https://test.com', type: LinkType.other, @@ -107,6 +160,7 @@ class TestHelpers { id: '1', title: 'Test Talk', room: 'Test Room', + startTime: talkDateTimeTemporal, ); static final talks = PaginatedResult( @@ -124,12 +178,38 @@ class TestHelpers { ); static final favorites = Favorites( - id: 'favoritesId', + id: favoritesId, userId: userId, talks: [ FavoritesTalk( + id: '1', talk: talk, ), ], ); + + static final favoritesJson = { + 'id': favoritesId, + 'userId': userId, + 'talks': [ + { + 'id': '1', + 'talk': { + 'id': '1', + 'title': 'Test Talk', + 'room': 'Test Room', + 'startTime': talkDateTime.toIso8601String(), + }, + }, + ], + }; + + static final favoritesResult = PaginatedResult( + [favorites], + null, + null, + null, + Favorites.classType, + null, + ); } diff --git a/api/packages/speakers_repository/test/src/speakers_repository_test.dart b/api/packages/speakers_repository/test/src/speakers_repository_test.dart index bde1ceb..77c67f8 100644 --- a/api/packages/speakers_repository/test/src/speakers_repository_test.dart +++ b/api/packages/speakers_repository/test/src/speakers_repository_test.dart @@ -1,5 +1,7 @@ // ignore_for_file: prefer_const_constructors +import 'dart:convert'; + import 'package:fluttercon_cache/fluttercon_cache.dart'; import 'package:fluttercon_data_source/fluttercon_data_source.dart'; import 'package:fluttercon_shared_models/fluttercon_shared_models.dart'; @@ -41,51 +43,27 @@ void main() { group('getSpeakers', () { setUp(() { when( - () => cache.getOrElse>( - key: speakersCacheKey, - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), + () => cache.get( + speakersCacheKey, ), - ).thenAnswer((_) async => TestHelpers.speakerPreviews); + ).thenAnswer((_) async => jsonEncode(TestHelpers.speakerPreviewsJson)); }); test('returns $SpeakerPreview list', () async { final speakers = await speakersRepository.getSpeakers(); expect(speakers, equals(TestHelpers.speakerPreviews)); }); - }); - - group('getSpeaker', () { - setUp(() { - when( - () => cache.getOrElse( - key: speakerCacheKey(TestHelpers.speakers.items[0]!.id), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), - ), - ).thenAnswer((_) async => TestHelpers.speakerDetail); - }); - - test('returns $SpeakerDetail', () async { - final speakers = await speakersRepository.getSpeaker( - id: TestHelpers.speakers.items[0]!.id, - userId: TestHelpers.userId, - ); - expect(speakers, equals(TestHelpers.speakerDetail)); - }); - }); - group('getSpeakersFromApi', () { - setUp(() { + test('fetches from api when cache is null', () async { + when(() => cache.get(speakersCacheKey)).thenAnswer((_) async => null); when(() => dataSource.getSpeakers()) .thenAnswer((_) async => TestHelpers.speakers); when(() => cache.set(speakersCacheKey, any())).thenAnswer( (_) async => {}, ); - }); + final speakers = await speakersRepository.getSpeakers(); + expect(speakers, equals(TestHelpers.speakerPreviews)); - test('fetches $Speaker list from api and caches', () async { - await speakersRepository.getSpeakersFromApi(); verify( () => dataSource.getSpeakers(), ).called(1); @@ -98,9 +76,28 @@ void main() { }); }); - group('getSpeakerDetailFromApi', () { - final speaker = TestHelpers.speakers.items[0]!; + group('getSpeaker', () { setUp(() { + when( + () => cache.get( + speakerCacheKey(TestHelpers.speakers.items[0]!.id), + ), + ).thenAnswer((_) async => jsonEncode(TestHelpers.speakerDetailJson)); + }); + + test('returns $SpeakerDetail', () async { + final speakers = await speakersRepository.getSpeaker( + id: TestHelpers.speakers.items[0]!.id, + userId: TestHelpers.userId, + ); + expect(speakers, equals(TestHelpers.speakerDetail)); + }); + + test('fetches from api when cache is null', () async { + final speaker = TestHelpers.speakers.items[0]!; + when(() => cache.get(speakerCacheKey(speaker.id))).thenAnswer( + (_) async => null, + ); when(() => dataSource.getSpeaker(id: speaker.id)) .thenAnswer((_) async => speaker); when(() => dataSource.getLinks(speaker: speaker)) @@ -110,21 +107,25 @@ void main() { when(() => dataSource.getSpeakerTalks(talks: [TestHelpers.talk])) .thenAnswer((_) async => TestHelpers.talks); when( - () => cache.getOrElse( - key: favoritesCacheKey(TestHelpers.userId), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), + () => cache.get( + favoritesCacheKey(TestHelpers.userId), + ), + ).thenAnswer((_) async => null); + when( + () => dataSource.getFavorites( + userId: TestHelpers.userId, ), - ).thenAnswer((_) async => TestHelpers.favorites); + ).thenAnswer((_) async => TestHelpers.favoritesResult); + when(() => cache.set(speakerCacheKey(speaker.id), any())) .thenAnswer((_) async => {}); - }); - test('fetches $SpeakerDetail from api and caches', () async { - await speakersRepository.getSpeakerDetailFromApi( + final speakers = await speakersRepository.getSpeaker( id: speaker.id, userId: TestHelpers.userId, ); + expect(speakers, equals(TestHelpers.speakerDetail)); + verify( () => dataSource.getSpeaker(id: speaker.id), ).called(1); diff --git a/api/packages/talks_repository/test/helpers/test_helpers.dart b/api/packages/talks_repository/test/helpers/test_helpers.dart index 1b21912..1779626 100644 --- a/api/packages/talks_repository/test/helpers/test_helpers.dart +++ b/api/packages/talks_repository/test/helpers/test_helpers.dart @@ -42,6 +42,32 @@ class TestHelpers { items: talks, ); + static final talksDataJson = { + 'items': [ + { + 'id': '1', + 'title': 'Test Talk 1', + 'room': 'Room 1', + 'startTime': talk1StartTime.toIso8601String(), + 'description': 'Test Description', + }, + { + 'id': '2', + 'title': 'Test Talk 2', + 'room': 'Room 2', + 'startTime': talk2StartTime.toIso8601String(), + 'description': 'Test Description', + }, + { + 'id': '3', + 'title': 'Test Talk 3', + 'room': 'Room 3', + 'startTime': talk3StartTime.toIso8601String(), + 'description': 'Test Description', + }, + ], + }; + static final talksResult = PaginatedResult( talks, null, @@ -178,6 +204,26 @@ class TestHelpers { static final speakerData = PaginatedData(items: speakerTalks); + static final speakerDataJson = { + 'items': [ + { + 'id': '1', + 'speaker': {'id': '1', 'name': 'Speaker 1'}, + 'talk': talksData.items[0]!.toJson(), + }, + { + 'id': '2', + 'speaker': {'id': '2', 'name': 'Speaker 2'}, + 'talk': talksData.items[1]!.toJson(), + }, + { + 'id': '3', + 'speaker': {'id': '3', 'name': 'Speaker 3'}, + 'talk': talksData.items[2]!.toJson(), + }, + ], + }; + static final speakerResult = PaginatedResult( speakerTalks, null, @@ -285,4 +331,15 @@ class TestHelpers { ], description: 'Test Description', ); + + static final talkDetailJson = { + 'id': '1', + 'title': 'Test Talk 1', + 'room': 'Room 1', + 'startTime': talk1StartTime.toIso8601String(), + 'speakers': [ + {'id': '1', 'name': 'Speaker 1', 'title': '', 'imageUrl': ''}, + ], + 'description': 'Test Description', + }; } diff --git a/api/packages/talks_repository/test/src/talks_repository_test.dart b/api/packages/talks_repository/test/src/talks_repository_test.dart index a5af559..8a6b8ae 100644 --- a/api/packages/talks_repository/test/src/talks_repository_test.dart +++ b/api/packages/talks_repository/test/src/talks_repository_test.dart @@ -1,4 +1,6 @@ // ignore_for_file: prefer_const_constructors +import 'dart:convert'; + import 'package:fluttercon_cache/fluttercon_cache.dart'; import 'package:fluttercon_data_source/fluttercon_data_source.dart'; import 'package:fluttercon_shared_models/fluttercon_shared_models.dart'; @@ -53,12 +55,10 @@ void main() { final request = TestHelpers.createFavoriteRequest; setUp(() { when( - () => cache.getOrElse( - key: favoritesCacheKey(request.userId), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), + () => cache.get( + favoritesCacheKey(request.userId), ), - ).thenAnswer((_) async => TestHelpers.favorites); + ).thenAnswer((_) async => jsonEncode(TestHelpers.favoritesJson)); when( () => dataSource.createFavoritesTalk( favoritesId: TestHelpers.favoritesId, @@ -93,12 +93,10 @@ void main() { final favoritesTalk = TestHelpers.favoritesTalkSingle.items.first!; setUp(() { when( - () => cache.getOrElse( - key: favoritesCacheKey(request.userId), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), + () => cache.get( + favoritesCacheKey(request.userId), ), - ).thenAnswer((_) async => TestHelpers.favorites); + ).thenAnswer((_) async => jsonEncode(TestHelpers.favoritesJson)); when( () => dataSource.getFavoritesTalks( favoritesId: TestHelpers.favoritesId, @@ -200,21 +198,17 @@ void main() { setUp(() { when( - () => cache.getOrElse( - key: favoritesCacheKey(TestHelpers.userId), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), + () => cache.get( + favoritesCacheKey(TestHelpers.userId), ), - ).thenAnswer((_) async => TestHelpers.favorites); + ).thenAnswer((_) async => jsonEncode(TestHelpers.favoritesJson)); when( - () => cache.getOrElse>( - key: speakerTalksCacheKey( + () => cache.get( + speakerTalksCacheKey( TestHelpers.favorites.talks!.map((e) => e.talk!.id).join(','), ), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), ), - ).thenAnswer((_) async => TestHelpers.speakerData); + ).thenAnswer((_) async => jsonEncode(TestHelpers.speakerDataJson)); }); test('returns $TalkTimeSlot data', () async { @@ -226,28 +220,25 @@ void main() { group('getTalks', () { setUp(() { when( - () => cache.getOrElse>( - key: talksCacheKey, - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), + () => cache.get( + talksCacheKey, ), - ).thenAnswer((_) async => TestHelpers.talksData); + ).thenAnswer((_) async => jsonEncode(TestHelpers.talksDataJson)); when( - () => cache.getOrElse>( - key: speakerTalksCacheKey( - TestHelpers.talksData.items.map((e) => e?.id).join(','), + () => cache.get( + speakerTalksCacheKey( + TestHelpers.talksData.items + .where((e) => e != null) + .map((e) => e?.id) + .join(','), ), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), ), - ).thenAnswer((_) async => TestHelpers.speakerData); + ).thenAnswer((_) async => jsonEncode(TestHelpers.speakerDataJson)); when( - () => cache.getOrElse( - key: favoritesCacheKey(TestHelpers.userId), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), + () => cache.get( + favoritesCacheKey(TestHelpers.userId), ), - ).thenAnswer((_) async => TestHelpers.favorites); + ).thenAnswer((_) async => jsonEncode(TestHelpers.favoritesJson)); }); test('returns $TalkTimeSlot data', () async { @@ -262,12 +253,10 @@ void main() { final talkDetail = TestHelpers.talkDetail; setUp(() { when( - () => cache.getOrElse( - key: talkCacheKey(talkDetail.id), - fromJson: any(named: 'fromJson'), - orElse: any(named: 'orElse'), + () => cache.get( + talkCacheKey(talkDetail.id), ), - ).thenAnswer((_) async => talkDetail); + ).thenAnswer((_) async => jsonEncode(TestHelpers.talkDetailJson)); }); test('returns $TalkDetail', () async {