TF-2461 Write unit test for login AuthorizationInterceptors
Signed-off-by: dab246 <tdvu@linagora.com> Signed-off-by: dab246 <tdvu@linagora.com>
This commit is contained in:
@@ -77,7 +77,7 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
case AuthenticationType.none:
|
case AuthenticationType.none:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
log('AuthorizationInterceptors::onRequest(): URL = ${options.uri} | HEADER = ${options.headers} | DATA = ${options.data}');
|
log('AuthorizationInterceptors::onRequest(): URL = ${options.uri} | HEADER = ${options.headers} | DATA = ${options.data} | METHOD = ${options.method}');
|
||||||
super.onRequest(options, handler);
|
super.onRequest(options, handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ class AuthorizationInterceptors extends QueuedInterceptorsWrapper {
|
|||||||
|
|
||||||
@override
|
@override
|
||||||
void onError(DioError err, ErrorInterceptorHandler handler) async {
|
void onError(DioError err, ErrorInterceptorHandler handler) async {
|
||||||
logError('AuthorizationInterceptors::onError(): DIO_ERROR = $err');
|
logError('AuthorizationInterceptors::onError(): DIO_ERROR = $err | METHOD = ${err.requestOptions.method}');
|
||||||
try {
|
try {
|
||||||
if (validateToRefreshToken(responseStatusCode: err.response?.statusCode)) {
|
if (validateToRefreshToken(responseStatusCode: err.response?.statusCode)) {
|
||||||
log('AuthorizationInterceptors::onError:_validateToRefreshToken');
|
log('AuthorizationInterceptors::onError:_validateToRefreshToken');
|
||||||
|
|||||||
+1
-1
@@ -1039,7 +1039,7 @@ packages:
|
|||||||
source: hosted
|
source: hosted
|
||||||
version: "1.1.0"
|
version: "1.1.0"
|
||||||
http_mock_adapter:
|
http_mock_adapter:
|
||||||
dependency: transitive
|
dependency: "direct dev"
|
||||||
description:
|
description:
|
||||||
name: http_mock_adapter
|
name: http_mock_adapter
|
||||||
sha256: "0e7eaa5d77a273af1c2b5ec5066578faaa73039b63ccda5263c200756f24441a"
|
sha256: "0e7eaa5d77a273af1c2b5ec5066578faaa73039b63ccda5263c200756f24441a"
|
||||||
|
|||||||
@@ -249,6 +249,8 @@ dev_dependencies:
|
|||||||
|
|
||||||
json_serializable: 6.6.1
|
json_serializable: 6.6.1
|
||||||
|
|
||||||
|
http_mock_adapter: 0.4.2
|
||||||
|
|
||||||
dependency_overrides:
|
dependency_overrides:
|
||||||
|
|
||||||
pointer_interceptor: 0.9.1
|
pointer_interceptor: 0.9.1
|
||||||
|
|||||||
@@ -0,0 +1,229 @@
|
|||||||
|
|
||||||
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:core/data/constants/constant.dart';
|
||||||
|
import 'package:core/data/network/dio_client.dart';
|
||||||
|
import 'package:dio/dio.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:http_mock_adapter/http_mock_adapter.dart';
|
||||||
|
import 'package:mockito/annotations.dart';
|
||||||
|
import 'package:mockito/mockito.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/data/local/account_cache_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/data/local/token_oidc_cache_manager.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/data/network/authentication_client/authentication_client_base.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/data/network/interceptors/authorization_interceptors.dart';
|
||||||
|
import 'package:tmail_ui_user/features/login/domain/extensions/oidc_configuration_extensions.dart';
|
||||||
|
import 'package:tmail_ui_user/main/utils/ios_sharing_manager.dart';
|
||||||
|
|
||||||
|
import '../../fixtures/account_fixtures.dart';
|
||||||
|
import '../../fixtures/oidc_fixtures.dart';
|
||||||
|
import 'authorization_interceptor_test.mocks.dart';
|
||||||
|
|
||||||
|
@GenerateMocks([
|
||||||
|
AuthenticationClientBase,
|
||||||
|
TokenOidcCacheManager,
|
||||||
|
AccountCacheManager,
|
||||||
|
IOSSharingManager
|
||||||
|
])
|
||||||
|
void main() {
|
||||||
|
late Dio dio;
|
||||||
|
late DioAdapter dioAdapter;
|
||||||
|
late AuthenticationClientBase authenticationClient;
|
||||||
|
late TokenOidcCacheManager tokenOidcCacheManager;
|
||||||
|
late AccountCacheManager accountCacheManager;
|
||||||
|
late IOSSharingManager iosSharingManager;
|
||||||
|
late AuthorizationInterceptors authorizationInterceptors;
|
||||||
|
|
||||||
|
const baseUrl = 'http://domain.com/jmap';
|
||||||
|
const responseStatusCode401 = 401;
|
||||||
|
const responseStatusCode500 = 500;
|
||||||
|
const responseStatusCode200 = 200;
|
||||||
|
|
||||||
|
final dioError401 = DioError(
|
||||||
|
error: {'message': 'Token Expired'},
|
||||||
|
requestOptions: RequestOptions(path: baseUrl, method: 'POST'),
|
||||||
|
response: Response(
|
||||||
|
statusCode: responseStatusCode401,
|
||||||
|
requestOptions: RequestOptions(path: baseUrl)
|
||||||
|
),
|
||||||
|
type: DioErrorType.badResponse,
|
||||||
|
);
|
||||||
|
|
||||||
|
final dataRequestSuccessfully = {'message': 'Request successfully!'};
|
||||||
|
|
||||||
|
setUp(() {
|
||||||
|
final headers = <String, dynamic>{
|
||||||
|
HttpHeaders.acceptHeader: DioClient.jmapHeader,
|
||||||
|
HttpHeaders.contentTypeHeader: Constant.contentTypeHeaderDefault
|
||||||
|
};
|
||||||
|
final baseOption = BaseOptions(headers: headers);
|
||||||
|
dio = Dio(baseOption)
|
||||||
|
..options.baseUrl = baseUrl;
|
||||||
|
|
||||||
|
authenticationClient = MockAuthenticationClientBase();
|
||||||
|
tokenOidcCacheManager = MockTokenOidcCacheManager();
|
||||||
|
accountCacheManager = MockAccountCacheManager();
|
||||||
|
iosSharingManager = MockIOSSharingManager();
|
||||||
|
|
||||||
|
authorizationInterceptors = AuthorizationInterceptors(
|
||||||
|
dio,
|
||||||
|
authenticationClient,
|
||||||
|
tokenOidcCacheManager,
|
||||||
|
accountCacheManager,
|
||||||
|
iosSharingManager);
|
||||||
|
|
||||||
|
dio.interceptors.add(authorizationInterceptors);
|
||||||
|
|
||||||
|
dioAdapter = DioAdapter(dio: dio);
|
||||||
|
});
|
||||||
|
|
||||||
|
group('AuthorizationInterceptor test', () {
|
||||||
|
|
||||||
|
group("validateToRefreshToken method test", () {
|
||||||
|
test('validateToRefreshToken should return true when conditions are met', () async {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
|
newToken: OIDCFixtures.tokenOidcExpiredTime,
|
||||||
|
newConfig: OIDCFixtures.oidcConfiguration);
|
||||||
|
|
||||||
|
final result = authorizationInterceptors.validateToRefreshToken(responseStatusCode: responseStatusCode401);
|
||||||
|
|
||||||
|
expect(result, true);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validateToRefreshToken should return false when condition `responseStatusCode == 500`', () async {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
|
newToken: OIDCFixtures.tokenOidcExpiredTime,
|
||||||
|
newConfig: OIDCFixtures.oidcConfiguration);
|
||||||
|
|
||||||
|
final result = authorizationInterceptors.validateToRefreshToken(responseStatusCode: responseStatusCode500);
|
||||||
|
|
||||||
|
expect(result, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validateToRefreshToken should return false when condition `OidcConfiguration is null`', () async {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
|
newToken: OIDCFixtures.tokenOidcExpiredTime,
|
||||||
|
newConfig: null);
|
||||||
|
|
||||||
|
final result = authorizationInterceptors.validateToRefreshToken(responseStatusCode: responseStatusCode401);
|
||||||
|
|
||||||
|
expect(result, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validateToRefreshToken should return false when condition `token is empty`', () async {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
|
newToken: OIDCFixtures.tokenOidcExpiredTimeAndTokenEmpty,
|
||||||
|
newConfig: OIDCFixtures.oidcConfiguration);
|
||||||
|
|
||||||
|
final result = authorizationInterceptors.validateToRefreshToken(responseStatusCode: responseStatusCode401);
|
||||||
|
|
||||||
|
expect(result, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validateToRefreshToken should return false when condition `refreshToken is empty`', () async {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
|
newToken: OIDCFixtures.tokenOidcExpiredTimeAndRefreshTokenEmpty,
|
||||||
|
newConfig: OIDCFixtures.oidcConfiguration);
|
||||||
|
|
||||||
|
final result = authorizationInterceptors.validateToRefreshToken(responseStatusCode: responseStatusCode401);
|
||||||
|
|
||||||
|
expect(result, false);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('validateToRefreshToken should return false when condition `Time not expired`', () async {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
|
newToken: OIDCFixtures.newTokenOidc,
|
||||||
|
newConfig: OIDCFixtures.oidcConfiguration);
|
||||||
|
|
||||||
|
final result = authorizationInterceptors.validateToRefreshToken(responseStatusCode: responseStatusCode401);
|
||||||
|
|
||||||
|
expect(result, false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
group('QueuedInterceptorsWrapper test', () {
|
||||||
|
test(
|
||||||
|
'WHEN make a request with `tokenOidcExpiredTime`\n'
|
||||||
|
'AND returns error `dioError401`\n'
|
||||||
|
'THEN refresh token successfully received `newTokenOidc`\n'
|
||||||
|
'AND re-execute request with `newTokenOidc`\n'
|
||||||
|
'THEN response data SHOULD return `dataRequestSuccessfully`\n',
|
||||||
|
() async {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
|
newToken: OIDCFixtures.tokenOidcExpiredTime,
|
||||||
|
newConfig: OIDCFixtures.oidcConfiguration);
|
||||||
|
|
||||||
|
dioAdapter.onPost(
|
||||||
|
baseUrl,
|
||||||
|
(server) => server.throws(responseStatusCode401, dioError401),
|
||||||
|
);
|
||||||
|
|
||||||
|
when(authenticationClient.refreshingTokensOIDC(
|
||||||
|
OIDCFixtures.oidcConfiguration.clientId,
|
||||||
|
OIDCFixtures.oidcConfiguration.redirectUrl,
|
||||||
|
OIDCFixtures.oidcConfiguration.discoveryUrl,
|
||||||
|
OIDCFixtures.oidcConfiguration.scopes,
|
||||||
|
OIDCFixtures.tokenOidcExpiredTime.refreshToken
|
||||||
|
)).thenAnswer((_) async {
|
||||||
|
dioAdapter.onPost(
|
||||||
|
baseUrl,
|
||||||
|
(server) => server.reply(responseStatusCode200, dataRequestSuccessfully)
|
||||||
|
);
|
||||||
|
|
||||||
|
return OIDCFixtures.newTokenOidc;
|
||||||
|
});
|
||||||
|
|
||||||
|
when(accountCacheManager.getCurrentAccount()).thenAnswer((_) async => AccountFixtures.aliceAccount);
|
||||||
|
|
||||||
|
final response = await dio.post(baseUrl);
|
||||||
|
|
||||||
|
expect(response.statusCode, responseStatusCode200);
|
||||||
|
expect(response.data, dataRequestSuccessfully);
|
||||||
|
});
|
||||||
|
|
||||||
|
test(
|
||||||
|
'WHEN make a request with `tokenOidcExpiredTime`\n'
|
||||||
|
'AND returns error `dioError401`\n'
|
||||||
|
'THEN refresh token successfully received `newTokenOidc`\n'
|
||||||
|
'AND `newTokenOidc` equals `tokenOidcExpiredTime` \n'
|
||||||
|
'AND re-execute request with `newTokenOidc`\n'
|
||||||
|
'THEN return error SHOULD `dioError401`\n',
|
||||||
|
() async {
|
||||||
|
authorizationInterceptors.setTokenAndAuthorityOidc(
|
||||||
|
newToken: OIDCFixtures.tokenOidcExpiredTime,
|
||||||
|
newConfig: OIDCFixtures.oidcConfiguration);
|
||||||
|
|
||||||
|
dioAdapter.onPost(
|
||||||
|
baseUrl,
|
||||||
|
(server) => server.throws(responseStatusCode401, dioError401)
|
||||||
|
);
|
||||||
|
|
||||||
|
when(authenticationClient.refreshingTokensOIDC(
|
||||||
|
OIDCFixtures.oidcConfiguration.clientId,
|
||||||
|
OIDCFixtures.oidcConfiguration.redirectUrl,
|
||||||
|
OIDCFixtures.oidcConfiguration.discoveryUrl,
|
||||||
|
OIDCFixtures.oidcConfiguration.scopes,
|
||||||
|
OIDCFixtures.tokenOidcExpiredTime.refreshToken
|
||||||
|
)).thenAnswer((_) async {
|
||||||
|
dioAdapter.onPost(
|
||||||
|
baseUrl,
|
||||||
|
(server) => server.throws(responseStatusCode401, dioError401)
|
||||||
|
);
|
||||||
|
return OIDCFixtures.tokenOidcExpiredTime;
|
||||||
|
});
|
||||||
|
|
||||||
|
when(accountCacheManager.getCurrentAccount()).thenAnswer((_) async => AccountFixtures.aliceAccount);
|
||||||
|
|
||||||
|
expect(
|
||||||
|
() async => await dio.post(baseUrl),
|
||||||
|
throwsA(predicate<DioError>((error) => error.response?.statusCode == responseStatusCode401))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() {
|
||||||
|
dioAdapter.close();
|
||||||
|
dio.close();
|
||||||
|
});
|
||||||
|
}
|
||||||
Vendored
+11
@@ -1,6 +1,17 @@
|
|||||||
import 'package:jmap_dart_client/jmap/account_id.dart';
|
import 'package:jmap_dart_client/jmap/account_id.dart';
|
||||||
import 'package:jmap_dart_client/jmap/core/id.dart';
|
import 'package:jmap_dart_client/jmap/core/id.dart';
|
||||||
|
import 'package:jmap_dart_client/jmap/core/user_name.dart';
|
||||||
|
import 'package:model/account/authentication_type.dart';
|
||||||
|
import 'package:model/account/personal_account.dart';
|
||||||
|
|
||||||
class AccountFixtures {
|
class AccountFixtures {
|
||||||
static final aliceAccountId = AccountId(Id('411ce'));
|
static final aliceAccountId = AccountId(Id('411ce'));
|
||||||
|
static final aliceAccount = PersonalAccount(
|
||||||
|
'dab',
|
||||||
|
AuthenticationType.oidc,
|
||||||
|
isSelected: true,
|
||||||
|
accountId: aliceAccountId,
|
||||||
|
apiUrl: 'https://domain.com/jmap',
|
||||||
|
userName: UserName('Alice')
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Vendored
+35
@@ -0,0 +1,35 @@
|
|||||||
|
|
||||||
|
import 'package:model/oidc/oidc_configuration.dart';
|
||||||
|
import 'package:model/oidc/token_id.dart';
|
||||||
|
import 'package:model/oidc/token_oidc.dart';
|
||||||
|
|
||||||
|
class OIDCFixtures {
|
||||||
|
static final tokenOidcExpiredTime = TokenOIDC(
|
||||||
|
'dab123',
|
||||||
|
TokenId('dab123'),
|
||||||
|
'dab456',
|
||||||
|
expiredTime: DateTime.now().subtract(const Duration(days: 1)));
|
||||||
|
|
||||||
|
static final tokenOidcExpiredTimeAndRefreshTokenEmpty = TokenOIDC(
|
||||||
|
'dab123',
|
||||||
|
TokenId('dab123'),
|
||||||
|
'',
|
||||||
|
expiredTime: DateTime.now().subtract(const Duration(days: 1)));
|
||||||
|
|
||||||
|
static final tokenOidcExpiredTimeAndTokenEmpty = TokenOIDC(
|
||||||
|
'',
|
||||||
|
TokenId('dab123'),
|
||||||
|
'dab456',
|
||||||
|
expiredTime: DateTime.now().subtract(const Duration(days: 1)));
|
||||||
|
|
||||||
|
static final newTokenOidc = TokenOIDC(
|
||||||
|
'test123',
|
||||||
|
TokenId('test123'),
|
||||||
|
'test456',
|
||||||
|
expiredTime: DateTime.now().add(const Duration(days: 1)));
|
||||||
|
|
||||||
|
static final oidcConfiguration = OIDCConfiguration(
|
||||||
|
authority: 'https://example.com',
|
||||||
|
clientId: 'client-id',
|
||||||
|
scopes: ['scope1', 'scope2', 'scope3']);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user