feat: add tests

This commit is contained in:
2026-07-29 01:18:44 +02:00
parent 8a264d0ae6
commit 34d5800c07
8 changed files with 1127 additions and 0 deletions
+150
View File
@@ -0,0 +1,150 @@
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:mocktail/mocktail.dart';
import 'package:kooltab2/services/pin_lock_service.dart';
class MockFlutterSecureStorage extends Mock
implements FlutterSecureStorage {}
String _computeHash(String pin, String salt) {
final bytes = utf8.encode('$salt:$pin');
return sha256.convert(bytes).toString();
}
void main() {
late PinLockService service;
late MockFlutterSecureStorage mockStorage;
setUp(() {
mockStorage = MockFlutterSecureStorage();
service = PinLockService(storage: mockStorage);
});
group('PinLockService', () {
group('hasPin', () {
test('returns false when no hash stored', () async {
when(() => mockStorage.read(key: 'pin_hash'))
.thenAnswer((_) async => null);
final result = await service.hasPin();
expect(result, false);
});
test('returns false when hash is empty', () async {
when(() => mockStorage.read(key: 'pin_hash'))
.thenAnswer((_) async => '');
final result = await service.hasPin();
expect(result, false);
});
test('returns true when hash exists', () async {
when(() => mockStorage.read(key: 'pin_hash'))
.thenAnswer((_) async => 'somehash');
final result = await service.hasPin();
expect(result, true);
});
});
group('setPin', () {
test('stores salt and hash', () async {
when(() => mockStorage.write(key: any(named: 'key'), value: any(named: 'value')))
.thenAnswer((_) async {});
await service.setPin('1234');
verify(() => mockStorage.write(key: 'pin_salt', value: any(named: 'value'))).called(1);
verify(() => mockStorage.write(key: 'pin_hash', value: any(named: 'value'))).called(1);
});
test('generates unique salts for different pins', () async {
when(() => mockStorage.write(key: any(named: 'key'), value: any(named: 'value')))
.thenAnswer((_) async {});
await service.setPin('1234');
final firstSaltCapture = verify(() => mockStorage.write(
key: 'pin_salt',
value: captureAny(named: 'value'),
)).captured.first;
await service.setPin('5678');
final secondSaltCapture = verify(() => mockStorage.write(
key: 'pin_salt',
value: captureAny(named: 'value'),
)).captured.last;
expect(firstSaltCapture, isNot(equals(secondSaltCapture)));
});
});
group('verifyPin', () {
test('returns false when salt is missing', () async {
when(() => mockStorage.read(key: 'pin_salt'))
.thenAnswer((_) async => null);
when(() => mockStorage.read(key: 'pin_hash'))
.thenAnswer((_) async => 'somehash');
final result = await service.verifyPin('1234');
expect(result, false);
});
test('returns false when hash is missing', () async {
when(() => mockStorage.read(key: 'pin_salt'))
.thenAnswer((_) async => 'somesalt');
when(() => mockStorage.read(key: 'pin_hash'))
.thenAnswer((_) async => null);
final result = await service.verifyPin('1234');
expect(result, false);
});
test('returns true for correct pin', () async {
const salt = 'test_salt';
const pin = '1234';
final expectedHash = _computeHash(pin, salt);
when(() => mockStorage.read(key: 'pin_salt'))
.thenAnswer((_) async => salt);
when(() => mockStorage.read(key: 'pin_hash'))
.thenAnswer((_) async => expectedHash);
final result = await service.verifyPin('1234');
expect(result, true);
});
test('returns false for incorrect pin', () async {
const salt = 'test_salt';
final correctHash = _computeHash('1234', salt);
when(() => mockStorage.read(key: 'pin_salt'))
.thenAnswer((_) async => salt);
when(() => mockStorage.read(key: 'pin_hash'))
.thenAnswer((_) async => correctHash);
final result = await service.verifyPin('5678');
expect(result, false);
});
});
group('clearPin', () {
test('deletes both salt and hash', () async {
when(() => mockStorage.delete(key: any(named: 'key')))
.thenAnswer((_) async {});
await service.clearPin();
verify(() => mockStorage.delete(key: 'pin_salt')).called(1);
verify(() => mockStorage.delete(key: 'pin_hash')).called(1);
});
});
});
}