// test/unit/login_use_case_test.dart // // Unit test untuk LoginUseCase / AuthRepositoryImpl. // Jalankan: flutter test test/unit/login_use_case_test.dart // // Dependencies (pubspec.yaml dev_dependencies): // flutter_test: sdk: flutter // mockito: ^5.4.4 // build_runner: ^2.4.6 import 'package:dartz/dartz.dart'; import 'package:flutter_test/flutter_test.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; // ---------- Minimal stubs (copy dari project asli) ---------- abstract class Failure { final String message; const Failure(this.message); } class ServerFailure extends Failure { const ServerFailure(super.message); } class NetworkFailure extends Failure { const NetworkFailure(super.message); } class AuthFailure extends Failure { const AuthFailure(super.message); } class UserEntity { final String token; final String role; UserEntity({required this.token, required this.role}); } abstract class AuthRepository { Future> login(String email, String password); } // ---------- Mock abstract classes ---------- @GenerateMocks([AuthRepository]) // File mock di-generate via: flutter pub run build_runner build // Untuk demo tanpa build_runner, kita buat manual mock di bawah class MockAuthRepository extends Mock implements AuthRepository {} // ---------- Use case ---------- class LoginUseCase { final AuthRepository repository; LoginUseCase(this.repository); Future> call(String email, String password) { if (email.trim().isEmpty) { return Future.value(const Left(AuthFailure('Email tidak boleh kosong'))); } if (password.length < 6) { return Future.value( const Left(AuthFailure('Password minimal 6 karakter'))); } return repository.login(email, password); } } // ---------- Tests ---------- void main() { late LoginUseCase loginUseCase; late MockAuthRepository mockAuthRepository; setUp(() { mockAuthRepository = MockAuthRepository(); loginUseCase = LoginUseCase(mockAuthRepository); }); group('LoginUseCase — validasi input', () { test('harus return AuthFailure jika email kosong', () async { final result = await loginUseCase.call('', 'password123'); expect(result.isLeft(), true); result.fold( (failure) { expect(failure, isA()); expect(failure.message, contains('kosong')); }, (_) => fail('Seharusnya return Left'), ); }); test('harus return AuthFailure jika email hanya spasi', () async { final result = await loginUseCase.call(' ', 'password123'); expect(result.isLeft(), true); result.fold( (failure) => expect(failure, isA()), (_) => fail('Seharusnya return Left'), ); }); test('harus return AuthFailure jika password kurang dari 6 karakter', () async { final result = await loginUseCase.call('user@test.com', '123'); expect(result.isLeft(), true); result.fold( (failure) { expect(failure, isA()); expect(failure.message, contains('6')); }, (_) => fail('Seharusnya return Left'), ); }); test('harus return AuthFailure jika password tepat 5 karakter', () async { final result = await loginUseCase.call('user@test.com', 'abc12'); expect(result.isLeft(), true); result.fold( (failure) => expect(failure, isA()), (_) => fail('Seharusnya return Left'), ); }); }); group('LoginUseCase — delegasi ke repository', () { test('harus call repository.login dengan email dan password yang benar', () async { const email = 'evan@test.com'; const password = 'password123'; final fakeUser = UserEntity(token: 'jwt_token_xyz', role: 'ROLE_USER'); when(mockAuthRepository.login(email, password)) .thenAnswer((_) async => Right(fakeUser)); final result = await loginUseCase.call(email, password); verify(mockAuthRepository.login(email, password)).called(1); expect(result.isRight(), true); }); test('harus return UserEntity dengan token saat login sukses', () async { const email = 'guardian@test.com'; const password = 'secure123'; final fakeUser = UserEntity(token: 'guardian_jwt', role: 'ROLE_GUARDIAN'); when(mockAuthRepository.login(email, password)) .thenAnswer((_) async => Right(fakeUser)); final result = await loginUseCase.call(email, password); result.fold( (_) => fail('Seharusnya sukses'), (user) { expect(user.token, 'guardian_jwt'); expect(user.role, 'ROLE_GUARDIAN'); }, ); }); test('harus return ServerFailure saat API gagal', () async { const email = 'user@test.com'; const password = 'password123'; when(mockAuthRepository.login(email, password)).thenAnswer( (_) async => const Left(ServerFailure('Server tidak merespons'))); final result = await loginUseCase.call(email, password); expect(result.isLeft(), true); result.fold( (failure) { expect(failure, isA()); expect(failure.message, 'Server tidak merespons'); }, (_) => fail('Seharusnya return Left'), ); }); test('harus return NetworkFailure saat tidak ada koneksi', () async { const email = 'user@test.com'; const password = 'password123'; when(mockAuthRepository.login(email, password)).thenAnswer((_) async => const Left(NetworkFailure('Tidak ada koneksi internet'))); final result = await loginUseCase.call(email, password); result.fold( (failure) => expect(failure, isA()), (_) => fail('Seharusnya return Left'), ); }); test('tidak boleh call repository jika validasi gagal', () async { await loginUseCase.call('', 'password123'); verifyNever(mockAuthRepository.login('ignored', 'ignored')); }); test('harus return Right dengan role ROLE_USER untuk user biasa', () async { final fakeUser = UserEntity(token: 'user_token', role: 'ROLE_USER'); when(mockAuthRepository.login('user@test.com', 'user1234')) .thenAnswer((_) async => Right(fakeUser)); final result = await loginUseCase.call('user@test.com', 'user1234'); result.fold( (_) => fail('Seharusnya sukses'), (user) => expect(user.role, 'ROLE_USER'), ); }); test('password tepat 6 karakter harus lolos validasi', () async { final fakeUser = UserEntity(token: 'tok', role: 'ROLE_USER'); when(mockAuthRepository.login('a@b.com', 'abc123')) .thenAnswer((_) async => Right(fakeUser)); final result = await loginUseCase.call('a@b.com', 'abc123'); expect(result.isRight(), true); }); }); group('LoginUseCase — edge cases', () { test('harus handle karakter spesial di email', () async { final fakeUser = UserEntity(token: 'tok', role: 'ROLE_USER'); const email = 'user+tag@example.co.id'; when(mockAuthRepository.login(email, 'password123')) .thenAnswer((_) async => Right(fakeUser)); final result = await loginUseCase.call(email, 'password123'); expect(result.isRight(), true); }); test('harus handle exception dari repository', () async { when(mockAuthRepository.login('user@test.com', 'password123')) .thenThrow(Exception('Unexpected error')); // LoginUseCase tidak wrap exception dari repo sendiri — test ini // memverifikasi bahwa exception propagate (akan di-handle di BLoC) expect( () => loginUseCase.call('user@test.com', 'password123'), throwsException, ); }); }); }