| // Copyright (c) 2017, 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:collection'; |
| |
| import 'package:shelf/shelf.dart'; |
| import 'package:test/test.dart'; |
| |
| import 'expectation.dart'; |
| |
| /// A [Handler] that handles requests as specified by [expect] and |
| /// [expectAnything]. |
| class ShelfTestHandler { |
| /// The description used in debugging output for this handler. |
| final String description; |
| |
| /// Whether to log each request to [printOnFailure]. |
| final bool _log; |
| |
| /// The zone in which this handler was created. |
| final Zone _zone; |
| |
| /// The queue of expected requests to this handler. |
| final _expectations = Queue<Expectation>(); |
| |
| /// Creates a new handler that handles requests using handlers provided by |
| /// [expect] and [expectAnything]. |
| /// |
| /// If [log] is `true` (the default), this prints all requests using |
| /// [printOnFailure]. |
| /// |
| /// The [description] is used in debugging output for this handler. It |
| /// defaults to "ShelfTestHandler". |
| ShelfTestHandler({bool log = true, String? description}) |
| : _log = log, |
| description = description ?? 'ShelfTestHandler', |
| _zone = Zone.current; |
| |
| /// Expects that a single HTTP request with the given [method] and [path] will |
| /// be made to [this]. |
| /// |
| /// The [path] should be root-relative; that is, it shuld start with "/". |
| /// |
| /// When a matching request is made, [handler] is used to handle that request. |
| /// |
| /// If this and/or [expectAnything] are called multiple times, the requests |
| /// are expected to occur in the same order. |
| void expect(String method, String path, Handler handler) { |
| _expectations.add(Expectation(method, path, handler)); |
| } |
| |
| /// Expects that a single HTTP request will be made to [this]. |
| /// |
| /// When a request is made, [handler] is used to handle that request. |
| /// |
| /// If this and/or [expect] are called multiple times, the requests are |
| /// expected to occur in the same order. |
| void expectAnything(Handler handler) { |
| _expectations.add(Expectation.anything(handler)); |
| } |
| |
| /// The implementation of [Handler]. |
| FutureOr<Response> call(Request request) async { |
| var requestInfo = '${request.method} /${request.url}'; |
| if (_log) printOnFailure('[$description] $requestInfo'); |
| |
| try { |
| if (_expectations.isEmpty) { |
| throw TestFailure( |
| '$description received unexpected request $requestInfo.'); |
| } |
| |
| var expectation = _expectations.removeFirst(); |
| if ((expectation.method != null && |
| expectation.method != request.method) || |
| (expectation.path != '/${request.url.path}' && |
| expectation.path != null)) { |
| var message = '$description received unexpected request $requestInfo.'; |
| if (expectation.method != null) { |
| message += '\nExpected ${expectation.method} ${expectation.path}.'; |
| } |
| throw TestFailure(message); |
| } |
| |
| return await expectation.handler(request); |
| } on HijackException catch (_) { |
| rethrow; |
| } catch (error, stackTrace) { |
| _zone.handleUncaughtError(error, stackTrace); |
| // We can't return null here, the handler type doesn't allow it. |
| return Response.internalServerError(body: '$error'); |
| } |
| } |
| } |