import 'dart:convert'; import 'package:flutter_test/flutter_test.dart'; import 'package:halkyon_learn_flutter/src/core/content/payload.dart'; import 'package:halkyon_learn_flutter/src/feature/jellyfin/bloc/jellyfin_ties_cubit.dart'; import 'package:halkyon_learn_flutter/src/feature/jellyfin/data/jellyfin_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/jellyfin/model/jellyfin.dart'; import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_payload.dart'; import 'package:halkyon_learn_flutter/src/feature/settings/data/settings_repository.dart'; import 'package:halkyon_learn_flutter/src/feature/settings/model/person_settings.dart'; import '../../support/fake_settings_gateway.dart'; import '../../support/sample_episode.dart'; void main() { const elsewhere = JellyfinTie(serverId: 'server-2', itemId: 'show-9'); late FakeSettingsGateway settingsGateway; late SettingsRepository settings; late JellyfinRepository jellyfin; late JellyfinTiesCubit cubit; setUp(() async { settingsGateway = FakeSettingsGateway(); settings = SettingsRepository(settingsGateway); jellyfin = JellyfinRepository( (_) => sampleJellyfin(), signedInJellyfin(), deviceName: 'Test', appVersion: '1', ); await jellyfin.open(); }); tearDown(() async { await cubit.close(); await settings.dispose(); }); /// The cubit's state once it settled. Future settled() async { await pumpEventQueue(); return cubit.state; } test("a studyset's ties are the signed-in server's, named as the server " 'names them now, each tied once', () async { await settings.update( (settings) => settings.withJellyfinTie('japanese', elsewhere), ); cubit = JellyfinTiesCubit(settings, jellyfin, 'japanese'); expect((await settled()).items, isEmpty); await cubit.tie(sampleShow); await cubit.tie(sampleShow); final state = await settled(); expect(state.items, [sampleShow]); expect(state.isTied('show-1'), isTrue); expect((await settings.read()).jellyfinTiesOf('japanese'), [ elsewhere, const JellyfinTie(serverId: 'server-1', itemId: 'show-1'), ]); }); test('the instance gets ids only, never a title', () async { cubit = JellyfinTiesCubit(settings, jellyfin, 'japanese'); await cubit.tie(sampleShow); await settled(); final (_, payload) = settingsGateway.writes.last; expect( utf8.decode(payload.buffer.asUint8List()), isNot(contains('Sample')), ); expect( decodePayload(payload, newestSchema: settingsSchemaVersion), containsPair('jellyfinTies', { 'japanese': [ {'server': 'server-1', 'item': 'show-1'}, ], }), ); }); test("a tie made on another device shows, named by this device's " 'server', () async { cubit = JellyfinTiesCubit(settings, jellyfin, 'japanese'); expect((await settled()).items, isEmpty); settingsGateway.receiveRemoteRow( 'settings-remote', encodeSettingsPayload( const PersonSettings().withJellyfinTie( 'japanese', const JellyfinTie(serverId: 'server-1', itemId: 'show-1'), ), ), ); expect((await settled()).items, [sampleShow]); }); test("untying keeps the other servers' ties; an item the server no " 'longer has is left out', () async { await settings.update( (settings) => settings .withJellyfinTie('japanese', elsewhere) .withJellyfinTie( 'japanese', const JellyfinTie(serverId: 'server-1', itemId: 'gone'), ) .withJellyfinTie( 'japanese', const JellyfinTie(serverId: 'server-1', itemId: 'show-1'), ), ); cubit = JellyfinTiesCubit(settings, jellyfin, 'japanese'); expect((await settled()).items, [sampleShow]); await cubit.untie('show-1'); expect((await settled()).items, isEmpty); expect((await settings.read()).jellyfinTiesOf('japanese'), [ elsewhere, const JellyfinTie(serverId: 'server-1', itemId: 'gone'), ]); }); test('signed out, nothing is tied', () async { await settings.update( (settings) => settings.withJellyfinTie( 'japanese', const JellyfinTie(serverId: 'server-1', itemId: 'show-1'), ), ); cubit = JellyfinTiesCubit(settings, jellyfin, 'japanese'); expect((await settled()).items, [sampleShow]); await jellyfin.signOut(); final state = await settled(); expect(state.items, isEmpty); expect(state.isTied('show-1'), isFalse); }); }