add core/http tests

Signed-off-by: Sahil Kumar <[email protected]>
This commit is contained in:
Sahil Kumar
2021-06-04 16:24:52 +05:30
parent 473851684b
commit 214d1bcf1d
10 changed files with 1177 additions and 4 deletions
@@ -7,7 +7,6 @@ import 'package:stream_chat/src/core/http/token.dart';
import 'package:stream_chat/src/core/http/token_manager.dart';
import 'package:stream_chat/src/core/error/error.dart';
///
class AuthInterceptor extends Interceptor {
///
@@ -84,7 +83,7 @@ class AuthInterceptor extends Interceptor {
);
return handler.resolve(response);
} on DioError catch (error) {
return handler.reject(error);
return handler.next(error);
}
}
return handler.next(err);
@@ -86,8 +86,8 @@ class StreamHttpClient {
/// [StreamHttpClient] instance dequeue the request task。
void unlock() => httpClient.unlock();
///Clear the current [StreamHttpClient] instance waiting queue.
void clear() => httpClient.close();
/// Clear the current [StreamHttpClient] instance waiting queue.
void clear() => httpClient.clear();
/// Shuts down the [StreamHttpClient].
///
@@ -0,0 +1,38 @@
import 'package:stream_chat/src/core/http/connection_id_manager.dart';
import 'package:test/test.dart';
void main() {
late ConnectionIdManager connectionIdManager;
setUp(() {
connectionIdManager = ConnectionIdManager();
});
tearDown(() {
connectionIdManager.reset();
});
test('`setConnectionId` should set connectionId', () {
expect(connectionIdManager.connectionId, isNull);
expect(connectionIdManager.hasConnectionId, isFalse);
const connectionId = 'test-connection-id';
connectionIdManager.setConnectionId(connectionId);
expect(connectionIdManager.connectionId, connectionId);
expect(connectionIdManager.hasConnectionId, isTrue);
});
test('`reset` should clear the connectionId', () {
const connectionId = 'test-connection-id';
connectionIdManager.setConnectionId(connectionId);
expect(connectionIdManager.connectionId, connectionId);
expect(connectionIdManager.hasConnectionId, isTrue);
connectionIdManager.reset();
expect(connectionIdManager.connectionId, isNull);
expect(connectionIdManager.hasConnectionId, isFalse);
});
}
@@ -0,0 +1,270 @@
import 'package:dio/dio.dart';
import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/core/http/interceptor/auth_interceptor.dart';
import 'package:stream_chat/src/core/http/stream_chat_dio_error.dart';
import 'package:stream_chat/src/core/http/stream_http_client.dart';
import 'package:stream_chat/src/core/http/token.dart';
import 'package:stream_chat/src/core/http/token_manager.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
import '../../../mocks.dart';
void main() {
late StreamHttpClient client;
late TokenManager tokenManager;
late AuthInterceptor authInterceptor;
setUp(() {
client = MockHttpClient();
tokenManager = MockTokenManager();
authInterceptor = AuthInterceptor(client, tokenManager);
});
test(
'`onRequest` should add userId, authToken, authType in the request',
() async {
final options = RequestOptions(path: 'test-path');
final handler = RequestInterceptorHandler();
final headers = options.headers;
final queryParams = options.queryParameters;
expect(headers.containsKey('Authorization'), isFalse);
expect(headers.containsKey('stream-auth-type'), isFalse);
expect(queryParams.containsKey('user_id'), isFalse);
final token = Token.development('test-user-id');
when(() => tokenManager.loadToken(refresh: any(named: 'refresh')))
.thenAnswer((_) async => token);
authInterceptor.onRequest(options, handler);
final updatedOptions = (await handler.future).data as RequestOptions;
final updateHeaders = updatedOptions.headers;
final updatedQueryParams = updatedOptions.queryParameters;
expect(updateHeaders.containsKey('Authorization'), isTrue);
expect(updateHeaders['Authorization'], token.rawValue);
expect(updateHeaders.containsKey('stream-auth-type'), isTrue);
expect(updateHeaders['stream-auth-type'], token.authType.raw);
expect(updatedQueryParams.containsKey('user_id'), isTrue);
expect(updatedQueryParams['user_id'], token.userId);
verify(() => tokenManager.loadToken(refresh: any(named: 'refresh')))
.called(1);
verifyNoMoreInteractions(tokenManager);
},
);
test(
'`onRequest` should reject with error if `tokenManager.loadToken` throws',
() async {
final options = RequestOptions(path: 'test-path');
final handler = RequestInterceptorHandler();
authInterceptor.onRequest(options, handler);
try {
await handler.future;
} catch (e) {
// need to cast it as the type is private in dio
var error = (e as dynamic).data;
expect(error, isA<StreamChatDioError>());
error = (error as StreamChatDioError).error;
expect(error.code, ChatErrorCode.undefinedToken.code);
expect(error.message, ChatErrorCode.undefinedToken.message);
}
},
);
test('`onError` should retry the request with refreshed token', () async {
const path = 'test-request-path';
final options = RequestOptions(path: path);
const code = ChatErrorCode.tokenExpired;
final errorResponse = ErrorResponse()
..code = code.code
..message = code.message;
final response = Response(
requestOptions: options,
data: errorResponse.toJson(),
);
final err = DioError(requestOptions: options, response: response);
final handler = ErrorInterceptorHandler();
when(() => tokenManager.isStatic).thenReturn(false);
when(() => client.lock()).thenReturn(() {});
final token = Token.development('test-user-id');
when(() => tokenManager.loadToken(refresh: true))
.thenAnswer((_) async => token);
when(() => client.unlock()).thenReturn(() {});
when(() => client.request(
path,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
options: any(named: 'options'),
)).thenAnswer((_) async => Response(
requestOptions: options,
statusCode: 200,
));
authInterceptor.onError(err, handler);
final res = await handler.future;
var data = res.data;
expect(data, isA<Response>());
data = data as Response;
expect(data, isNotNull);
expect(data.statusCode, 200);
expect(data.requestOptions.path, path);
verify(() => tokenManager.isStatic).called(1);
verify(() => client.lock()).called(1);
verify(() => tokenManager.loadToken(refresh: true)).called(1);
verifyNoMoreInteractions(tokenManager);
verify(() => client.unlock()).called(1);
verify(() => client.request(
path,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(client);
});
test(
'`onError` should reject with error if retried request throws',
() async {
const path = 'test-request-path';
final options = RequestOptions(path: path);
const code = ChatErrorCode.tokenExpired;
final errorResponse = ErrorResponse()
..code = code.code
..message = code.message;
final response = Response(
requestOptions: options,
data: errorResponse.toJson(),
);
final err = DioError(requestOptions: options, response: response);
final handler = ErrorInterceptorHandler();
when(() => tokenManager.isStatic).thenReturn(false);
when(() => client.lock()).thenReturn(() {});
final token = Token.development('test-user-id');
when(() => tokenManager.loadToken(refresh: true))
.thenAnswer((_) async => token);
when(() => client.unlock()).thenReturn(() {});
when(() => client.request(
path,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
options: any(named: 'options'),
)).thenThrow(err);
authInterceptor.onError(err, handler);
try {
await handler.future;
} catch (e) {
// need to cast it as the type is private in dio
final error = (e as dynamic).data;
expect(error, isA<DioError>());
}
verify(() => tokenManager.isStatic).called(1);
verify(() => client.lock()).called(1);
verify(() => tokenManager.loadToken(refresh: true)).called(1);
verifyNoMoreInteractions(tokenManager);
verify(() => client.unlock()).called(1);
verify(() => client.request(
path,
data: options.data,
onReceiveProgress: options.onReceiveProgress,
onSendProgress: options.onSendProgress,
queryParameters: options.queryParameters,
cancelToken: options.cancelToken,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(client);
},
);
test(
'`onError` should reject with error if `tokenManager.isStatic` is true',
() async {
const path = 'test-request-path';
final options = RequestOptions(path: path);
const code = ChatErrorCode.tokenExpired;
final errorResponse = ErrorResponse()
..code = code.code
..message = code.message;
final response = Response(
requestOptions: options,
data: errorResponse.toJson(),
);
final err = DioError(requestOptions: options, response: response);
final handler = ErrorInterceptorHandler();
when(() => tokenManager.isStatic).thenReturn(true);
authInterceptor.onError(err, handler);
try {
await handler.future;
} catch (e) {
// need to cast it as the type is private in dio
final error = (e as dynamic).data;
expect(error, isA<DioError>());
final response = StreamChatNetworkError.fromDioError(error);
expect(response.errorCode, code);
}
verify(() => tokenManager.isStatic).called(1);
verifyNoMoreInteractions(tokenManager);
},
);
test(
'`onError` should reject with error if error is not a `tokenExpired error`',
() async {
const path = 'test-request-path';
final options = RequestOptions(path: path);
final response = Response(requestOptions: options);
final err = DioError(requestOptions: options, response: response);
final handler = ErrorInterceptorHandler();
authInterceptor.onError(err, handler);
try {
await handler.future;
} catch (e) {
// need to cast it as the type is private in dio
final error = (e as dynamic).data;
expect(error, isA<DioError>());
}
},
);
}
@@ -0,0 +1,67 @@
import 'package:dio/dio.dart';
import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/core/http/connection_id_manager.dart';
import 'package:stream_chat/src/core/http/interceptor/connection_id_interceptor.dart';
import 'package:test/test.dart';
import '../../../mocks.dart';
void main() {
late ConnectionIdManager connectionIdManager;
late ConnectionIdInterceptor connectionIdInterceptor;
setUp(() {
connectionIdManager = MockConnectionIdManager();
connectionIdInterceptor = ConnectionIdInterceptor(connectionIdManager);
});
test(
'`onRequest` should add connectionId in the request',
() async {
final options = RequestOptions(path: 'test-path');
final handler = RequestInterceptorHandler();
final queryParams = options.queryParameters;
expect(queryParams.containsKey('connection_id'), isFalse);
const connectionId = 'test-connection-id';
when(() => connectionIdManager.hasConnectionId).thenReturn(true);
when(() => connectionIdManager.connectionId).thenReturn(connectionId);
connectionIdInterceptor.onRequest(options, handler);
final updatedOptions = (await handler.future).data as RequestOptions;
final updatedQueryParams = updatedOptions.queryParameters;
expect(updatedQueryParams.containsKey('connection_id'), isTrue);
expect(updatedQueryParams['connection_id'], connectionId);
verify(() => connectionIdManager.hasConnectionId).called(1);
verify(() => connectionIdManager.connectionId).called(1);
verifyNoMoreInteractions(connectionIdManager);
},
);
test(
'`onRequest` should not add connectionId if `hasConnectionId` is false',
() async {
final options = RequestOptions(path: 'test-path');
final handler = RequestInterceptorHandler();
final queryParams = options.queryParameters;
expect(queryParams.containsKey('connection_id'), isFalse);
when(() => connectionIdManager.hasConnectionId).thenReturn(false);
connectionIdInterceptor.onRequest(options, handler);
final updatedOptions = (await handler.future).data as RequestOptions;
final updatedQueryParams = updatedOptions.queryParameters;
expect(updatedQueryParams.containsKey('connection_id'), isFalse);
verify(() => connectionIdManager.hasConnectionId).called(1);
verifyNoMoreInteractions(connectionIdManager);
},
);
}
@@ -0,0 +1,20 @@
import 'package:dio/dio.dart';
import 'package:stream_chat/src/core/error/error.dart';
import 'package:stream_chat/src/core/http/stream_chat_dio_error.dart';
import 'package:test/test.dart';
void main() {
test('should create a new instance of StreamChatDioError', () {
final error = StreamChatNetworkError(ChatErrorCode.inputError);
final options = RequestOptions(path: 'test-path');
final dioError = StreamChatDioError(
error: error,
requestOptions: options,
);
expect(dioError, isA<DioError>());
expect(dioError, isNotNull);
expect(dioError.error, error);
expect(dioError.requestOptions, options);
});
}
@@ -0,0 +1,53 @@
import 'package:stream_chat/src/core/http/stream_http_client.dart';
import 'package:stream_chat/src/location.dart';
import 'package:test/test.dart';
void main() {
test('should return the all default set params', () {
const options = StreamHttpClientOptions();
expect(options.location, isNull);
expect(options.baseUrl, 'https://chat-us-east-1.stream-io-api.com');
expect(options.connectTimeout, const Duration(seconds: 6));
expect(options.receiveTimeout, const Duration(seconds: 6));
});
test('should override all the default set params', () {
const options = StreamHttpClientOptions(
baseUrl: 'base-url',
connectTimeout: Duration(seconds: 3),
receiveTimeout: Duration(seconds: 3),
);
expect(options.location, isNull);
expect(options.baseUrl, 'base-url');
expect(options.connectTimeout, const Duration(seconds: 3));
expect(options.receiveTimeout, const Duration(seconds: 3));
});
group('should create baseUrl according to provided location', () {
test('us-east', () {
const options = StreamHttpClientOptions(location: Location.usEast);
expect(options.location, isNotNull);
expect(options.baseUrl, 'https://chat-proxy-us-east.stream-io-api.com');
});
test('eu-west', () {
const options = StreamHttpClientOptions(location: Location.euWest);
expect(options.location, isNotNull);
expect(options.baseUrl, 'https://chat-proxy-dublin.stream-io-api.com');
});
test('mumbai', () {
const options = StreamHttpClientOptions(location: Location.mumbai);
expect(options.location, isNotNull);
expect(options.baseUrl, 'https://chat-proxy-mumbai.stream-io-api.com');
});
test('sydney', () {
const options = StreamHttpClientOptions(location: Location.sydney);
expect(options.location, isNotNull);
expect(options.baseUrl, 'https://chat-proxy-sydney.stream-io-api.com');
});
test('singapore', () {
const options = StreamHttpClientOptions(location: Location.singapore);
expect(options.location, isNotNull);
expect(options.baseUrl, 'https://chat-proxy-singapore.stream-io-api.com');
});
});
}
@@ -0,0 +1,528 @@
import 'package:dio/dio.dart';
import 'package:logging/logging.dart';
import 'package:mocktail/mocktail.dart';
import 'package:stream_chat/src/core/api/responses.dart';
import 'package:stream_chat/src/core/error/error.dart';
import 'package:stream_chat/src/core/http/connection_id_manager.dart';
import 'package:stream_chat/src/core/http/interceptor/auth_interceptor.dart';
import 'package:stream_chat/src/core/http/interceptor/connection_id_interceptor.dart';
import 'package:stream_chat/src/core/http/interceptor/logging_interceptor.dart';
import 'package:stream_chat/src/core/http/stream_chat_dio_error.dart';
import 'package:stream_chat/src/core/http/stream_http_client.dart';
import 'package:stream_chat/src/core/http/token_manager.dart';
import 'package:test/test.dart';
import '../../mocks.dart';
void main() {
Response successResponse(String path) => Response(
requestOptions: RequestOptions(path: path),
statusCode: 200,
);
DioError throwableError(
String path, {
StreamChatNetworkError? error,
bool streamChatDioError = false,
}) {
if (streamChatDioError) assert(error != null, '');
final options = RequestOptions(path: path);
final data = ErrorResponse()
..code = error?.code
..statusCode = error?.statusCode
..message = error?.message;
DioError? dioError;
if (streamChatDioError) {
dioError = StreamChatDioError(error: error!, requestOptions: options);
} else {
dioError = DioError(
error: error,
requestOptions: options,
response: Response(
requestOptions: options,
statusCode: data.statusCode,
data: data.toJson(),
),
);
}
return dioError;
}
test('AuthInterceptor should be added if tokenManager is provided', () {
const apiKey = 'api-key';
final client = StreamHttpClient(apiKey, tokenManager: TokenManager());
expect(client.httpClient.interceptors.length, 1);
expect(client.httpClient.interceptors.first, isA<AuthInterceptor>());
});
test(
'connectionIdInterceptor should be added if connectionIdManager is provided',
() {
const apiKey = 'api-key';
final client = StreamHttpClient(
apiKey,
connectionIdManager: ConnectionIdManager(),
);
expect(client.httpClient.interceptors.length, 1);
expect(
client.httpClient.interceptors.first,
isA<ConnectionIdInterceptor>(),
);
},
);
test('loggingInterceptor should be added if logger is provided', () {
const apiKey = 'api-key';
final client = StreamHttpClient(
apiKey,
logger: Logger('test-logger'),
);
expect(client.httpClient.interceptors.length, 1);
expect(
client.httpClient.interceptors.first,
isA<LoggingInterceptor>(),
);
});
test('loggingInterceptor should log requests', () async {
const apiKey = 'api-key';
final logger = MockLogger();
final client = StreamHttpClient(apiKey, logger: logger);
try {
await client.get('path');
} catch (_) {}
verify(() => logger.info(any())).called(16);
});
test('loggingInterceptor should log error', () async {
const apiKey = 'api-key';
final logger = MockLogger();
final client = StreamHttpClient(apiKey, logger: logger);
try {
await client.get('path');
} catch (_) {}
verify(() => logger.severe(any())).called(8);
});
test('`.lock` should lock the dio client', () async {
final client = StreamHttpClient('api-key');
expect(client.httpClient.interceptors.requestLock.locked, isFalse);
client.lock();
expect(client.httpClient.interceptors.requestLock.locked, isTrue);
});
test('`.unlock` should unlock the dio client', () async {
final client = StreamHttpClient('api-key');
expect(client.httpClient.interceptors.requestLock.locked, isFalse);
client.lock();
expect(client.httpClient.interceptors.requestLock.locked, isTrue);
client.unlock();
expect(client.httpClient.interceptors.requestLock.locked, isFalse);
});
test('`.clear` should clear and unlock the dio client', () async {
final client = StreamHttpClient('api-key')..clear();
expect(client.httpClient.interceptors.requestLock.locked, isFalse);
});
test('`.close` should close the dio client', () async {
final client = StreamHttpClient('api-key')..close(force: true);
try {
await client.get('path');
} on StreamChatNetworkError catch (e) {
expect(e, isA<StreamChatNetworkError>());
expect(e.message, "Dio can't establish new connection after closed.");
}
});
test('`.get` should return response successfully', () async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-get-api-path';
when(() => dio.get(
path,
options: any(named: 'options'),
)).thenAnswer((_) async => successResponse(path));
final res = await client.get(path);
expect(res, isNotNull);
expect(res.statusCode, 200);
expect(res.requestOptions.path, path);
verify(() => dio.get(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
});
test('`.get` should throw an instance of `StreamChatNetworkError`', () async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-get-api-path';
final error = throwableError(
path,
error: StreamChatNetworkError(ChatErrorCode.internalSystemError),
);
when(() => dio.get(
path,
options: any(named: 'options'),
)).thenThrow(error);
try {
await client.get(path);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
expect(e, StreamChatNetworkError.fromDioError(error));
}
verify(() => dio.get(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
});
test('`.post` should return response successfully', () async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-post-api-path';
when(() => dio.post(
path,
options: any(named: 'options'),
)).thenAnswer((_) async => successResponse(path));
final res = await client.post(path);
expect(res, isNotNull);
expect(res.statusCode, 200);
expect(res.requestOptions.path, path);
verify(() => dio.post(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
});
test(
'`.post` should throw an instance of `StreamChatNetworkError`',
() async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-post-api-path';
final error = throwableError(
path,
error: StreamChatNetworkError(ChatErrorCode.internalSystemError),
);
when(() => dio.post(
path,
options: any(named: 'options'),
)).thenThrow(error);
try {
await client.post(path);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
expect(e, StreamChatNetworkError.fromDioError(error));
}
verify(() => dio.post(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
},
);
test('`.delete` should return response successfully', () async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-delete-api-path';
when(() => dio.delete(
path,
options: any(named: 'options'),
)).thenAnswer((_) async => successResponse(path));
final res = await client.delete(path);
expect(res, isNotNull);
expect(res.statusCode, 200);
expect(res.requestOptions.path, path);
verify(() => dio.delete(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
});
test(
'`.delete` should throw an instance of `StreamChatNetworkError`',
() async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-delete-api-path';
final error = throwableError(
path,
error: StreamChatNetworkError(ChatErrorCode.internalSystemError),
);
when(() => dio.delete(
path,
options: any(named: 'options'),
)).thenThrow(error);
try {
await client.delete(path);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
expect(e, StreamChatNetworkError.fromDioError(error));
}
verify(() => dio.delete(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
},
);
test('`.patch` should return response successfully', () async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-patch-api-path';
when(() => dio.patch(
path,
options: any(named: 'options'),
)).thenAnswer((_) async => successResponse(path));
final res = await client.patch(path);
expect(res, isNotNull);
expect(res.statusCode, 200);
expect(res.requestOptions.path, path);
verify(() => dio.patch(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
});
test(
'`.patch` should throw an instance of `StreamChatNetworkError`',
() async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-patch-api-path';
final error = throwableError(
path,
error: StreamChatNetworkError(ChatErrorCode.internalSystemError),
);
when(() => dio.patch(
path,
options: any(named: 'options'),
)).thenThrow(error);
try {
await client.patch(path);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
expect(e, StreamChatNetworkError.fromDioError(error));
}
verify(() => dio.patch(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
},
);
test('`.put` should return response successfully', () async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-put-api-path';
when(() => dio.put(
path,
options: any(named: 'options'),
)).thenAnswer((_) async => successResponse(path));
final res = await client.put(path);
expect(res, isNotNull);
expect(res.statusCode, 200);
expect(res.requestOptions.path, path);
verify(() => dio.put(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
});
test(
'`.put` should throw an instance of `StreamChatNetworkError`',
() async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-put-api-path';
final error = throwableError(
path,
error: StreamChatNetworkError(ChatErrorCode.internalSystemError),
);
when(() => dio.put(
path,
options: any(named: 'options'),
)).thenThrow(error);
try {
await client.put(path);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
expect(e, StreamChatNetworkError.fromDioError(error));
}
verify(() => dio.put(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
},
);
test('`.postFile` should return response successfully', () async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-delete-api-path';
final file = MultipartFile.fromBytes([]);
when(() => dio.post(
path,
data: any(named: 'data'),
options: any(named: 'options'),
)).thenAnswer((_) async => successResponse(path));
final res = await client.postFile(path, file);
expect(res, isNotNull);
expect(res.statusCode, 200);
expect(res.requestOptions.path, path);
verify(() => dio.post(
path,
data: any(named: 'data'),
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
});
test(
'`.postFile` should throw an instance of `StreamChatNetworkError`',
() async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-post-file-api-path';
final file = MultipartFile.fromBytes([]);
final error = throwableError(
path,
error: StreamChatNetworkError(ChatErrorCode.internalSystemError),
);
when(() => dio.post(
path,
data: any(named: 'data'),
options: any(named: 'options'),
)).thenThrow(error);
try {
await client.postFile(path, file);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
expect(e, StreamChatNetworkError.fromDioError(error));
}
verify(() => dio.post(
path,
data: any(named: 'data'),
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
},
);
test('`.request` should return response successfully', () async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-request-api-path';
when(() => dio.request(
path,
options: any(named: 'options'),
)).thenAnswer((_) async => successResponse(path));
final res = await client.request(path);
expect(res, isNotNull);
expect(res.statusCode, 200);
expect(res.requestOptions.path, path);
verify(() => dio.request(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
});
test(
'`.request` should throw an instance of `StreamChatNetworkError`',
() async {
final dio = MockDio();
final client = StreamHttpClient('api-key', dio: dio);
const path = 'test-put-api-path';
final error = throwableError(
path,
streamChatDioError: true,
error: StreamChatNetworkError(ChatErrorCode.internalSystemError),
);
when(() => dio.request(
path,
options: any(named: 'options'),
)).thenThrow(error);
try {
await client.request(path);
} catch (e) {
expect(e, isA<StreamChatNetworkError>());
expect(e, error.error);
}
verify(() => dio.request(
path,
options: any(named: 'options'),
)).called(1);
verifyNoMoreInteractions(dio);
},
);
}
@@ -0,0 +1,141 @@
import 'package:stream_chat/src/core/http/token.dart';
import 'package:stream_chat/src/core/http/token_manager.dart';
import 'package:test/test.dart';
void main() {
late TokenManager tokenManager;
setUp(() {
tokenManager = TokenManager();
});
tearDown(() {
tokenManager.reset();
});
test('`setTokenOrProvider` should set token', () async {
expect(tokenManager.userId, isNull);
const userId = 'test-user-id';
final token = Token.development(userId);
final returnedToken = await tokenManager.setTokenOrProvider(
userId,
token: token,
);
expect(returnedToken, token);
expect(tokenManager.userId, userId);
expect(tokenManager.isStatic, isTrue);
});
test('`setTokenOrProvider` should set tokenProvider', () async {
expect(tokenManager.userId, isNull);
const userId = 'test-user-id';
Future<String> tokenProvider(String userId) async =>
Token.development(userId).rawValue;
final returnedToken = await tokenManager.setTokenOrProvider(
userId,
provider: tokenProvider,
);
expect(returnedToken, isNotNull);
expect(tokenManager.userId, userId);
expect(tokenManager.isStatic, isFalse);
});
test(
'`setTokenOrProvider` should throw if both token and provider is not provided',
() async {
expect(tokenManager.userId, isNull);
const userId = 'test-user-id';
try {
await tokenManager.setTokenOrProvider(userId);
} catch (e) {
expect(e, isA<AssertionError>());
}
},
);
test(
'`setTokenOrProvider` should throw if both token and provider is provided',
() async {
expect(tokenManager.userId, isNull);
const userId = 'test-user-id';
final token = Token.development(userId);
Future<String> tokenProvider(String userId) async =>
Token.development(userId).rawValue;
try {
await tokenManager.setTokenOrProvider(
userId,
token: token,
provider: tokenProvider,
);
} catch (e) {
expect(e, isA<AssertionError>());
}
},
);
test(
'`.loadToken` should return token set via `setToken`',
() async {
const userId = 'test-user-id';
final token = Token.development(userId);
await tokenManager.setTokenOrProvider(userId, token: token);
final returnedToken = await tokenManager.loadToken();
expect(returnedToken, token);
},
);
test(
'`.loadToken` should return token set via `setProvider`',
() async {
const userId = 'test-user-id';
final token = Token.development(userId);
Future<String> tokenProvider(String userId) async => token.rawValue;
await tokenManager.setTokenOrProvider(userId, provider: tokenProvider);
final returnedToken = await tokenManager.loadToken();
expect(returnedToken, token);
},
);
test(
'`.loadToken` should return refreshed token set via `setProvider`',
() async {
const userId = 'test-user-id';
final token = Token.development(userId);
final refreshToken = Token.development(userId);
var refresh = false;
Future<String> tokenProvider(String userId) async {
if (refresh) return refreshToken.rawValue;
return token.rawValue;
}
await tokenManager.setTokenOrProvider(userId, provider: tokenProvider);
final returnedToken = await tokenManager.loadToken();
expect(returnedToken, token);
refresh = true;
final returnedRefreshToken = await tokenManager.loadToken(refresh: true);
expect(returnedRefreshToken, refreshToken);
},
);
test('`.reset` should reset the tokenManager', () async {
const userId = 'test-user-id';
final token = Token.development(userId);
await tokenManager.setTokenOrProvider(userId, token: token);
expect(tokenManager.userId, userId);
tokenManager.reset();
expect(tokenManager.userId, isNull);
});
}
@@ -0,0 +1,57 @@
import 'package:stream_chat/src/core/http/token.dart';
import 'package:stream_chat/stream_chat.dart';
import 'package:test/test.dart';
void main() {
test('`.anonymous` should create anonymous token with passed userId', () {
const userId = 'test-user-id';
final token = Token.anonymous(userId: userId);
expect(token, isNotNull);
expect(token.userId, userId);
expect(token.rawValue, isEmpty);
expect(token.authType, AuthType.anonymous);
expect(token.authType.raw, AuthType.anonymous.raw);
});
test('`.fromRawValue` should create token from rawValue', () {
const userId = 'test-user-id';
final devToken = Token.development(userId);
final token = Token.fromRawValue(devToken.rawValue);
expect(token, devToken);
});
test('`.fromRawValue` should throw if does not contain `user_id`', () {
const badToken = 'bad-token-without-a-user-id';
try {
Token.fromRawValue(badToken);
} catch (e) {
expect(e, isA<ArgumentError>());
}
});
test('`.development` should create a dev-token with provided user-id', () {
const userId = 'test-user-id';
final token = Token.development(userId);
expect(token, isNotNull);
expect(token.userId, userId);
expect(token.rawValue, isNotEmpty);
expect(token.authType, AuthType.jwt);
expect(token.authType.raw, AuthType.jwt.raw);
});
test(
'`.guest` should create a guest-token with provided user and provider',
() async {
final user = User(id: 'test-user-id');
Future<String> provider(User user) async =>
Token.development(user.id).rawValue;
final token = await Token.guest(user, provider);
expect(token, isNotNull);
expect(token.userId, user.id);
expect(token.rawValue, isNotEmpty);
expect(token.authType, AuthType.jwt);
expect(token.authType.raw, AuthType.jwt.raw);
},
);
}