blob: a8fc299c3b8e62b0c7c9ab6bacd293d4218e739b [file] [log] [blame]
library googleapis_beta.common_internal_test;
import 'dart:async';
import 'dart:convert';
import 'package:crypto/crypto.dart' as crypto;
import 'package:googleapis_beta/common/common.dart';
import 'package:googleapis_beta/src/common_internal.dart';
import 'package:http/http.dart' as http;
import 'package:unittest/unittest.dart';
class HttpServerMock extends http.BaseClient {
Function _callback;
bool _expectJson;
void register(Function callback, bool expectJson) {
_callback = callback;
_expectJson = expectJson;
}
Future<http.StreamedResponse> send(http.BaseRequest request) {
if (_expectJson) {
return request.finalize()
.transform(UTF8.decoder)
.join('')
.then((String jsonString) {
if (jsonString.isEmpty) {
return _callback(request, null);
} else {
return _callback(request, JSON.decode(jsonString));
}
});
} else {
var stream = request.finalize();
if (stream == null) {
return _callback(request, []);
} else {
return stream.toBytes().then((data) {
return _callback(request, data);
});
}
}
}
}
http.StreamedResponse stringResponse(int status, Map headers, String body) {
var stream = new Stream.fromIterable([UTF8.encode(body)]);
return new http.StreamedResponse(stream, status, headers: headers);
}
http.StreamedResponse binaryResponse(int status,
Map<String,String> headers,
List<int> bytes) {
var stream = new Stream.fromIterable([bytes]);
return new http.StreamedResponse(stream, status, headers: headers);
}
Stream<List<int>> byteStream(String s) {
var bodyController = new StreamController();
bodyController.add(UTF8.encode(s));
bodyController.close();
return bodyController.stream;
}
class _ApiRequestError extends TypeMatcher {
const _ApiRequestError() : super("ApiRequestError");
bool matches(item, Map matchState) => item is ApiRequestError;
}
class _DetailedApiRequestError extends TypeMatcher {
const _DetailedApiRequestError() : super("DetailedApiRequestError");
bool matches(item, Map matchState) => item is DetailedApiRequestError;
}
class TestError {}
class _TestError extends TypeMatcher {
const _TestError() : super("TestError");
bool matches(item, Map matchState) => item is TestError;
}
const isApiRequestError = const _ApiRequestError();
const isDetailedApiRequestError = const _DetailedApiRequestError();
const isTestError = const _TestError();
main() {
group('common-external', () {
test('escaper', () {
expect(Escaper.ecapePathComponent('a/b%c '), equals('a%2Fb%25c%20'));
expect(Escaper.ecapeVariable('a/b%c '), equals('a%2Fb%25c%20'));
expect(Escaper.ecapeVariableReserved('a/b%c+ '), equals('a/b%25c+%20'));
expect(Escaper.escapeQueryComponent('a/b%c '), equals('a%2Fb%25c%20'));
});
test('mapMap', () {
newTestMap() => {
's' : 'string',
'i' : 42,
};
var copy = mapMap(newTestMap());
expect(copy, hasLength(2));
expect(copy['s'], equals('string'));
expect(copy['i'], equals(42));
var mod = mapMap(newTestMap(), (x) => '$x foobar');
expect(mod, hasLength(2));
expect(mod['s'], equals('string foobar'));
expect(mod['i'], equals('42 foobar'));
});
test('base64-encoder', () {
var base64encoder = new Base64Encoder();
testString(String msg, String expectedBase64) {
var msgBytes = UTF8.encode(msg);
Stream singleByteStream(List<int> msgBytes) {
var controller = new StreamController();
for (var byte in msgBytes) {
controller.add([byte]);
}
controller.close();
return controller.stream;
}
Stream allByteStream(List<int> msgBytes) {
var controller = new StreamController();
controller.add(msgBytes);
controller.close();
return controller.stream;
}
singleByteStream(msgBytes)
.transform(base64encoder)
.join('')
.then(expectAsync((String result) {
expect(result, equals(expectedBase64));
}));
allByteStream(msgBytes)
.transform(base64encoder)
.join('')
.then(expectAsync((String result) {
expect(result, equals(expectedBase64));
}));
expect(Base64Encoder.lengthOfBase64Stream(msg.length),
equals(expectedBase64.length));
}
testString('pleasure.', 'cGxlYXN1cmUu');
testString('leasure.', 'bGVhc3VyZS4=');
testString('easure.', 'ZWFzdXJlLg==');
testString('asure.', 'YXN1cmUu');
testString('sure.', 'c3VyZS4=');
testString('', '');
});
group('chunk-stack', () {
var chunkSize = 9;
folded(List<List<int>> byteArrays) {
return byteArrays.fold([], (buf, e) => buf..addAll(e));
}
test('finalize', () {
var chunkStack = new ChunkStack(9);
chunkStack.finalize();
expect(() => chunkStack.addBytes([1]), throwsA(isStateError));
expect(() => chunkStack.finalize(), throwsA(isStateError));
});
test('empty', () {
var chunkStack = new ChunkStack(9);
expect(chunkStack.length, equals(0));
chunkStack.finalize();
expect(chunkStack.length, equals(0));
});
test('sub-chunk-size', () {
var bytes = [1, 2, 3];
var chunkStack = new ChunkStack(9);
chunkStack.addBytes(bytes);
expect(chunkStack.length, equals(0));
chunkStack.finalize();
expect(chunkStack.length, equals(1));
expect(chunkStack.totalByteLength, equals(bytes.length));
var chunks = chunkStack.removeSublist(0, chunkStack.length);
expect(chunkStack.length, equals(0));
expect(chunks, hasLength(1));
expect(folded(chunks.first.byteArrays), equals(bytes));
expect(chunks.first.offset, equals(0));
expect(chunks.first.length, equals(3));
expect(chunks.first.endOfChunk, equals(bytes.length));
});
test('exact-chunk-size', () {
var bytes = [1, 2, 3, 4, 5, 6, 7, 8, 9];
var chunkStack = new ChunkStack(9);
chunkStack.addBytes(bytes);
expect(chunkStack.length, equals(1));
chunkStack.finalize();
expect(chunkStack.length, equals(1));
expect(chunkStack.totalByteLength, equals(bytes.length));
var chunks = chunkStack.removeSublist(0, chunkStack.length);
expect(chunkStack.length, equals(0));
expect(chunks, hasLength(1));
expect(folded(chunks.first.byteArrays), equals(bytes));
expect(chunks.first.offset, equals(0));
expect(chunks.first.length, equals(bytes.length));
expect(chunks.first.endOfChunk, equals(bytes.length));
});
test('super-chunk-size', () {
var bytes0 = [1, 2, 3, 4];
var bytes1 = [1, 2, 3, 4];
var bytes2 = [5, 6, 7, 8, 9, 10, 11];
var bytes = folded([bytes0, bytes1, bytes2]);
var chunkStack = new ChunkStack(9);
chunkStack.addBytes(bytes0);
chunkStack.addBytes(bytes1);
chunkStack.addBytes(bytes2);
expect(chunkStack.length, equals(1));
chunkStack.finalize();
expect(chunkStack.length, equals(2));
expect(chunkStack.totalByteLength, equals(bytes.length));
var chunks = chunkStack.removeSublist(0, chunkStack.length);
expect(chunkStack.length, equals(0));
expect(chunks, hasLength(2));
expect(folded(chunks.first.byteArrays),
equals(bytes.sublist(0, chunkSize)));
expect(chunks.first.offset, equals(0));
expect(chunks.first.length, equals(chunkSize));
expect(chunks.first.endOfChunk, equals(chunkSize));
expect(folded(chunks.last.byteArrays),
equals(bytes.sublist(chunkSize)));
expect(chunks.last.offset, equals(chunkSize));
expect(chunks.last.length, equals(bytes.length - chunkSize));
expect(chunks.last.endOfChunk, equals(bytes.length));
});
});
test('media', () {
// Tests for [MediaRange]
var partialRange = new ByteRange(1, 100);
expect(partialRange.start, equals(1));
expect(partialRange.end, equals(100));
var fullRange = new ByteRange(0, -1);
expect(fullRange.start, equals(0));
expect(fullRange.end, equals(-1));
expect(() => new ByteRange(0, 0), throws);
expect(() => new ByteRange(-1, 0), throws);
expect(() => new ByteRange(-1, 1), throws);
// Tests for [DownloadOptions]
expect(DownloadOptions.Metadata.isMetadataDownload, isTrue);
expect(DownloadOptions.FullMedia.isFullDownload, isTrue);
expect(DownloadOptions.FullMedia.isMetadataDownload, isFalse);
// Tests for [Media]
var stream = new StreamController().stream;
expect(() => new Media(null, 0, contentType: 'foobar'),
throwsA(isArgumentError));
expect(() => new Media(stream, 0, contentType: null),
throwsA(isArgumentError));
expect(() => new Media(stream, -1, contentType: 'foobar'),
throwsA(isArgumentError));
var lengthUnknownMedia = new Media(stream, null);
expect(lengthUnknownMedia.stream, equals(stream));
expect(lengthUnknownMedia.length, equals(null));
var media = new Media(stream, 10, contentType: 'foobar');
expect(media.stream, equals(stream));
expect(media.length, equals(10));
expect(media.contentType, equals('foobar'));
// Tests for [ResumableUploadOptions]
expect(() => new ResumableUploadOptions(numberOfAttempts: 0),
throwsA(isArgumentError));
expect(() => new ResumableUploadOptions(chunkSize: 1),
throwsA(isArgumentError));
});
group('api-requester', () {
var httpMock, rootUrl, basePath;
ApiRequester requester;
var responseHeaders = {
'content-type' : 'application/json; charset=utf-8',
};
setUp(() {
httpMock = new HttpServerMock();
rootUrl = 'http://example.com/';
basePath = '/base/';
requester = new ApiRequester(httpMock, rootUrl, basePath);
});
// Tests for Request, Response
group('metadata-request-response', () {
test('empty-request-empty-response', () {
httpMock.register(expectAsync((http.BaseRequest request, json) {
expect(request.method, equals('GET'));
expect('${request.url}',
equals('http://example.com/base/abc?alt=json'));
return stringResponse(200, responseHeaders, '');
}), true);
requester.request('abc', 'GET').then(expectAsync((response) {
expect(response, isNull);
}));
});
test('json-map-request-json-map-response', () {
httpMock.register(expectAsync((http.BaseRequest request, json) {
expect(request.method, equals('GET'));
expect('${request.url}',
equals('http://example.com/base/abc?alt=json'));
expect(json is Map, isTrue);
expect(json, hasLength(1));
expect(json['foo'], equals('bar'));
return stringResponse(200, responseHeaders, '{"foo2" : "bar2"}');
}), true);
requester.request('abc',
'GET',
body: JSON.encode({'foo' : 'bar'})).then(
expectAsync((response) {
expect(response is Map, isTrue);
expect(response, hasLength(1));
expect(response['foo2'], equals('bar2'));
}));
});
test('json-list-request-json-list-response', () {
httpMock.register(expectAsync((http.BaseRequest request, json) {
expect(request.method, equals('GET'));
expect('${request.url}',
equals('http://example.com/base/abc?alt=json'));
expect(json is List, isTrue);
expect(json, hasLength(2));
expect(json[0], equals('a'));
expect(json[1], equals(1));
return stringResponse(200, responseHeaders, '["b", 2]');
}), true);
requester.request('abc',
'GET',
body: JSON.encode(['a', 1])).then(
expectAsync((response) {
expect(response is List, isTrue);
expect(response[0], equals('b'));
expect(response[1], equals(2));
}));
});
});
group('media-download', () {
test('media-download', () {
var data256 = new List.generate(256, (i) => i);
httpMock.register(expectAsync((http.BaseRequest request, data) {
expect(request.method, equals('GET'));
expect('${request.url}',
equals('http://example.com/base/abc?alt=media'));
expect(data, isEmpty);
var headers = {
'content-length' : '${data256.length}',
'content-type' : 'foobar',
};
return binaryResponse(200, headers, data256);
}), false);
requester.request('abc',
'GET',
body: '',
downloadOptions: DownloadOptions.FullMedia).then(
expectAsync((Media media) {
expect(media.contentType, equals('foobar'));
expect(media.length, equals(data256.length));
media.stream.fold([], (b, d) => b..addAll(d)).then(expectAsync((d) {
expect(d, equals(data256));
}));
}));
});
test('media-download-partial', () {
var data256 = new List.generate(256, (i) => i);
var data64 = data256.sublist(128, 128 + 64);
httpMock.register(expectAsync((http.BaseRequest request, data) {
expect(request.method, equals('GET'));
expect('${request.url}',
equals('http://example.com/base/abc?alt=media'));
expect(data, isEmpty);
expect(request.headers['range'],
equals('bytes=128-191'));
var headers = {
'content-length' : '${data64.length}',
'content-type' : 'foobar',
'content-range' : 'bytes 128-191/256',
};
return binaryResponse(200, headers, data64);
}), false);
var range = new ByteRange(128, 128 + 64 - 1);
var options = new PartialDownloadOptions(range);
requester.request('abc',
'GET',
body: '',
downloadOptions: options).then(
expectAsync((Media media) {
expect(media.contentType, equals('foobar'));
expect(media.length, equals(data64.length));
media.stream.fold([], (b, d) => b..addAll(d)).then(expectAsync((d) {
expect(d, equals(data64));
}));
}));
});
test('json-upload-media-download', () {
var data256 = new List.generate(256, (i) => i);
httpMock.register(expectAsync((http.BaseRequest request, json) {
expect(request.method, equals('GET'));
expect('${request.url}',
equals('http://example.com/base/abc?alt=media'));
expect(json is List, isTrue);
expect(json, hasLength(2));
expect(json[0], equals('a'));
expect(json[1], equals(1));
var headers = {
'content-length' : '${data256.length}',
'content-type' : 'foobar',
};
return binaryResponse(200, headers, data256);
}), true);
requester.request('abc',
'GET',
body: JSON.encode(['a', 1]),
downloadOptions: DownloadOptions.FullMedia).then(
expectAsync((Media media) {
expect(media.contentType, equals('foobar'));
expect(media.length, equals(data256.length));
media.stream.fold([], (b, d) => b..addAll(d)).then(expectAsync((d) {
expect(d, equals(data256));
}));
}));
});
});
// Tests for media uploads
group('media-upload', () {
Stream streamFromByteArrays(byteArrays) {
var controller = new StreamController();
for (var array in byteArrays) {
controller.add(array);
}
controller.close();
return controller.stream;
}
Media mediaFromByteArrays(byteArrays, {bool withLen: true}) {
int len = 0;
byteArrays.forEach((array) { len += array.length; });
if (!withLen) len = null;
return new Media(streamFromByteArrays(byteArrays),
len,
contentType: 'foobar');
}
validateServerRequest(e, http.BaseRequest request, List<int> data) {
return new Future.sync(() {
var h = e['headers'];
var r = e['response'];
expect(request.url.toString(), equals(e['url']));
expect(request.method, equals(e['method']));
h.forEach((k, v) {
expect(request.headers[k], equals(v));
});
expect(data, equals(e['data']));
return r;
});
}
serverRequestValidator(List expectations) {
int i = 0;
return (http.BaseRequest request, List<int> data) {
return validateServerRequest(expectations[i++], request, data);
};
}
test('simple', () {
var bytes = new List.generate(10 * 256 * 1024 + 1, (i) => i % 256);
var expectations = [
{
'url' : 'http://example.com/xyz?uploadType=media&alt=json',
'method' : 'POST',
'data' : bytes,
'headers' : {
'content-length' : '${bytes.length}',
'content-type' : 'foobar',
},
'response' : stringResponse(200, responseHeaders, '')
},
];
httpMock.register(
expectAsync(serverRequestValidator(expectations)), false);
var media = mediaFromByteArrays([bytes]);
requester.request('/xyz',
'POST',
uploadMedia: media).then(
expectAsync((response) {}));
});
test('multipart-upload', () {
var bytes = new List.generate(10 * 256 * 1024 + 1, (i) => i % 256);
var contentBytes =
'--314159265358979323846\r\n'
'Content-Type: $CONTENT_TYPE_JSON_UTF8\r\n\r\n'
'BODY'
'\r\n--314159265358979323846\r\n'
'Content-Type: foobar\r\n'
'Content-Transfer-Encoding: base64\r\n\r\n'
'${crypto.CryptoUtils.bytesToBase64(bytes)}'
'\r\n--314159265358979323846--';
var expectations = [
{
'url' : 'http://example.com/xyz?uploadType=multipart&alt=json',
'method' : 'POST',
'data' : UTF8.encode('$contentBytes'),
'headers' : {
'content-length' : '${contentBytes.length}',
'content-type' :
'multipart/related; boundary="314159265358979323846"',
},
'response' : stringResponse(200, responseHeaders, '')
},
];
httpMock.register(
expectAsync(serverRequestValidator(expectations)), false);
var media = mediaFromByteArrays([bytes]);
requester.request('/xyz',
'POST',
body: 'BODY',
uploadMedia: media).then(
expectAsync((response) {}));
});
group('resumable-upload', () {
// TODO: respect [stream]
buildExpectations(List<int> bytes, int chunkSize, bool stream,
{int numberOfServerErrors: 0}) {
int totalLength = bytes.length;
int numberOfChunks = totalLength ~/ chunkSize;
int numberOfBytesInLastChunk = totalLength % chunkSize;
if (numberOfBytesInLastChunk > 0) {
numberOfChunks++;
} else {
numberOfBytesInLastChunk = chunkSize;
}
var expectations = [];
// First request is making a POST and gets the upload URL.
expectations.add({
'url' : 'http://example.com/xyz?uploadType=resumable&alt=json',
'method' : 'POST',
'data' : [],
'headers' : {
'content-length' : '0',
'content-type' : 'application/json; charset=utf-8',
'x-upload-content-type' : 'foobar',
}..addAll(stream ? {} : {
'x-upload-content-length' : '$totalLength',
}),
'response' : stringResponse(
200, {'location' : 'http://upload.com/'}, '')
});
var lastEnd = 0;
for (int i = 0; i < numberOfChunks; i++) {
bool isLast = i == (numberOfChunks - 1);
var lengthMarker = stream && !isLast ? '*' : '$totalLength';
int bytesToExpect = chunkSize;
if (isLast) {
bytesToExpect = numberOfBytesInLastChunk;
}
var start = i * chunkSize;
var end = start + bytesToExpect;
var sublist = bytes.sublist(start, end);
var firstContentRange =
'bytes $start-${end-1}/$lengthMarker';
var firstRange =
'bytes=0-${end-1}';
// We issue [numberOfServerErrors] 503 errors first, and then a
// successfull response.
for (var j = 0; j < (numberOfServerErrors + 1); j++) {
bool successfullResponse = j == numberOfServerErrors;
var response;
if (successfullResponse) {
var headers = isLast
? { 'content-type' : 'application/json; charset=utf-8' }
: {'range' : firstRange };
response = stringResponse(isLast ? 200 : 308, headers, '');
} else {
var headers = {};
response = stringResponse(503, headers, '');
}
expectations.add({
'url' : 'http://upload.com/',
'method' : 'PUT',
'data' : sublist,
'headers' : {
'content-length' : '${sublist.length}',
'content-range' : firstContentRange,
'content-type' : 'foobar',
},
'response' : response,
});
}
}
return expectations;
}
List<List<int>> makeParts(List<int> bytes, List<int> splits) {
var parts = [];
int lastEnd = 0;
for (int i = 0; i < splits.length; i++) {
parts.add(bytes.sublist(lastEnd, splits[i]));
lastEnd = splits[i];
}
return parts;
}
runTest(int chunkSizeInBlocks, int length, List splits, bool stream,
{int numberOfServerErrors: 0, resumableOptions,
int expectedErrorStatus, int messagesNrOfFailure}) {
int chunkSize = chunkSizeInBlocks * 256 * 1024;
int i = 0;
var bytes = new List.generate(length, (i) => i % 256);
var parts = makeParts(bytes, splits);
// Simulation of our server
var expectations = buildExpectations(
bytes, chunkSize, false,
numberOfServerErrors: numberOfServerErrors);
// If the server simulates 50X errors and the client resumes only
// a limited amount of time, we'll trunkate the number of requests
// the server expects.
// [The client will give up and if the server expects more, the test
// would timeout.]
if (expectedErrorStatus != null) {
expectations = expectations.sublist(0, messagesNrOfFailure);
}
httpMock.register(
expectAsync(serverRequestValidator(expectations),
count: expectations.length),
false);
// Our client
var media = mediaFromByteArrays(parts);
if (resumableOptions == null) {
resumableOptions =
new ResumableUploadOptions(chunkSize: chunkSize);
}
var result = requester.request('/xyz',
'POST',
uploadMedia: media,
uploadOptions: resumableOptions);
if (expectedErrorStatus != null) {
result.catchError(expectAsync((error) {
expect(error is DetailedApiRequestError, isTrue);
expect(error.status, equals(expectedErrorStatus));
}));
} else {
result.then(expectAsync((_) {}));
}
}
Function backoffWrapper(int callCount) {
return expectAsync((int failedAttempts) {
var exp = ResumableUploadOptions.ExponentialBackoff;
Duration duration = exp(failedAttempts);
expect(duration.inSeconds, equals(1 << (failedAttempts - 1)));
return const Duration(milliseconds: 1);
}, count: callCount);
}
test('length-small-block', () {
runTest(1, 10, [10], false);
});
test('length-small-block-parts', () {
runTest(1, 20, [1, 2, 3, 4, 5, 6, 7, 19, 20], false);
});
test('length-big-block', () {
runTest(1, 1024 * 1024, [1024*1024], false);
});
test('length-big-block-parts', () {
runTest(1, 1024 * 1024,
[1,
256*1024-1,
256*1024,
256*1024+1,
1024*1024-1,
1024*1024], false);
});
test('stream-small-block', () {
runTest(1, 10, [10], true);
});
test('stream-small-block-parts', () {
runTest(1, 20, [1, 2, 3, 4, 5, 6, 7, 19, 20], true);
});
test('stream-big-block', () {
runTest(1, 1024 * 1024, [1024*1024], true);
});
test('stream-big-block-parts', () {
runTest(1, 1024 * 1024,
[1,
256*1024-1,
256*1024,
256*1024+1,
1024*1024-1,
1024*1024], true);
});
test('stream-big-block-parts--with-server-error-recovery', () {
var numFailedAttempts = 4 * 3;
var options = new ResumableUploadOptions(
chunkSize: 256 * 1024, numberOfAttempts: 4,
backoffFunction: backoffWrapper(numFailedAttempts));
runTest(1, 1024 * 1024,
[1,
256*1024-1,
256*1024,
256*1024+1,
1024*1024-1,
1024*1024],
true,
numberOfServerErrors: 3,
resumableOptions: options);
});
test('stream-big-block-parts--server-error', () {
var numFailedAttempts = 2;
var options = new ResumableUploadOptions(
chunkSize: 256 * 1024, numberOfAttempts: 3,
backoffFunction: backoffWrapper(numFailedAttempts));
runTest(1, 1024 * 1024,
[1,
256*1024-1,
256*1024,
256*1024+1,
1024*1024-1,
1024*1024],
true,
numberOfServerErrors: 3,
resumableOptions: options,
expectedErrorStatus: 503,
messagesNrOfFailure: 4);
});
});
});
// Tests for error responses
group('request-errors', () {
makeTestError() {
// All errors from the [http.Client] propagate through.
// We use [TestError] to simulate it.
httpMock.register(expectAsync((http.BaseRequest request, string) {
return new Future.error(new TestError());
}), false);
}
makeDetailed400Error() {
httpMock.register(expectAsync((http.BaseRequest request, string) {
return stringResponse(400,
responseHeaders,
'{"error" : {"code" : 42, "message": "foo"}}');
}), false);
}
makeNormal199Error() {
httpMock.register(expectAsync((http.BaseRequest request, string) {
return stringResponse(199, {}, '');
}), false);
}
makeInvalidContentTypeError() {
httpMock.register(expectAsync((http.BaseRequest request, string) {
var responseHeaders = { 'content-type' : 'image/png'};
return stringResponse(200, responseHeaders, '');
}), false);
}
test('normal-http-client', () {
makeTestError();
expect(requester.request('abc', 'GET'), throwsA(isTestError));
});
test('normal-detailed-400', () {
makeDetailed400Error();
requester.request('abc', 'GET')
.catchError(expectAsync((error, stack) {
expect(error, isDetailedApiRequestError);
DetailedApiRequestError e = error;
expect(e.status, equals(42));
expect(e.message, equals('foo'));
}));
});
test('normal-199', () {
makeNormal199Error();
expect(requester.request('abc', 'GET'), throwsA(isApiRequestError));
});
test('normal-invalid-content-type', () {
makeInvalidContentTypeError();
expect(requester.request('abc', 'GET'), throwsA(isApiRequestError));
});
var options = DownloadOptions.FullMedia;
test('media-http-client', () {
makeTestError();
expect(requester.request('abc', 'GET', downloadOptions: options),
throwsA(isTestError));
});
test('media-detailed-400', () {
makeDetailed400Error();
requester.request('abc', 'GET')
.catchError(expectAsync((error, stack) {
expect(error, isDetailedApiRequestError);
DetailedApiRequestError e = error;
expect(e.status, equals(42));
expect(e.message, equals('foo'));
}));
});
test('media-199', () {
makeNormal199Error();
expect(requester.request('abc', 'GET', downloadOptions: options),
throwsA(isApiRequestError));
});
});
// Tests for path/query parameters
test('request-parameters-query', () {
var queryParams = {
'a' : ['a1', 'a2'],
's' : ['s1']
};
httpMock.register(expectAsync((http.BaseRequest request, json) {
expect(request.method, equals('GET'));
expect('${request.url}',
equals('http://example.com/base/abc?a=a1&a=a2&s=s1&alt=json'));
return stringResponse(200, responseHeaders, '');
}), true);
requester.request('abc', 'GET', queryParams: queryParams)
.then(expectAsync((response) {
expect(response, isNull);
}));
});
test('request-parameters-path', () {
httpMock.register(expectAsync((http.BaseRequest request, json) {
expect(request.method, equals('GET'));
expect('${request.url}', equals(
'http://example.com/base/s/foo/a1/a2/bar/s1/e?alt=json'));
return stringResponse(200, responseHeaders, '');
}), true);
requester.request('s/foo/a1/a2/bar/s1/e', 'GET')
.then(expectAsync((response) {
expect(response, isNull);
}));
});
});
});
}