124 lines
4.0 KiB
Dart
124 lines
4.0 KiB
Dart
import 'dart:async';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
|
|
import 'package:sxchnge_auth/sxchnge_auth.dart';
|
|
|
|
class PendingService implements VerificationService {
|
|
final pending = Completer<VerificationResult>();
|
|
int calls = 0;
|
|
String? email;
|
|
String? locale;
|
|
@override
|
|
Future<VerificationResult> send({
|
|
required String email,
|
|
required String locale,
|
|
}) {
|
|
calls++;
|
|
this.email = email;
|
|
this.locale = locale;
|
|
return pending.future;
|
|
}
|
|
|
|
@override
|
|
Future<VerificationResult> confirm({required String token}) {
|
|
calls++;
|
|
return pending.future;
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
for (final scenario in {
|
|
'demo-valid': VerificationStatus.verified,
|
|
'demo-expired': VerificationStatus.expired,
|
|
'bad': VerificationStatus.invalid,
|
|
'': VerificationStatus.invalid,
|
|
'demo-internal': VerificationStatus.error,
|
|
}.entries) {
|
|
test('confirmation maps ${scenario.key} to ${scenario.value}', () async {
|
|
final provider = VerificationProvider(
|
|
const DummyVerificationService(delay: Duration.zero),
|
|
);
|
|
addTearDown(provider.dispose);
|
|
await provider.confirm(scenario.key);
|
|
expect(provider.state.status, scenario.value);
|
|
expect(
|
|
provider.state.result?.verifiedEmail,
|
|
scenario.value == VerificationStatus.verified
|
|
? 'alex@example.com'
|
|
: null,
|
|
);
|
|
expect(
|
|
provider.state.isSimulated,
|
|
scenario.value == VerificationStatus.verified,
|
|
);
|
|
});
|
|
}
|
|
test(
|
|
'send trims email, forwards locale and suppresses duplicate requests',
|
|
() async {
|
|
final service = PendingService();
|
|
final provider = VerificationProvider(service);
|
|
addTearDown(provider.dispose);
|
|
final first = provider.send(email: ' alex@example.com ', locale: 'ru');
|
|
expect(provider.state.status, VerificationStatus.sending);
|
|
await provider.send(email: 'other@example.com', locale: 'en');
|
|
expect(service.calls, 1);
|
|
expect(service.email, 'alex@example.com');
|
|
expect(service.locale, 'ru');
|
|
service.pending.complete(const VerificationResult(isSimulated: true));
|
|
await first;
|
|
expect(provider.state.status, VerificationStatus.sent);
|
|
expect(provider.state.isSimulated, isTrue);
|
|
},
|
|
);
|
|
test('reset discards late result', () async {
|
|
final service = PendingService();
|
|
final provider = VerificationProvider(service);
|
|
addTearDown(provider.dispose);
|
|
final operation = provider.confirm('token');
|
|
provider.reset();
|
|
service.pending.complete(const VerificationResult(isSimulated: true));
|
|
await operation;
|
|
expect(provider.state.status, VerificationStatus.idle);
|
|
expect(provider.state.result, isNull);
|
|
});
|
|
test(
|
|
'a later failed confirmation clears the previous verified identity',
|
|
() async {
|
|
final provider = VerificationProvider(
|
|
const DummyVerificationService(delay: Duration.zero),
|
|
);
|
|
addTearDown(provider.dispose);
|
|
await provider.confirm('demo-valid');
|
|
expect(provider.state.result?.verifiedEmail, 'alex@example.com');
|
|
final operation = provider.confirm('bad');
|
|
expect(provider.state.result, isNull);
|
|
await operation;
|
|
expect(provider.state.result, isNull);
|
|
},
|
|
);
|
|
test('dispose discards late exception', () async {
|
|
final service = PendingService();
|
|
final provider = VerificationProvider(service);
|
|
final operation = provider.confirm('token');
|
|
provider.dispose();
|
|
service.pending.completeError(StateError('private error'));
|
|
await operation;
|
|
});
|
|
test(
|
|
'send errors are internal errors even for token exception types',
|
|
() async {
|
|
final provider = VerificationProvider(
|
|
const DummyVerificationService(
|
|
delay: Duration.zero,
|
|
sendFailure: VerificationError.expired,
|
|
),
|
|
);
|
|
addTearDown(provider.dispose);
|
|
await provider.send(email: 'alex@example.com', locale: 'en');
|
|
expect(provider.state.status, VerificationStatus.error);
|
|
},
|
|
);
|
|
}
|