add(angel3): adding re-branded angel3 mock_request package
This commit is contained in:
parent
9148934aa6
commit
d884105ded
16 changed files with 1127 additions and 0 deletions
71
packages/mock_request/.gitignore
vendored
Normal file
71
packages/mock_request/.gitignore
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.dart_tool
|
||||
.packages
|
||||
.pub/
|
||||
build/
|
||||
|
||||
# If you're building an application, you may want to check-in your pubspec.lock
|
||||
pubspec.lock
|
||||
|
||||
# Directory created by dartdoc
|
||||
# If you don't generate documentation locally you can remove this line.
|
||||
doc/api/
|
||||
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
|
||||
# SDK 1.20 and later (no longer creates packages directories)
|
||||
|
||||
# Older SDK versions
|
||||
# (Include if the minimum SDK version specified in pubsepc.yaml is earlier than 1.20)
|
||||
.project
|
||||
.buildlog
|
||||
**/packages/
|
||||
|
||||
|
||||
# Files created by dart2js
|
||||
# (Most Dart developers will use pub build to compile Dart, use/modify these
|
||||
# rules if you intend to use dart2js directly
|
||||
# Convention is to use extension '.dart.js' for Dart compiled to Javascript to
|
||||
# differentiate from explicit Javascript files)
|
||||
*.dart.js
|
||||
*.part.js
|
||||
*.js.deps
|
||||
*.js.map
|
||||
*.info.json
|
||||
|
||||
# Directory created by dartdoc
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
### JetBrains template
|
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||
|
||||
# User-specific stuff:
|
||||
|
||||
## VsCode
|
||||
.vscode/
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
.idea/
|
||||
/out/
|
||||
.idea_modules/
|
||||
|
||||
# JIRA plugin
|
||||
atlassian-ide-plugin.xml
|
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||
com_crashlytics_export_strings.xml
|
||||
crashlytics.properties
|
||||
crashlytics-build.properties
|
||||
fabric.properties
|
12
packages/mock_request/AUTHORS.md
Normal file
12
packages/mock_request/AUTHORS.md
Normal file
|
@ -0,0 +1,12 @@
|
|||
Primary Authors
|
||||
===============
|
||||
|
||||
* __[Thomas Hii](dukefirehawk.apps@gmail.com)__
|
||||
|
||||
Thomas is the current maintainer of the code base. He has refactored and migrated the
|
||||
code base to support NNBD.
|
||||
|
||||
* __[Tobe O](thosakwe@gmail.com)__
|
||||
|
||||
Tobe has written much of the original code prior to NNBD migration. He has moved on and
|
||||
is no longer involved with the project.
|
78
packages/mock_request/CHANGELOG.md
Normal file
78
packages/mock_request/CHANGELOG.md
Normal file
|
@ -0,0 +1,78 @@
|
|||
# Change Log
|
||||
|
||||
## 8.1.1
|
||||
|
||||
* Updated repository link
|
||||
|
||||
## 8.1.0
|
||||
|
||||
* Updated `lints` to 3.0.0
|
||||
|
||||
## 8.0.0
|
||||
|
||||
* Require Dart >= 3.0
|
||||
* Updated `http` to 1.0.0
|
||||
|
||||
## 7.0.1
|
||||
|
||||
* Fixed `BytesBuilder` warnings
|
||||
|
||||
## 7.0.0
|
||||
|
||||
* Require Dart >= 2.17
|
||||
|
||||
## 6.0.0
|
||||
|
||||
* Require Dart >= 2.16
|
||||
|
||||
## 5.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 4.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 3.0.0
|
||||
|
||||
* Skipped release
|
||||
|
||||
## 2.1.0
|
||||
|
||||
* Updated linter to `package:lints`
|
||||
|
||||
## 2.0.2
|
||||
|
||||
* Updated README
|
||||
* Updated test cases
|
||||
|
||||
## 2.0.1
|
||||
|
||||
* Updated README
|
||||
|
||||
## 2.0.0
|
||||
|
||||
* Migrated to work with Dart >= 2.12 NNBD
|
||||
|
||||
## 1.0.7
|
||||
|
||||
* Prepare for upcoming Dart SDK change where `HttpHeaders` methods
|
||||
`add` and `set` take an additional optional parameter `preserveHeaderCase` (thanks @domesticmouse!).
|
||||
|
||||
## 1.0.6
|
||||
|
||||
* Prepare for upcoming Dart SDK change whereby `HttpRequest` implements
|
||||
`Stream<Uint8List>` rather than `Stream<List<int>>`.
|
||||
|
||||
## 1.0.5
|
||||
|
||||
* Add `toString` to `MockHttpHeaders`.
|
||||
|
||||
## 1.0.4
|
||||
|
||||
* Fix for `ifModifiedSince`
|
||||
|
||||
## 1.0.3
|
||||
|
||||
* Dart2 fixes
|
||||
* Apparently fix hangs that break Angel tests
|
29
packages/mock_request/LICENSE
Normal file
29
packages/mock_request/LICENSE
Normal file
|
@ -0,0 +1,29 @@
|
|||
BSD 3-Clause License
|
||||
|
||||
Copyright (c) 2021, dukefirehawk.com
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice, this
|
||||
list of conditions and the following disclaimer.
|
||||
|
||||
2. Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
|
||||
3. Neither the name of the copyright holder nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
26
packages/mock_request/README.md
Normal file
26
packages/mock_request/README.md
Normal file
|
@ -0,0 +1,26 @@
|
|||
# Mock HTTP Request
|
||||
|
||||
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_mock_request?include_prereleases)
|
||||
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
|
||||
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
|
||||
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/mock_request/LICENSE)
|
||||
|
||||
**Forked from `mock_request` to support NNBD**
|
||||
|
||||
Manufacture dart:io HttpRequests, HttpResponses, HttpHeaders, etc. This makes it possible to test server-side Dart applications without having to ever bind to a port.
|
||||
|
||||
This package was originally designed to make testing [Angel3](https://angel3-framework.web.app/) applications smoother, but works with any Dart-based server.
|
||||
|
||||
## Usage
|
||||
|
||||
```dart
|
||||
var rq = MockHttpRequest('GET', Uri.parse('/foo'));
|
||||
await rq.close();
|
||||
await app.handleRequest(rq); // Run within your server-side application
|
||||
var rs = rq.response;
|
||||
expect(rs.statusCode, equals(200));
|
||||
expect(await rs.transform(UTF8.decoder).join(),
|
||||
equals(JSON.encode('Hello, world!')));
|
||||
```
|
||||
|
||||
More examples can be found in the included test cases.
|
1
packages/mock_request/analysis_options.yaml
Normal file
1
packages/mock_request/analysis_options.yaml
Normal file
|
@ -0,0 +1 @@
|
|||
include: package:lints/recommended.yaml
|
8
packages/mock_request/example/main.dart
Normal file
8
packages/mock_request/example/main.dart
Normal file
|
@ -0,0 +1,8 @@
|
|||
import 'dart:async';
|
||||
import 'package:platform_mock_request/platform_mock_request.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
var rq =
|
||||
MockHttpRequest('GET', Uri.parse('/foo'), persistentConnection: false);
|
||||
await rq.close();
|
||||
}
|
6
packages/mock_request/lib/platform_mock_request.dart
Normal file
6
packages/mock_request/lib/platform_mock_request.dart
Normal file
|
@ -0,0 +1,6 @@
|
|||
export 'src/connection_info.dart';
|
||||
export 'src/headers.dart';
|
||||
export 'src/lockable_headers.dart';
|
||||
export 'src/request.dart';
|
||||
export 'src/response.dart';
|
||||
export 'src/session.dart';
|
13
packages/mock_request/lib/src/connection_info.dart
Normal file
13
packages/mock_request/lib/src/connection_info.dart
Normal file
|
@ -0,0 +1,13 @@
|
|||
import 'dart:io';
|
||||
|
||||
class MockHttpConnectionInfo implements HttpConnectionInfo {
|
||||
@override
|
||||
final InternetAddress remoteAddress;
|
||||
@override
|
||||
final int localPort, remotePort;
|
||||
|
||||
MockHttpConnectionInfo(
|
||||
{required this.remoteAddress,
|
||||
this.localPort = 8080,
|
||||
this.remotePort = 80});
|
||||
}
|
174
packages/mock_request/lib/src/headers.dart
Normal file
174
packages/mock_request/lib/src/headers.dart
Normal file
|
@ -0,0 +1,174 @@
|
|||
import 'dart:io';
|
||||
|
||||
class MockHttpHeaders implements HttpHeaders {
|
||||
final Map<String, List<String>> _data = {};
|
||||
final List<String> _noFolding = [];
|
||||
//Uri? _host;
|
||||
String? _hostname;
|
||||
int _port = 80;
|
||||
|
||||
List<String> get doNotFold => List<String>.unmodifiable(_noFolding);
|
||||
|
||||
@override
|
||||
ContentType get contentType {
|
||||
if (_data.containsKey(HttpHeaders.contentTypeHeader)) {
|
||||
return ContentType.parse(_data[HttpHeaders.contentTypeHeader]!.join(','));
|
||||
} else {
|
||||
return ContentType.html;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
set contentType(ContentType? value) =>
|
||||
set(HttpHeaders.contentTypeHeader, value?.value ?? ContentType.html);
|
||||
|
||||
@override
|
||||
DateTime get date => _data.containsKey(HttpHeaders.dateHeader)
|
||||
? HttpDate.parse(_data[HttpHeaders.dateHeader]!.join(','))
|
||||
: DateTime.now();
|
||||
|
||||
@override
|
||||
set date(DateTime? value) =>
|
||||
set(HttpHeaders.dateHeader, HttpDate.format(value ?? DateTime.now()));
|
||||
|
||||
@override
|
||||
DateTime get expires => _data.containsKey(HttpHeaders.expiresHeader)
|
||||
? HttpDate.parse(_data[HttpHeaders.expiresHeader]!.join(','))
|
||||
: DateTime.now();
|
||||
|
||||
@override
|
||||
set expires(DateTime? value) =>
|
||||
set(HttpHeaders.expiresHeader, HttpDate.format(value ?? DateTime.now()));
|
||||
|
||||
@override
|
||||
DateTime get ifModifiedSince =>
|
||||
_data.containsKey(HttpHeaders.ifModifiedSinceHeader)
|
||||
? HttpDate.parse(_data[HttpHeaders.ifModifiedSinceHeader]!.join(','))
|
||||
: DateTime.now();
|
||||
|
||||
@override
|
||||
set ifModifiedSince(DateTime? value) => set(HttpHeaders.ifModifiedSinceHeader,
|
||||
HttpDate.format(value ?? DateTime.now()));
|
||||
|
||||
@override
|
||||
String? get host {
|
||||
return _hostname;
|
||||
/*
|
||||
if (_host != null) {
|
||||
return _host!.host;
|
||||
} else if (_data.containsKey(HttpHeaders.hostHeader)) {
|
||||
_host = Uri.parse(_data[HttpHeaders.hostHeader]!.join(','));
|
||||
return _host!.host;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
@override
|
||||
int get port {
|
||||
return _port;
|
||||
}
|
||||
|
||||
@override
|
||||
List<String>? operator [](String name) => _data[name.toLowerCase()];
|
||||
|
||||
@override
|
||||
void add(String name, Object value, {bool preserveHeaderCase = false}) {
|
||||
var lower = preserveHeaderCase ? name : name.toLowerCase();
|
||||
|
||||
if (_data.containsKey(lower)) {
|
||||
if (value is Iterable) {
|
||||
_data[lower]!.addAll(value.map((x) => x.toString()).toList());
|
||||
} else {
|
||||
_data[lower]!.add(value.toString());
|
||||
}
|
||||
} else {
|
||||
if (value is Iterable) {
|
||||
_data[lower] = value.map((x) => x.toString()).toList();
|
||||
} else {
|
||||
_data[lower] = [value.toString()];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void clear() {
|
||||
_data.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
void forEach(void Function(String name, List<String> values) action) {
|
||||
_data.forEach(action);
|
||||
}
|
||||
|
||||
@override
|
||||
void noFolding(String name) {
|
||||
_noFolding.add(name.toLowerCase());
|
||||
}
|
||||
|
||||
@override
|
||||
void remove(String name, Object value) {
|
||||
var lower = name.toLowerCase();
|
||||
|
||||
if (_data.containsKey(lower)) {
|
||||
if (value is Iterable) {
|
||||
for (var x in value) {
|
||||
_data[lower]!.remove(x.toString());
|
||||
}
|
||||
} else {
|
||||
_data[lower]!.remove(value.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void removeAll(String name) {
|
||||
_data.remove(name.toLowerCase());
|
||||
}
|
||||
|
||||
@override
|
||||
void set(String name, Object value, {bool preserveHeaderCase = false}) {
|
||||
var lower = preserveHeaderCase ? name : name.toLowerCase();
|
||||
_data.remove(lower);
|
||||
|
||||
if (value is Iterable) {
|
||||
_data[lower] = value.map((x) => x.toString()).toList();
|
||||
} else {
|
||||
_data[lower] = [value.toString()];
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
String? value(String name) => _data[name.toLowerCase()]?.join(',');
|
||||
|
||||
@override
|
||||
String toString() {
|
||||
var b = StringBuffer();
|
||||
_data.forEach((k, v) {
|
||||
b.write('$k: ');
|
||||
b.write(v.join(','));
|
||||
b.writeln();
|
||||
});
|
||||
return b.toString();
|
||||
}
|
||||
|
||||
@override
|
||||
bool chunkedTransferEncoding = false;
|
||||
|
||||
@override
|
||||
int contentLength = 0;
|
||||
|
||||
@override
|
||||
bool persistentConnection = true;
|
||||
|
||||
@override
|
||||
set host(String? host) {
|
||||
_hostname = host;
|
||||
}
|
||||
|
||||
@override
|
||||
set port(int? port) {
|
||||
_port = port ?? 80;
|
||||
}
|
||||
}
|
67
packages/mock_request/lib/src/lockable_headers.dart
Normal file
67
packages/mock_request/lib/src/lockable_headers.dart
Normal file
|
@ -0,0 +1,67 @@
|
|||
import 'headers.dart';
|
||||
|
||||
/// Headers that can be locked to editing, i.e. after a request body has been written.
|
||||
class LockableMockHttpHeaders extends MockHttpHeaders {
|
||||
bool _locked = false;
|
||||
|
||||
StateError _stateError() =>
|
||||
StateError('Cannot modify headers after they have been write-locked.');
|
||||
|
||||
void lock() {
|
||||
_locked = true;
|
||||
}
|
||||
|
||||
@override
|
||||
void add(String name, Object value, {bool preserveHeaderCase = false}) {
|
||||
if (_locked) {
|
||||
throw _stateError();
|
||||
} else {
|
||||
super.add(name, value, preserveHeaderCase: preserveHeaderCase);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void clear() {
|
||||
if (_locked) {
|
||||
throw _stateError();
|
||||
} else {
|
||||
super.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void noFolding(String name) {
|
||||
if (_locked) {
|
||||
throw _stateError();
|
||||
} else {
|
||||
super.noFolding(name);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void remove(String name, Object value) {
|
||||
if (_locked) {
|
||||
throw _stateError();
|
||||
} else {
|
||||
super.remove(name, value);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void removeAll(String name) {
|
||||
if (_locked) {
|
||||
throw _stateError();
|
||||
} else {
|
||||
super.removeAll(name);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void set(String name, Object value, {bool preserveHeaderCase = false}) {
|
||||
if (_locked) {
|
||||
throw _stateError();
|
||||
} else {
|
||||
super.set(name, value, preserveHeaderCase: preserveHeaderCase);
|
||||
}
|
||||
}
|
||||
}
|
323
packages/mock_request/lib/src/request.dart
Normal file
323
packages/mock_request/lib/src/request.dart
Normal file
|
@ -0,0 +1,323 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'dart:typed_data';
|
||||
import 'package:charcode/ascii.dart';
|
||||
import 'connection_info.dart';
|
||||
import 'lockable_headers.dart';
|
||||
import 'response.dart';
|
||||
import 'session.dart';
|
||||
|
||||
class MockHttpRequest
|
||||
implements HttpRequest, StreamSink<List<int>>, StringSink {
|
||||
int _contentLength = 0;
|
||||
late BytesBuilder _buf;
|
||||
final Completer _done = Completer();
|
||||
final LockableMockHttpHeaders _headers = LockableMockHttpHeaders();
|
||||
Uri? _requestedUri;
|
||||
late MockHttpSession _session;
|
||||
final StreamController<Uint8List> _stream = StreamController<Uint8List>();
|
||||
|
||||
@override
|
||||
final List<Cookie> cookies = [];
|
||||
|
||||
@override
|
||||
HttpConnectionInfo connectionInfo =
|
||||
MockHttpConnectionInfo(remoteAddress: InternetAddress.loopbackIPv4);
|
||||
|
||||
@override
|
||||
MockHttpResponse response = MockHttpResponse(
|
||||
contentLength: 0,
|
||||
encoding: utf8,
|
||||
persistentConnection: false,
|
||||
reasonPhrase: '',
|
||||
statusCode: 200);
|
||||
|
||||
@override
|
||||
HttpSession get session => _session;
|
||||
|
||||
@override
|
||||
final String method;
|
||||
|
||||
@override
|
||||
final Uri uri;
|
||||
|
||||
@override
|
||||
bool persistentConnection = true;
|
||||
|
||||
/// [copyBuffer] corresponds to `copy` on the [BytesBuilder] constructor.
|
||||
MockHttpRequest(this.method, this.uri,
|
||||
{bool copyBuffer = true,
|
||||
String? protocolVersion,
|
||||
String? sessionId,
|
||||
this.certificate,
|
||||
this.persistentConnection = true}) {
|
||||
_buf = BytesBuilder(copy: copyBuffer != false);
|
||||
_session = MockHttpSession(id: sessionId ?? 'mock-http-session');
|
||||
|
||||
this.protocolVersion = protocolVersion ?? '1.1';
|
||||
}
|
||||
|
||||
@override
|
||||
int get contentLength => _contentLength;
|
||||
|
||||
@override
|
||||
HttpHeaders get headers => _headers;
|
||||
|
||||
@override
|
||||
Uri get requestedUri {
|
||||
if (_requestedUri != null) {
|
||||
return _requestedUri!;
|
||||
} else {
|
||||
return _requestedUri = Uri(
|
||||
scheme: 'http',
|
||||
host: 'example.com',
|
||||
path: uri.path,
|
||||
query: uri.query,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
set requestedUri(Uri value) {
|
||||
_requestedUri = value;
|
||||
}
|
||||
|
||||
@override
|
||||
late String protocolVersion;
|
||||
|
||||
@override
|
||||
X509Certificate? certificate;
|
||||
|
||||
@override
|
||||
void add(List<int> data) {
|
||||
if (_done.isCompleted) {
|
||||
throw StateError('Cannot add to closed MockHttpRequest.');
|
||||
} else {
|
||||
_headers.lock();
|
||||
_contentLength += data.length;
|
||||
_buf.add(data);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void addError(error, [StackTrace? stackTrace]) {
|
||||
if (_done.isCompleted) {
|
||||
throw StateError('Cannot add to closed MockHttpRequest.');
|
||||
} else {
|
||||
_stream.addError(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future addStream(Stream<List<int>> stream) {
|
||||
var c = Completer();
|
||||
stream.listen(add, onError: addError, onDone: c.complete);
|
||||
return c.future;
|
||||
}
|
||||
|
||||
@override
|
||||
Future close() async {
|
||||
await flush();
|
||||
_headers.lock();
|
||||
scheduleMicrotask(_stream.close);
|
||||
_done.complete();
|
||||
return await _done.future;
|
||||
}
|
||||
|
||||
@override
|
||||
Future get done => _done.future;
|
||||
|
||||
// @override
|
||||
Future flush() async {
|
||||
_contentLength += _buf.length;
|
||||
_stream.add(_buf.takeBytes());
|
||||
}
|
||||
|
||||
@override
|
||||
void write(Object? obj) {
|
||||
obj?.toString().codeUnits.forEach(writeCharCode);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeAll(Iterable objects, [String separator = '']) {
|
||||
write(objects.join(separator));
|
||||
}
|
||||
|
||||
@override
|
||||
void writeCharCode(int charCode) {
|
||||
add([charCode]);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeln([Object? obj = '']) {
|
||||
write(obj ?? '');
|
||||
add([$cr, $lf]);
|
||||
}
|
||||
|
||||
@override
|
||||
Future<bool> any(bool Function(Uint8List element) test) {
|
||||
return _stream.stream.any((List<int> e) {
|
||||
return test(Uint8List.fromList(e));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<Uint8List> asBroadcastStream({
|
||||
void Function(StreamSubscription<Uint8List> subscription)? onListen,
|
||||
void Function(StreamSubscription<Uint8List> subscription)? onCancel,
|
||||
}) {
|
||||
return _stream.stream
|
||||
.asBroadcastStream(onListen: onListen, onCancel: onCancel);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<E> asyncExpand<E>(Stream<E>? Function(Uint8List event) convert) =>
|
||||
_stream.stream.asyncExpand(convert);
|
||||
|
||||
@override
|
||||
Stream<E> asyncMap<E>(FutureOr<E> Function(Uint8List event) convert) =>
|
||||
_stream.stream.asyncMap(convert);
|
||||
|
||||
@override
|
||||
Future<bool> contains(Object? needle) => _stream.stream.contains(needle);
|
||||
|
||||
@override
|
||||
Stream<Uint8List> distinct(
|
||||
[bool Function(Uint8List previous, Uint8List next)? equals]) =>
|
||||
_stream.stream.distinct(equals);
|
||||
|
||||
@override
|
||||
Future<E> drain<E>([E? futureValue]) => _stream.stream.drain(futureValue);
|
||||
|
||||
@override
|
||||
Future<Uint8List> elementAt(int index) => _stream.stream.elementAt(index);
|
||||
|
||||
@override
|
||||
Future<bool> every(bool Function(Uint8List element) test) =>
|
||||
_stream.stream.every(test);
|
||||
|
||||
@override
|
||||
Stream<S> expand<S>(Iterable<S> Function(Uint8List value) convert) =>
|
||||
_stream.stream.expand(convert);
|
||||
|
||||
@override
|
||||
Future<Uint8List> get first => _stream.stream.first;
|
||||
|
||||
@override
|
||||
Future<Uint8List> firstWhere(bool Function(Uint8List element) test,
|
||||
{List<int> Function()? orElse}) =>
|
||||
_stream.stream
|
||||
.firstWhere(test, orElse: () => Uint8List.fromList(orElse!()));
|
||||
|
||||
@override
|
||||
Future<S> fold<S>(
|
||||
S initialValue, S Function(S previous, Uint8List element) combine) =>
|
||||
_stream.stream.fold(initialValue, combine);
|
||||
|
||||
@override
|
||||
Future forEach(void Function(Uint8List element) action) =>
|
||||
_stream.stream.forEach(action);
|
||||
|
||||
@override
|
||||
Stream<Uint8List> handleError(Function onError,
|
||||
{bool Function(Object?)? test}) =>
|
||||
_stream.stream.handleError(onError, test: test);
|
||||
|
||||
@override
|
||||
bool get isBroadcast => _stream.stream.isBroadcast;
|
||||
|
||||
@override
|
||||
Future<bool> get isEmpty => _stream.stream.isEmpty;
|
||||
|
||||
@override
|
||||
Future<String> join([String separator = '']) =>
|
||||
_stream.stream.join(separator);
|
||||
|
||||
@override
|
||||
Future<Uint8List> get last => _stream.stream.last;
|
||||
|
||||
@override
|
||||
Future<Uint8List> lastWhere(bool Function(Uint8List element) test,
|
||||
{List<int> Function()? orElse}) =>
|
||||
_stream.stream
|
||||
.lastWhere(test, orElse: () => Uint8List.fromList(orElse!()));
|
||||
|
||||
@override
|
||||
Future<int> get length => _stream.stream.length;
|
||||
|
||||
@override
|
||||
StreamSubscription<Uint8List> listen(
|
||||
void Function(Uint8List event)? onData, {
|
||||
Function? onError,
|
||||
void Function()? onDone,
|
||||
bool? cancelOnError,
|
||||
}) {
|
||||
return _stream.stream.listen(
|
||||
onData,
|
||||
onError: onError,
|
||||
onDone: onDone,
|
||||
cancelOnError: cancelOnError == true,
|
||||
);
|
||||
}
|
||||
|
||||
@override
|
||||
Stream<S> map<S>(S Function(Uint8List event) convert) =>
|
||||
_stream.stream.map(convert);
|
||||
|
||||
@override
|
||||
Future pipe(StreamConsumer<List<int>> streamConsumer) =>
|
||||
_stream.stream.cast<List<int>>().pipe(streamConsumer);
|
||||
|
||||
@override
|
||||
Future<Uint8List> reduce(
|
||||
List<int> Function(Uint8List previous, Uint8List element) combine) {
|
||||
return _stream.stream.reduce((Uint8List previous, Uint8List element) {
|
||||
return Uint8List.fromList(combine(previous, element));
|
||||
});
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Uint8List> get single => _stream.stream.single;
|
||||
|
||||
@override
|
||||
Future<Uint8List> singleWhere(bool Function(Uint8List element) test,
|
||||
{List<int> Function()? orElse}) =>
|
||||
_stream.stream
|
||||
.singleWhere(test, orElse: () => Uint8List.fromList(orElse!()));
|
||||
|
||||
@override
|
||||
Stream<Uint8List> skip(int count) => _stream.stream.skip(count);
|
||||
|
||||
@override
|
||||
Stream<Uint8List> skipWhile(bool Function(Uint8List element) test) =>
|
||||
_stream.stream.skipWhile(test);
|
||||
|
||||
@override
|
||||
Stream<Uint8List> take(int count) => _stream.stream.take(count);
|
||||
|
||||
@override
|
||||
Stream<Uint8List> takeWhile(bool Function(Uint8List element) test) =>
|
||||
_stream.stream.takeWhile(test);
|
||||
|
||||
@override
|
||||
Stream<Uint8List> timeout(Duration timeLimit,
|
||||
{void Function(EventSink<Uint8List> sink)? onTimeout}) =>
|
||||
_stream.stream.timeout(timeLimit, onTimeout: onTimeout);
|
||||
|
||||
@override
|
||||
Future<List<Uint8List>> toList() => _stream.stream.toList();
|
||||
|
||||
@override
|
||||
Future<Set<Uint8List>> toSet() => _stream.stream.toSet();
|
||||
|
||||
@override
|
||||
Stream<S> transform<S>(StreamTransformer<List<int>, S> streamTransformer) =>
|
||||
_stream.stream.cast<List<int>>().transform(streamTransformer);
|
||||
|
||||
@override
|
||||
Stream<Uint8List> where(bool Function(Uint8List event) test) =>
|
||||
_stream.stream.where(test);
|
||||
|
||||
@override
|
||||
Stream<R> cast<R>() => Stream.castFrom<List<int>, R>(this);
|
||||
}
|
151
packages/mock_request/lib/src/response.dart
Normal file
151
packages/mock_request/lib/src/response.dart
Normal file
|
@ -0,0 +1,151 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io' hide BytesBuilder;
|
||||
import 'dart:typed_data';
|
||||
import 'package:charcode/ascii.dart';
|
||||
import 'connection_info.dart';
|
||||
import 'lockable_headers.dart';
|
||||
|
||||
class MockHttpResponse extends Stream<List<int>> implements HttpResponse {
|
||||
BytesBuilder _buf = BytesBuilder();
|
||||
bool _bufferOutput = true;
|
||||
final Completer _done = Completer();
|
||||
final LockableMockHttpHeaders _headers = LockableMockHttpHeaders();
|
||||
final StreamController<List<int>> _stream = StreamController<List<int>>();
|
||||
|
||||
@override
|
||||
final List<Cookie> cookies = [];
|
||||
|
||||
@override
|
||||
HttpConnectionInfo connectionInfo =
|
||||
MockHttpConnectionInfo(remoteAddress: InternetAddress.anyIPv4);
|
||||
|
||||
/// [copyBuffer] corresponds to `copy` on the [BytesBuilder] constructor.
|
||||
MockHttpResponse(
|
||||
{bool copyBuffer = true,
|
||||
required this.statusCode,
|
||||
required this.reasonPhrase,
|
||||
required this.contentLength,
|
||||
this.deadline,
|
||||
required this.encoding,
|
||||
required this.persistentConnection,
|
||||
bool? bufferOutput}) {
|
||||
_buf = BytesBuilder(copy: copyBuffer != false);
|
||||
_bufferOutput = bufferOutput != false;
|
||||
statusCode = 200;
|
||||
}
|
||||
|
||||
@override
|
||||
bool get bufferOutput => _bufferOutput;
|
||||
|
||||
@override
|
||||
set bufferOutput(bool value) {}
|
||||
|
||||
@override
|
||||
int contentLength;
|
||||
|
||||
@override
|
||||
Duration? deadline;
|
||||
|
||||
@override
|
||||
bool persistentConnection;
|
||||
|
||||
@override
|
||||
String reasonPhrase;
|
||||
|
||||
@override
|
||||
int statusCode;
|
||||
|
||||
@override
|
||||
Encoding encoding;
|
||||
|
||||
@override
|
||||
HttpHeaders get headers => _headers;
|
||||
|
||||
@override
|
||||
Future get done => _done.future;
|
||||
|
||||
@override
|
||||
void add(List<int> data) {
|
||||
if (_done.isCompleted) {
|
||||
throw StateError('Cannot add to closed MockHttpResponse.');
|
||||
} else {
|
||||
_headers.lock();
|
||||
if (_bufferOutput == true) {
|
||||
_buf.add(data);
|
||||
} else {
|
||||
_stream.add(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
void addError(error, [StackTrace? stackTrace]) {
|
||||
if (_done.isCompleted) {
|
||||
throw StateError('Cannot add to closed MockHttpResponse.');
|
||||
} else {
|
||||
_stream.addError(error, stackTrace);
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Future addStream(Stream<List<int>> stream) {
|
||||
var c = Completer();
|
||||
stream.listen(add, onError: addError, onDone: c.complete);
|
||||
return c.future;
|
||||
}
|
||||
|
||||
@override
|
||||
Future close() async {
|
||||
_headers.lock();
|
||||
await flush();
|
||||
scheduleMicrotask(_stream.close);
|
||||
_done.complete();
|
||||
//return await _done.future;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Socket> detachSocket({bool writeHeaders = true}) {
|
||||
throw UnsupportedError('MockHttpResponses have no socket to detach.');
|
||||
}
|
||||
|
||||
@override
|
||||
Future flush() async {
|
||||
_stream.add(_buf.takeBytes());
|
||||
}
|
||||
|
||||
@override
|
||||
Future redirect(Uri location,
|
||||
{int status = HttpStatus.movedTemporarily}) async {
|
||||
statusCode = status;
|
||||
}
|
||||
|
||||
@override
|
||||
void write(Object? obj) {
|
||||
obj?.toString().codeUnits.forEach(writeCharCode);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeAll(Iterable objects, [String separator = '']) {
|
||||
write(objects.join(separator));
|
||||
}
|
||||
|
||||
@override
|
||||
void writeCharCode(int charCode) {
|
||||
add([charCode]);
|
||||
}
|
||||
|
||||
@override
|
||||
void writeln([Object? obj = '']) {
|
||||
write(obj ?? '');
|
||||
add([$cr, $lf]);
|
||||
}
|
||||
|
||||
@override
|
||||
StreamSubscription<List<int>> listen(void Function(List<int> event)? onData,
|
||||
{Function? onError, void Function()? onDone, bool? cancelOnError}) =>
|
||||
_stream.stream.listen(onData,
|
||||
onError: onError,
|
||||
onDone: onDone,
|
||||
cancelOnError: cancelOnError == true);
|
||||
}
|
74
packages/mock_request/lib/src/session.dart
Normal file
74
packages/mock_request/lib/src/session.dart
Normal file
|
@ -0,0 +1,74 @@
|
|||
import 'dart:collection';
|
||||
import 'dart:io';
|
||||
|
||||
class MockHttpSession extends MapBase implements HttpSession {
|
||||
final Map _data = {};
|
||||
|
||||
@override
|
||||
String id;
|
||||
|
||||
MockHttpSession({required this.id});
|
||||
|
||||
@override
|
||||
int get length => _data.length;
|
||||
|
||||
@override
|
||||
dynamic operator [](Object? key) => _data[key];
|
||||
|
||||
@override
|
||||
void operator []=(key, value) {
|
||||
_data[key] = value;
|
||||
}
|
||||
|
||||
@override
|
||||
void addAll(Map other) => _data.addAll(other);
|
||||
|
||||
@override
|
||||
void clear() {
|
||||
_data.clear();
|
||||
}
|
||||
|
||||
@override
|
||||
bool containsKey(Object? key) => _data.containsKey(key);
|
||||
|
||||
@override
|
||||
bool containsValue(Object? value) => _data.containsValue(value);
|
||||
|
||||
@override
|
||||
void destroy() {
|
||||
print('destroy() was called on a MockHttpSession, which does nothing.');
|
||||
}
|
||||
|
||||
@override
|
||||
void forEach(void Function(dynamic, dynamic) action) {
|
||||
_data.forEach(action);
|
||||
}
|
||||
|
||||
@override
|
||||
bool get isEmpty => _data.isEmpty;
|
||||
|
||||
@override
|
||||
bool get isNew => true;
|
||||
|
||||
@override
|
||||
bool get isNotEmpty => _data.isNotEmpty;
|
||||
|
||||
@override
|
||||
Iterable get keys => _data.keys;
|
||||
|
||||
@override
|
||||
dynamic putIfAbsent(key, dynamic Function() ifAbsent) =>
|
||||
_data.putIfAbsent(key, ifAbsent);
|
||||
|
||||
@override
|
||||
dynamic remove(Object? key) => _data.remove(key);
|
||||
|
||||
@override
|
||||
Iterable get values => _data.values;
|
||||
|
||||
@override
|
||||
set onTimeout(void Function() callback) {
|
||||
print(
|
||||
'An onTimeout callback was set on a MockHttpSession, which will do nothing.');
|
||||
}
|
||||
}
|
25
packages/mock_request/pubspec.yaml
Normal file
25
packages/mock_request/pubspec.yaml
Normal file
|
@ -0,0 +1,25 @@
|
|||
name: platform_mock_request
|
||||
version: 9.0.0
|
||||
description: Manufacture dart:io HttpRequests, HttpResponses, HttpHeaders, etc.
|
||||
homepage: https://angel3-framework.web.app/
|
||||
repository: https://github.com/dart-backend/angel/tree/master/packages/mock_request
|
||||
environment:
|
||||
sdk: '>=3.3.0 <4.0.0'
|
||||
dependencies:
|
||||
charcode: ^1.3.0
|
||||
dev_dependencies:
|
||||
#angel3_framework: ^7.0.0
|
||||
http: ^1.0.0
|
||||
test: ^1.24.0
|
||||
lints: ^4.0.0
|
||||
# dependency_overrides:
|
||||
# angel3_framework:
|
||||
# path: ../framework
|
||||
# angel3_route:
|
||||
# path: ../route
|
||||
# angel3_model:
|
||||
# path: ../model
|
||||
# angel3_http_exception:
|
||||
# path: ../http_exception
|
||||
# angel3_container:
|
||||
# path: ../container/angel_container
|
69
packages/mock_request/test/all_test.dart
Normal file
69
packages/mock_request/test/all_test.dart
Normal file
|
@ -0,0 +1,69 @@
|
|||
//import 'dart:convert';
|
||||
//import 'dart:io';
|
||||
//import 'package:angel3_framework/angel3_framework.dart';
|
||||
//import 'package:angel3_framework/http.dart';
|
||||
//import 'package:angel3_mock_request/angel3_mock_request.dart';
|
||||
//import 'package:test/test.dart';
|
||||
|
||||
void main() {
|
||||
//var uri = Uri.parse('http://localhost:3000');
|
||||
|
||||
/*
|
||||
var app = Angel()
|
||||
..get('/foo', (req, res) => 'Hello, world!')
|
||||
..post('/body',
|
||||
(req, res) => req.parseBody().then((_) => req.bodyAsMap.length))
|
||||
..get('/session', (req, res) async {
|
||||
req.session?['foo'] = 'bar';
|
||||
})
|
||||
..get('/conn', (RequestContext req, res) {
|
||||
return res.serialize(req.ip == InternetAddress.loopbackIPv4.address);
|
||||
});
|
||||
|
||||
var http = AngelHttp(app);
|
||||
|
||||
test('receive a response', () async {
|
||||
var rq = MockHttpRequest('GET', uri.resolve('/foo'));
|
||||
await rq.close();
|
||||
await http.handleRequest(rq);
|
||||
var rs = rq.response;
|
||||
expect(rs.statusCode, equals(200));
|
||||
expect(await rs.transform(utf8.decoder).join(),
|
||||
equals(json.encode('Hello, world!')));
|
||||
});
|
||||
|
||||
test('send a body', () async {
|
||||
var rq = MockHttpRequest('POST', uri.resolve('/body'));
|
||||
rq
|
||||
..headers.set(HttpHeaders.contentTypeHeader, ContentType.json.mimeType)
|
||||
..write(json.encode({'foo': 'bar', 'bar': 'baz', 'baz': 'quux'}));
|
||||
await rq.close();
|
||||
await http.handleRequest(rq);
|
||||
var rs = rq.response;
|
||||
expect(rs.statusCode, equals(200));
|
||||
expect(await rs.transform(utf8.decoder).join(), equals(json.encode(3)));
|
||||
});
|
||||
|
||||
test('session', () async {
|
||||
var rq = MockHttpRequest('GET', uri.resolve('/session'));
|
||||
await rq.close();
|
||||
await http.handleRequest(rq);
|
||||
expect(rq.session.keys, contains('foo'));
|
||||
expect(rq.session['foo'], equals('bar'));
|
||||
});
|
||||
|
||||
test('connection info', () async {
|
||||
var rq = MockHttpRequest('GET', uri.resolve('/conn'));
|
||||
await rq.close();
|
||||
await http.handleRequest(rq);
|
||||
var rs = rq.response;
|
||||
expect(await rs.transform(utf8.decoder).join(), equals(json.encode(true)));
|
||||
});
|
||||
|
||||
test('requested uri', () {
|
||||
var rq = MockHttpRequest('GET', uri.resolve('/mock'));
|
||||
expect(rq.uri.path, '/mock');
|
||||
expect(rq.requestedUri.toString(), 'http://example.com/mock');
|
||||
});
|
||||
*/
|
||||
}
|
Loading…
Reference in a new issue