Files
sxchnge/frontend/packages/sxchnge_auth/test/auth_test.dart

178 lines
5.6 KiB
Dart

import 'dart:async';
import 'package:flutter_test/flutter_test.dart';
import 'package:sxchnge_auth/sxchnge_auth.dart';
class ControlledService implements LoginService, SignupService {
final calls = <Completer<AuthResult>>[];
LoginRequest? loginInput;
SignupRequest? signupInput;
Future<AuthResult> next() {
final call = Completer<AuthResult>();
calls.add(call);
return call.future;
}
@override
Future<AuthResult> login(LoginRequest request) {
loginInput = request;
return next();
}
@override
Future<AuthResult> signup(SignupRequest request) {
signupInput = request;
return next();
}
}
void main() {
const result = AuthResult(userId: 'demo', isSimulated: true);
for (final signup in [false, true]) {
group(signup ? 'signup' : 'login', () {
late ControlledService service;
late AuthProvider provider;
Future<void> submit() => signup
? (provider as SignupProvider).signup(
name: ' Alex ',
email: ' a@example.com ',
password: ' secret ',
locale: 'ru',
)
: (provider as LoginProvider).login(
email: ' a@example.com ',
password: ' secret ',
locale: 'ru',
);
setUp(() {
service = ControlledService();
provider = signup ? SignupProvider(service) : LoginProvider(service);
});
test(
'loading, duplicate suppression, result and normalized input',
() async {
final states = <AuthStatus>[];
provider.addListener(() => states.add(provider.state.status));
final first = submit();
await submit();
expect(service.calls, hasLength(1));
expect(provider.state.isLoading, isTrue);
if (signup) {
expect(service.signupInput!.name, 'Alex');
expect(service.signupInput!.email, 'a@example.com');
expect(service.signupInput!.password, ' secret ');
expect(service.signupInput!.locale, 'ru');
} else {
expect(service.loginInput!.email, 'a@example.com');
expect(service.loginInput!.password, ' secret ');
expect(service.loginInput!.locale, 'ru');
}
service.calls.single.complete(result);
await first;
expect(states, [AuthStatus.loading, AuthStatus.success]);
expect(provider.state.result, same(result));
provider.dispose();
},
);
test('typed failure and retry clear previous error', () async {
final first = submit();
service.calls.last.completeError(
const AuthException(AuthError.unavailable),
);
await first;
expect(provider.state.error, AuthError.unavailable);
final retry = submit();
expect(provider.state.error, isNull);
service.calls.last.complete(result);
await retry;
expect(provider.state.status, AuthStatus.success);
provider.dispose();
});
test('unknown exceptions are sanitized', () async {
final first = submit();
service.calls.last.completeError(StateError('secret password'));
await first;
expect(provider.state.error, AuthError.unexpected);
expect(provider.state.result, isNull);
provider.dispose();
});
test('reset rejects stale completion during a new operation', () async {
final first = submit();
provider.reset();
expect(provider.state.status, AuthStatus.idle);
final second = submit();
service.calls.first.complete(result);
await first;
expect(provider.state.status, AuthStatus.loading);
service.calls.last.complete(result);
await second;
expect(provider.state.status, AuthStatus.success);
provider.dispose();
});
test('dispose ignores late failures and further submissions', () async {
var notifications = 0;
provider.addListener(() => notifications++);
final pending = submit();
provider.dispose();
service.calls.last.completeError(
const AuthException(AuthError.unavailable),
);
await pending;
await submit();
expect(notifications, 1);
expect(service.calls, hasLength(1));
});
});
}
test('dummy services simulate success and configured errors', () async {
const login = LoginRequest(
email: 'a@example.com',
password: 'secret',
locale: 'en',
);
const signup = SignupRequest(
name: 'Alex',
email: 'a@example.com',
password: 'secret',
locale: 'ru',
);
expect(
(await const DummyLoginService(delay: Duration.zero).login(login))
.isSimulated,
isTrue,
);
final accepted = await const DummySignupService(delay: Duration.zero)
.signup(signup);
expect(accepted.isSimulated, isTrue);
expect(accepted.delivery?.email, 'a@example.com');
expect(accepted.delivery?.isSimulated, isTrue);
await expectLater(
const DummyLoginService(
delay: Duration.zero,
failure: AuthError.invalidCredentials,
).login(login),
throwsA(
isA<AuthException>().having(
(e) => e.code,
'code',
AuthError.invalidCredentials,
),
),
);
await expectLater(
const DummySignupService(
delay: Duration.zero,
failure: AuthError.emailInUse,
).signup(signup),
throwsA(
isA<AuthException>().having(
(e) => e.code,
'code',
AuthError.emailInUse,
),
),
);
});
}