import 'package:flutter/foundation.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:immich_mobile/domain/models/exif.model.dart'; import 'package:immich_mobile/domain/services/asset.service.dart'; import 'package:mocktail/mocktail.dart'; import '../../infrastructure/repository.mock.dart'; import '../../test_utils.dart'; void main() { late AssetService sut; late MockRemoteAssetRepository mockRemoteAssetRepository; late MockDriftLocalAssetRepository mockLocalAssetRepository; setUp(() { mockRemoteAssetRepository = MockRemoteAssetRepository(); mockLocalAssetRepository = MockDriftLocalAssetRepository(); sut = AssetService( remoteAssetRepository: mockRemoteAssetRepository, localAssetRepository: mockLocalAssetRepository, ); }); group('getAspectRatio', () { test('flips dimensions on Android for 90° and 270° orientations', () async { debugDefaultTargetPlatformOverride = TargetPlatform.android; addTearDown(() => debugDefaultTargetPlatformOverride = null); for (final orientation in [90, 270]) { final localAsset = TestUtils.createLocalAsset( id: 'local-$orientation', width: 1920, height: 1080, orientation: orientation, ); final result = await sut.getAspectRatio(localAsset); expect(result, 1080 / 1920, reason: 'Orientation $orientation should flip on Android'); } }); test('does not flip dimensions on iOS regardless of orientation', () async { debugDefaultTargetPlatformOverride = TargetPlatform.iOS; addTearDown(() => debugDefaultTargetPlatformOverride = null); for (final orientation in [0, 90, 270]) { final localAsset = TestUtils.createLocalAsset( id: 'local-$orientation', width: 1920, height: 1080, orientation: orientation, ); final result = await sut.getAspectRatio(localAsset); expect(result, 1920 / 1080, reason: 'iOS should never flip dimensions'); } }); test('fetches dimensions from remote repository when missing from asset', () async { final remoteAsset = TestUtils.createRemoteAsset(id: 'remote-1', width: null, height: null); final exif = const ExifInfo(orientation: '1'); final fetchedAsset = TestUtils.createRemoteAsset(id: 'remote-1', width: 1920, height: 1080); when(() => mockRemoteAssetRepository.getExif('remote-1')).thenAnswer((_) async => exif); when(() => mockRemoteAssetRepository.get('remote-1')).thenAnswer((_) async => fetchedAsset); final result = await sut.getAspectRatio(remoteAsset); expect(result, 1920 / 1080); verify(() => mockRemoteAssetRepository.get('remote-1')).called(1); }); test('fetches dimensions from local repository when missing from local asset', () async { final localAsset = TestUtils.createLocalAsset(id: 'local-1', width: null, height: null, orientation: 0); final fetchedAsset = TestUtils.createLocalAsset(id: 'local-1', width: 1920, height: 1080, orientation: 0); when(() => mockLocalAssetRepository.get('local-1')).thenAnswer((_) async => fetchedAsset); final result = await sut.getAspectRatio(localAsset); expect(result, 1920 / 1080); verify(() => mockLocalAssetRepository.get('local-1')).called(1); }); test('returns 1.0 when dimensions are still unavailable after fetching', () async { final remoteAsset = TestUtils.createRemoteAsset(id: 'remote-1', width: null, height: null); final exif = const ExifInfo(orientation: '1'); when(() => mockRemoteAssetRepository.getExif('remote-1')).thenAnswer((_) async => exif); when(() => mockRemoteAssetRepository.get('remote-1')).thenAnswer((_) async => null); final result = await sut.getAspectRatio(remoteAsset); expect(result, 1.0); }); test('returns 1.0 when height is zero', () async { final remoteAsset = TestUtils.createRemoteAsset(id: 'remote-1', width: 1920, height: 0); final exif = const ExifInfo(orientation: '1'); when(() => mockRemoteAssetRepository.getExif('remote-1')).thenAnswer((_) async => exif); final result = await sut.getAspectRatio(remoteAsset); expect(result, 1.0); }); test('handles local asset with remoteId and uses exif from remote', () async { final localAsset = TestUtils.createLocalAsset( id: 'local-1', remoteId: 'remote-1', width: 1920, height: 1080, orientation: 0, ); final exif = const ExifInfo(orientation: '6'); when(() => mockRemoteAssetRepository.getExif('remote-1')).thenAnswer((_) async => exif); final result = await sut.getAspectRatio(localAsset); expect(result, 1080 / 1920); }); test('handles various flipped EXIF orientations correctly', () async { final flippedOrientations = ['5', '6', '7', '8', '90', '-90']; for (final orientation in flippedOrientations) { final remoteAsset = TestUtils.createRemoteAsset(id: 'remote-$orientation', width: 1920, height: 1080); final exif = ExifInfo(orientation: orientation); when(() => mockRemoteAssetRepository.getExif('remote-$orientation')).thenAnswer((_) async => exif); final result = await sut.getAspectRatio(remoteAsset); expect(result, 1080 / 1920, reason: 'Orientation $orientation should flip dimensions'); } }); test('handles various non-flipped EXIF orientations correctly', () async { final nonFlippedOrientations = ['1', '2', '3', '4']; for (final orientation in nonFlippedOrientations) { final remoteAsset = TestUtils.createRemoteAsset(id: 'remote-$orientation', width: 1920, height: 1080); final exif = ExifInfo(orientation: orientation); when(() => mockRemoteAssetRepository.getExif('remote-$orientation')).thenAnswer((_) async => exif); final result = await sut.getAspectRatio(remoteAsset); expect(result, 1920 / 1080, reason: 'Orientation $orientation should NOT flip dimensions'); } }); }); }