409 lines
11 KiB
Dart

// test/unit/register_use_case_test.dart
//
// Unit test untuk RegisterUseCase.
// Jalankan: flutter test test/unit/register_use_case_test.dart
import 'package:dartz/dartz.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mockito/mockito.dart';
// ---------- Stubs ----------
abstract class Failure {
final String message;
const Failure(this.message);
}
class ServerFailure extends Failure {
const ServerFailure(super.message);
}
class ValidationFailure extends Failure {
const ValidationFailure(super.message);
}
class NetworkFailure extends Failure {
const NetworkFailure(super.message);
}
class UserEntity {
final String token;
final String role;
final String displayName;
final String? uniqueUserId; // hanya ROLE_USER yang punya
UserEntity({
required this.token,
required this.role,
required this.displayName,
this.uniqueUserId,
});
}
abstract class RegisterRepository {
Future<Either<Failure, UserEntity>> register({
required String email,
required String password,
required String displayName,
required String role, // 'ROLE_USER' | 'ROLE_GUARDIAN'
});
}
class MockRegisterRepository extends Mock implements RegisterRepository {
@override
Future<Either<Failure, UserEntity>> register({
String? email,
String? password,
String? displayName,
String? role,
}) =>
super.noSuchMethod(
Invocation.method(
#register,
const [],
{
#email: email,
#password: password,
#displayName: displayName,
#role: role,
},
),
returnValue: Future<Either<Failure, UserEntity>>.value(
const Left(ServerFailure('Repository belum di-stub')),
),
returnValueForMissingStub: Future<Either<Failure, UserEntity>>.value(
const Left(ServerFailure('Repository belum di-stub')),
),
) as Future<Either<Failure, UserEntity>>;
}
// ---------- Use case ----------
class RegisterUseCase {
final RegisterRepository repository;
RegisterUseCase(this.repository);
Future<Either<Failure, UserEntity>> call({
required String email,
required String password,
required String displayName,
required String role,
}) async {
// Validasi email format sederhana
if (email.trim().isEmpty || !email.contains('@')) {
return const Left(ValidationFailure('Format email tidak valid'));
}
// Validasi password
if (password.length < 6) {
return const Left(ValidationFailure('Password harus minimal 6 karakter'));
}
// Validasi displayName
if (displayName.trim().isEmpty) {
return const Left(ValidationFailure('Nama tidak boleh kosong'));
}
// Validasi role
if (role != 'ROLE_USER' && role != 'ROLE_GUARDIAN') {
return const Left(ValidationFailure('Role tidak valid'));
}
return repository.register(
email: email.trim(),
password: password,
displayName: displayName.trim(),
role: role,
);
}
}
// ---------- Tests ----------
void main() {
late RegisterUseCase registerUseCase;
late MockRegisterRepository mockRepo;
setUp(() {
mockRepo = MockRegisterRepository();
registerUseCase = RegisterUseCase(mockRepo);
});
group('RegisterUseCase — validasi email', () {
test('harus gagal jika email kosong', () async {
final result = await registerUseCase.call(
email: '',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
);
expect(result.isLeft(), true);
result.fold(
(f) => expect(f, isA<ValidationFailure>()),
(_) => fail('Seharusnya gagal'),
);
});
test('harus gagal jika email tanpa @', () async {
final result = await registerUseCase.call(
email: 'bukan-email',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
);
expect(result.isLeft(), true);
});
test('harus berhasil dengan email valid', () async {
final fakeUser = UserEntity(
token: 'tok',
role: 'ROLE_USER',
displayName: 'Evan',
uniqueUserId: 'ABC123DEF456',
);
when(mockRepo.register(
email: 'evan@test.com',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
)).thenAnswer((_) async => Right(fakeUser));
final result = await registerUseCase.call(
email: 'evan@test.com',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
);
expect(result.isRight(), true);
});
});
group('RegisterUseCase — validasi password', () {
test('harus gagal jika password kurang dari 6 karakter', () async {
final result = await registerUseCase.call(
email: 'evan@test.com',
password: '12345',
displayName: 'Evan',
role: 'ROLE_USER',
);
expect(result.isLeft(), true);
result.fold(
(f) {
expect(f, isA<ValidationFailure>());
expect(f.message, contains('6'));
},
(_) => fail('Seharusnya gagal'),
);
});
test('harus gagal jika password kosong', () async {
final result = await registerUseCase.call(
email: 'evan@test.com',
password: '',
displayName: 'Evan',
role: 'ROLE_USER',
);
expect(result.isLeft(), true);
});
test('password tepat 6 karakter harus lolos', () async {
final fakeUser =
UserEntity(token: 'tok', role: 'ROLE_USER', displayName: 'Evan');
when(mockRepo.register(
email: 'evan@test.com',
password: 'abc123',
displayName: 'Evan',
role: 'ROLE_USER',
)).thenAnswer((_) async => Right(fakeUser));
final result = await registerUseCase.call(
email: 'evan@test.com',
password: 'abc123',
displayName: 'Evan',
role: 'ROLE_USER',
);
expect(result.isRight(), true);
});
});
group('RegisterUseCase — validasi displayName', () {
test('harus gagal jika displayName kosong', () async {
final result = await registerUseCase.call(
email: 'evan@test.com',
password: 'password123',
displayName: '',
role: 'ROLE_USER',
);
expect(result.isLeft(), true);
});
test('harus gagal jika displayName hanya spasi', () async {
final result = await registerUseCase.call(
email: 'evan@test.com',
password: 'password123',
displayName: ' ',
role: 'ROLE_USER',
);
expect(result.isLeft(), true);
});
});
group('RegisterUseCase — validasi role', () {
test('harus gagal jika role bukan ROLE_USER atau ROLE_GUARDIAN', () async {
final result = await registerUseCase.call(
email: 'evan@test.com',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_ADMIN',
);
expect(result.isLeft(), true);
result.fold(
(f) => expect(f, isA<ValidationFailure>()),
(_) => fail('Seharusnya gagal'),
);
});
test('ROLE_USER harus valid', () async {
final fakeUser = UserEntity(
token: 'tok',
role: 'ROLE_USER',
displayName: 'Evan',
uniqueUserId: 'ABC123DEF456',
);
when(mockRepo.register(
email: 'evan@test.com',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
)).thenAnswer((_) async => Right(fakeUser));
final result = await registerUseCase.call(
email: 'evan@test.com',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
);
expect(result.isRight(), true);
});
test('ROLE_GUARDIAN harus valid', () async {
final fakeUser = UserEntity(
token: 'tok',
role: 'ROLE_GUARDIAN',
displayName: 'Bambang',
);
when(mockRepo.register(
email: 'bambang@test.com',
password: 'guardian123',
displayName: 'Bambang',
role: 'ROLE_GUARDIAN',
)).thenAnswer((_) async => Right(fakeUser));
final result = await registerUseCase.call(
email: 'bambang@test.com',
password: 'guardian123',
displayName: 'Bambang',
role: 'ROLE_GUARDIAN',
);
expect(result.isRight(), true);
});
});
group('RegisterUseCase — ROLE_USER mendapat uniqueUserId', () {
test('ROLE_USER harus mendapat uniqueUserId dari backend', () async {
final fakeUser = UserEntity(
token: 'user_jwt',
role: 'ROLE_USER',
displayName: 'Evan',
uniqueUserId: 'ABC123DEF456',
);
when(mockRepo.register(
email: 'evan@test.com',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
)).thenAnswer((_) async => Right(fakeUser));
final result = await registerUseCase.call(
email: 'evan@test.com',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
);
result.fold(
(_) => fail('Seharusnya sukses'),
(user) {
expect(user.uniqueUserId, isNotNull);
expect(user.uniqueUserId!.length, 12);
expect(user.role, 'ROLE_USER');
},
);
});
test('ROLE_GUARDIAN tidak perlu uniqueUserId', () async {
final fakeUser = UserEntity(
token: 'guardian_jwt',
role: 'ROLE_GUARDIAN',
displayName: 'Bambang',
uniqueUserId: null,
);
when(mockRepo.register(
email: 'bambang@test.com',
password: 'guardian123',
displayName: 'Bambang',
role: 'ROLE_GUARDIAN',
)).thenAnswer((_) async => Right(fakeUser));
final result = await registerUseCase.call(
email: 'bambang@test.com',
password: 'guardian123',
displayName: 'Bambang',
role: 'ROLE_GUARDIAN',
);
result.fold(
(_) => fail('Seharusnya sukses'),
(user) => expect(user.uniqueUserId, isNull),
);
});
});
group('RegisterUseCase — error dari server', () {
test('harus propagate ServerFailure dari repository', () async {
when(mockRepo.register(
email: 'taken@test.com',
password: 'password123',
displayName: 'Duplicate',
role: 'ROLE_USER',
)).thenAnswer(
(_) async => const Left(ServerFailure('Email sudah terdaftar')));
final result = await registerUseCase.call(
email: 'taken@test.com',
password: 'password123',
displayName: 'Duplicate',
role: 'ROLE_USER',
);
result.fold(
(f) {
expect(f, isA<ServerFailure>());
expect(f.message, 'Email sudah terdaftar');
},
(_) => fail('Seharusnya gagal'),
);
});
test('tidak boleh call repo jika validasi gagal', () async {
await registerUseCase.call(
email: '',
password: 'password123',
displayName: 'Evan',
role: 'ROLE_USER',
);
verifyNever(mockRepo.register(
email: 'ignored',
password: 'ignored',
displayName: 'ignored',
role: 'ignored',
));
});
});
}