| Index: pkg/appengine/test/remote_api_test.dart
|
| diff --git a/pkg/appengine/test/remote_api_test.dart b/pkg/appengine/test/remote_api_test.dart
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..a88d39529acc585c1791518e233e0e30adeb4b25
|
| --- /dev/null
|
| +++ b/pkg/appengine/test/remote_api_test.dart
|
| @@ -0,0 +1,236 @@
|
| +// Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file
|
| +// for details. All rights reserved. Use of this source code is governed by a
|
| +// BSD-style license that can be found in the LICENSE file.
|
| +
|
| +import 'dart:async';
|
| +import 'dart:io';
|
| +import 'dart:convert';
|
| +
|
| +import 'package:unittest/unittest.dart';
|
| +
|
| +import 'package:appengine/api/remote_api.dart';
|
| +import 'package:appengine/api/errors.dart';
|
| +import 'package:appengine/src/protobuf_api/rpc/rpc_service.dart';
|
| +import 'package:appengine/src/appengine_context.dart';
|
| +import 'package:appengine/src/api_impl/remote_api_impl.dart';
|
| +import 'package:appengine/src/protobuf_api/internal/remote_api.pb.dart' as pb;
|
| +
|
| +import 'utils/error_matchers.dart';
|
| +
|
| +class MockRPCService extends RPCService {
|
| + final pb.Request _expectedPbRequest;
|
| + final List<int> _responseBytes;
|
| + final String _expectedTicket;
|
| +
|
| + MockRPCService(
|
| + this._expectedPbRequest, this._responseBytes, this._expectedTicket);
|
| +
|
| + Future<List<int>> call(String apiPackage,
|
| + String method,
|
| + List<int> requestProtocolBuffer,
|
| + {String ticket}) {
|
| + expect(apiPackage, equals(_expectedPbRequest.serviceName));
|
| + expect(method, equals(_expectedPbRequest.method));
|
| + expect(requestProtocolBuffer, equals(_expectedPbRequest.request));
|
| + expect(ticket, equals(_expectedTicket));
|
| + return new Future.value(_responseBytes);
|
| + }
|
| +}
|
| +
|
| +class ErrorMockRPCService extends RPCService {
|
| + static const RPC_ERROR_CODE = 42;
|
| + static const RPC_ERROR_MESSAGE = "rpc error from mock";
|
| +
|
| + final bool throwNetworkError;
|
| + final bool throwProtocolError;
|
| + final bool throwRpcError;
|
| +
|
| + ErrorMockRPCService({this.throwNetworkError: false,
|
| + this.throwProtocolError: false,
|
| + this.throwRpcError: false});
|
| +
|
| + Future<List<int>> call(String apiPackage,
|
| + String method,
|
| + List<int> requestProtocolBuffer,
|
| + {String ticket}) {
|
| + return new Future.sync(() {
|
| + if (throwNetworkError) {
|
| + throw new NetworkError("network error from mock");
|
| + } else if (throwProtocolError) {
|
| + throw new ProtocolError("protocol error from mock");
|
| + } else if (throwRpcError) {
|
| + throw new RpcApplicationError(RPC_ERROR_CODE, RPC_ERROR_MESSAGE);
|
| + }
|
| + throw "Unknown error from mock";
|
| + });
|
| + }
|
| +}
|
| +
|
| +Future<List<Future>> testRemoteApi(RemoteApi remoteApi, Function client) {
|
| + return HttpServer.bind('127.0.0.1', 0).then((HttpServer server) {
|
| + var serverFuture = server.first.then((HttpRequest request) {
|
| + return remoteApi.handleRemoteApiRequest(request);
|
| + }).whenComplete(() => server.close(force: true));
|
| + var clientFuture = client(server.address.address, server.port);
|
| + return [serverFuture, clientFuture];
|
| + });
|
| +}
|
| +
|
| +void runTests() {
|
| + var INVALID_PROTOBUF = [1, 2, 3, 4, 5];
|
| + var serverResponseBytes = [4, 5, 6];
|
| +
|
| + pb.Request pbRequest = new pb.Request();
|
| + pbRequest.requestId = 'test-id';
|
| + pbRequest.serviceName = 'test-service';
|
| + pbRequest.method = 'test-method';
|
| + pbRequest.request = [1, 2, 3];
|
| +
|
| + runClient(String address, int port,
|
| + List<int> requestData, Function validate,
|
| + {String path: '/foobar', method: 'POST'}) {
|
| + var client = new HttpClient();
|
| + return client.open(method, address, port, path).then((request) {
|
| + request.add(requestData);
|
| + return request.close().then((HttpClientResponse response) {
|
| + return response
|
| + .fold([], (buffer, data) => buffer..addAll(data))
|
| + .then(validate);
|
| + });
|
| + }).whenComplete(client.close);
|
| + }
|
| +
|
| + runClientAndCheckRtokResponse(String address, int port) {
|
| + var rtok = 'RR';
|
| + validate(List<int> bytes) {
|
| + var json = JSON.decode(UTF8.decode(bytes));
|
| + expect(json, isMap);
|
| + expect(json['app_id'], equals('dev~application'));
|
| + expect(json['rtok'], equals(rtok));
|
| + }
|
| +
|
| + return runClient(
|
| + address, port, [], validate, path: '/foobar?rtok=$rtok', method: 'GET');
|
| + }
|
| +
|
| + runClientAndCheckApiCallResponse(String address, int port) {
|
| + validate(List<int> bytes) {
|
| + pb.Response response = new pb.Response.fromBuffer(bytes);
|
| + expect(response.response, equals(serverResponseBytes));
|
| + }
|
| + return runClient(address, port, pbRequest.writeToBuffer(), validate);
|
| + }
|
| +
|
| + runClientWithInvalidMethod(String address, int port) {
|
| + return runClient(
|
| + address, port, [], (_) {}, path: '/foobar?rtok=foo', method: 'HEAD');
|
| + }
|
| +
|
| + runClientWithInvalidProtobuf(String address, int port) {
|
| + return runClient(address, port, INVALID_PROTOBUF, (_) {},
|
| + path: '/foobar?rtok=foo');
|
| + }
|
| +
|
| + runClientAndCheckRpcError(String address, int port) {
|
| + validate(List<int> bytes) {
|
| + pb.Response pbResponse = new pb.Response.fromBuffer(bytes);
|
| + expect(pbResponse.applicationError.code,
|
| + equals(ErrorMockRPCService.RPC_ERROR_CODE));
|
| + expect(pbResponse.applicationError.detail,
|
| + equals(ErrorMockRPCService.RPC_ERROR_MESSAGE));
|
| + }
|
| +
|
| + return runClient(address, port, pbRequest.writeToBuffer(), validate);
|
| + }
|
| +
|
| + runClientWithoutValidation(String address, int port) {
|
| + return runClient(address, port, pbRequest.writeToBuffer(), (){});
|
| + }
|
| +
|
| + group('remote_api', () {
|
| + var invalidTicket = 'invalid-ticket';
|
| + var context = new AppengineContext(
|
| + 'dev', 'application', 'version', null, null, null);
|
| +
|
| + test('get_rtok', () {
|
| + var remoteApi = new RemoteApiImpl(null, context, invalidTicket);
|
| +
|
| + return testRemoteApi(remoteApi, runClientAndCheckRtokResponse)
|
| + .then((futures) => Future.wait(futures));
|
| + });
|
| +
|
| + test('make_request', () {
|
| + var rpcMock =
|
| + new MockRPCService(pbRequest, serverResponseBytes, invalidTicket);
|
| + var remoteApi = new RemoteApiImpl(rpcMock, context, invalidTicket);
|
| +
|
| + return testRemoteApi(remoteApi, runClientAndCheckApiCallResponse)
|
| + .then((futures) => Future.wait(futures));
|
| + });
|
| +
|
| + test('invalid_method', () {
|
| + var remoteApi = new RemoteApiImpl(null, context, invalidTicket);
|
| +
|
| + return testRemoteApi(remoteApi, runClientWithInvalidMethod)
|
| + .then((futures) {
|
| + expect(futures[0], throwsA(isAppEngineApplicationError));
|
| + return futures[1].catchError((_) {});
|
| + });
|
| + });
|
| +
|
| + test('invalid_protobuf_request', () {
|
| + var remoteApi = new RemoteApiImpl(null, context, invalidTicket);
|
| +
|
| + return testRemoteApi(remoteApi, runClientWithInvalidProtobuf)
|
| + .then((futures) {
|
| + expect(futures[0], throws);
|
| + return futures[1].catchError((_) {});
|
| + });
|
| + });
|
| +
|
| + test('rpc_error', () {
|
| + var rpcMock = new ErrorMockRPCService(throwRpcError: true);
|
| + var remoteApi = new RemoteApiImpl(rpcMock, context, invalidTicket);
|
| +
|
| + return testRemoteApi(remoteApi, runClientAndCheckRpcError)
|
| + .then((futures) => Future.wait(futures));
|
| + });
|
| +
|
| + test('network_error', () {
|
| + var rpcMock = new ErrorMockRPCService(throwNetworkError: true);
|
| + var remoteApi = new RemoteApiImpl(rpcMock, context, invalidTicket);
|
| +
|
| + return testRemoteApi(remoteApi, runClientWithoutValidation)
|
| + .then((futures) {
|
| + expect(futures[0], throwsA(isNetworkError));
|
| + return futures[1].catchError((e) {});
|
| + });
|
| + });
|
| +
|
| + test('protocol_error', () {
|
| + var rpcMock = new ErrorMockRPCService(throwProtocolError: true);
|
| + var remoteApi = new RemoteApiImpl(rpcMock, context, invalidTicket);
|
| +
|
| + return testRemoteApi(remoteApi, runClientWithoutValidation)
|
| + .then((futures) {
|
| + expect(futures[0], throwsA(isProtocolError));
|
| + return futures[1].catchError((e) {});
|
| + });
|
| + });
|
| +
|
| + test('unknown_error', () {
|
| + var rpcMock = new ErrorMockRPCService();
|
| + var remoteApi = new RemoteApiImpl(rpcMock, context, invalidTicket);
|
| +
|
| + return testRemoteApi(remoteApi, runClientWithoutValidation)
|
| + .then((futures) {
|
| + expect(futures[0], throws);
|
| + return futures[1].catchError((e) {});
|
| + });
|
| + });
|
| + });
|
| +}
|
| +
|
| +main() {
|
| + runTests();
|
| +}
|
|
|