Added merge_map and mock_request packages
This commit is contained in:
parent
aefe1f1ab8
commit
f8ec10d4e2
26 changed files with 1285 additions and 6 deletions
|
@ -8,6 +8,9 @@
|
|||
* Updated angel_route to 5.0.0
|
||||
* Updated angel_model to 3.0.0
|
||||
* Updated angel_container to 3.0.0
|
||||
* Updated angel_framework to 4.0.0
|
||||
- merge_map
|
||||
- mock_request
|
||||
|
||||
# 3.0.0 (Non NNBD)
|
||||
* Changed Dart SDK requirements for all packages to ">=2.10.0 <3.0.0"
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
name: angel_framework
|
||||
version: 3.0.0
|
||||
version: 4.0.0
|
||||
description: A high-powered HTTP server with dependency injection, routing and much more.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/angel-dart/angel_framework
|
||||
|
@ -10,25 +10,29 @@ dependencies:
|
|||
angel_container:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/container/angel_container
|
||||
angel_http_exception:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/http_exception
|
||||
angel_model:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/model
|
||||
angel_route:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/route
|
||||
charcode: ^1.0.0
|
||||
combinator: ^1.0.0
|
||||
combinator:
|
||||
git:
|
||||
url: https://github.com/dukefirehawk/angel.git
|
||||
ref: sdk-2.12.x_nnbd
|
||||
path: packages/combinator
|
||||
file: ^6.1.0
|
||||
http_parser: ^4.0.0
|
||||
http_server: ^0.9.0
|
||||
|
|
76
packages/merge_map/.gitignore
vendored
Normal file
76
packages/merge_map/.gitignore
vendored
Normal file
|
@ -0,0 +1,76 @@
|
|||
# Created by .ignore support plugin (hsz.mobi)
|
||||
### 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:
|
||||
.idea/workspace.xml
|
||||
.idea/tasks.xml
|
||||
.idea/dictionaries
|
||||
.idea/vcs.xml
|
||||
.idea/jsLibraryMappings.xml
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/dataSources.ids
|
||||
.idea/dataSources.xml
|
||||
.idea/dataSources.local.xml
|
||||
.idea/sqlDataSources.xml
|
||||
.idea/dynamic.xml
|
||||
.idea/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/gradle.xml
|
||||
.idea/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.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
|
||||
### Dart template
|
||||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.buildlog
|
||||
.packages
|
||||
.project
|
||||
.pub/
|
||||
build/
|
||||
**/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
|
||||
doc/api/
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
pubspec.lock
|
||||
|
||||
.dart_tool
|
7
packages/merge_map/CHANGELOG.md
Normal file
7
packages/merge_map/CHANGELOG.md
Normal file
|
@ -0,0 +1,7 @@
|
|||
# 1.0.2
|
||||
* Add an example, for Pub's sake.
|
||||
|
||||
# 1.0.1
|
||||
* Add a specific constraint on Dart versions, to prevent Pub from rejecting all packages that depend on
|
||||
`merge_map` (the entire Angel framework).
|
||||
* Add generic type support
|
21
packages/merge_map/LICENSE
Normal file
21
packages/merge_map/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Tobe O
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
19
packages/merge_map/README.md
Normal file
19
packages/merge_map/README.md
Normal file
|
@ -0,0 +1,19 @@
|
|||
# merge_map
|
||||
Combine multiple Maps into one. Equivalent to
|
||||
[Object.assign](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/assign)
|
||||
in JS.
|
||||
|
||||
# Example
|
||||
|
||||
```dart
|
||||
import "package:merge_map/merge_map.dart";
|
||||
|
||||
main() {
|
||||
Map map1 = {'hello': 'world'};
|
||||
Map map2 = {'foo': {'bar': 'baz', 'this': 'will be overwritten'}};
|
||||
Map map3 = {'foo': {'john': 'doe', 'this': 'overrides previous maps'}};
|
||||
Map merged = mergeMap(map1, map2, map3);
|
||||
|
||||
// {hello: world, foo: {bar: baz, john: doe, this: overrides previous maps}}
|
||||
}
|
||||
```
|
11
packages/merge_map/example/main.dart
Normal file
11
packages/merge_map/example/main.dart
Normal file
|
@ -0,0 +1,11 @@
|
|||
import 'package:merge_map/merge_map.dart';
|
||||
|
||||
main() {
|
||||
Map map1 = {'hello': 'world'};
|
||||
Map map2 = {'foo': {'bar': 'baz', 'this': 'will be overwritten'}};
|
||||
Map map3 = {'foo': {'john': 'doe', 'this': 'overrides previous maps'}};
|
||||
Map merged = mergeMap([map1, map2, map3]);
|
||||
print(merged);
|
||||
|
||||
// {hello: world, foo: {bar: baz, john: doe, this: overrides previous maps}}
|
||||
}
|
34
packages/merge_map/lib/merge_map.dart
Normal file
34
packages/merge_map/lib/merge_map.dart
Normal file
|
@ -0,0 +1,34 @@
|
|||
/// Exposes the [mergeMap] function, which... merges Maps.
|
||||
library merge_map;
|
||||
|
||||
_copyValues<K, V>(
|
||||
Map<K, V> from, Map<K, V?>? to, bool recursive, bool acceptNull) {
|
||||
for (var key in from.keys) {
|
||||
if (from[key] is Map<K, V> && recursive) {
|
||||
if (!(to![key] is Map<K, V>)) {
|
||||
to[key] = <K, V>{} as V;
|
||||
}
|
||||
_copyValues(from[key] as Map, to[key] as Map?, recursive, acceptNull);
|
||||
} else {
|
||||
if (from[key] != null || acceptNull) to![key] = from[key];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Merges the values of the given maps together.
|
||||
///
|
||||
/// `recursive` is set to `true` by default. If set to `true`,
|
||||
/// then nested maps will also be merged. Otherwise, nested maps
|
||||
/// will overwrite others.
|
||||
///
|
||||
/// `acceptNull` is set to `false` by default. If set to `false`,
|
||||
/// then if the value on a map is `null`, it will be ignored, and
|
||||
/// that `null` will not be copied.
|
||||
Map<K, V> mergeMap<K, V>(Iterable<Map<K, V>> maps,
|
||||
{bool recursive: true, bool acceptNull: false}) {
|
||||
Map<K, V> result = <K, V>{};
|
||||
maps.forEach((Map<K, V> map) {
|
||||
if (map != null) _copyValues(map, result, recursive, acceptNull);
|
||||
});
|
||||
return result;
|
||||
}
|
9
packages/merge_map/pubspec.yaml
Normal file
9
packages/merge_map/pubspec.yaml
Normal file
|
@ -0,0 +1,9 @@
|
|||
name: merge_map
|
||||
description: Combine multiple Maps into one. Equivalent to Object.assign in JS.
|
||||
version: 3.0.0
|
||||
homepage: https://github.com/thosakwe/merge_map
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
environment:
|
||||
sdk: '>=2.12.0 <3.0.0'
|
||||
dev_dependencies:
|
||||
test: ^1.16.8
|
94
packages/merge_map/test/all_test.dart
Normal file
94
packages/merge_map/test/all_test.dart
Normal file
|
@ -0,0 +1,94 @@
|
|||
import "package:merge_map/merge_map.dart";
|
||||
import "package:test/test.dart";
|
||||
|
||||
void main() {
|
||||
test('can merge two simple maps', () {
|
||||
Map merged = mergeMap([
|
||||
{'hello': 'world'},
|
||||
{'hello': 'dolly'}
|
||||
]);
|
||||
expect(merged['hello'], equals('dolly'));
|
||||
});
|
||||
|
||||
test("the last map's values supersede those of prior", () {
|
||||
Map merged = mergeMap([
|
||||
{'letter': 'a'},
|
||||
{'letter': 'b'},
|
||||
{'letter': 'c'}
|
||||
]);
|
||||
expect(merged['letter'], equals('c'));
|
||||
});
|
||||
|
||||
test("can merge two once-nested maps", () {
|
||||
Map map1 = {
|
||||
'hello': 'world',
|
||||
'foo': {'nested': false}
|
||||
};
|
||||
Map map2 = {
|
||||
'goodbye': 'sad life',
|
||||
'foo': {'nested': true, 'it': 'works'}
|
||||
};
|
||||
Map merged = mergeMap([map1, map2]);
|
||||
|
||||
expect(merged['hello'], equals('world'));
|
||||
expect(merged['goodbye'], equals('sad life'));
|
||||
expect(merged['foo']['nested'], equals(true));
|
||||
expect(merged['foo']['it'], equals('works'));
|
||||
});
|
||||
|
||||
test("once-nested map supersession", () {
|
||||
Map map1 = {
|
||||
'hello': 'world',
|
||||
'foo': {'nested': false}
|
||||
};
|
||||
Map map2 = {
|
||||
'goodbye': 'sad life',
|
||||
'foo': {'nested': true, 'it': 'works'}
|
||||
};
|
||||
Map map3 = {
|
||||
'foo': {'nested': 'supersession'}
|
||||
};
|
||||
|
||||
Map merged = mergeMap([map1, map2, map3]);
|
||||
expect(merged['foo']['nested'], equals('supersession'));
|
||||
});
|
||||
|
||||
test("can merge two twice-nested maps", () {
|
||||
Map map1 = {
|
||||
'a': {
|
||||
'b': {'c': 'd'}
|
||||
}
|
||||
};
|
||||
Map map2 = {
|
||||
'a': {
|
||||
'b': {'c': 'D', 'e': 'f'}
|
||||
}
|
||||
};
|
||||
Map merged = mergeMap([map1, map2]);
|
||||
|
||||
expect(merged['a']['b']['c'], equals('D'));
|
||||
expect(merged['a']['b']['e'], equals('f'));
|
||||
});
|
||||
|
||||
test("twice-nested map supersession", () {
|
||||
Map map1 = {
|
||||
'a': {
|
||||
'b': {'c': 'd'}
|
||||
}
|
||||
};
|
||||
Map map2 = {
|
||||
'a': {
|
||||
'b': {'c': 'D', 'e': 'f'}
|
||||
}
|
||||
};
|
||||
Map map3 = {
|
||||
'a': {
|
||||
'b': {'e': 'supersession'}
|
||||
}
|
||||
};
|
||||
Map merged = mergeMap([map1, map2, map3]);
|
||||
|
||||
expect(merged['a']['b']['c'], equals('D'));
|
||||
expect(merged['a']['b']['e'], equals('supersession'));
|
||||
});
|
||||
}
|
72
packages/mock_request/.gitignore
vendored
Normal file
72
packages/mock_request/.gitignore
vendored
Normal file
|
@ -0,0 +1,72 @@
|
|||
# See https://www.dartlang.org/tools/private-files.html
|
||||
|
||||
# Files and directories created by pub
|
||||
.buildlog
|
||||
.packages
|
||||
.project
|
||||
.pub/
|
||||
build/
|
||||
**/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
|
||||
doc/api/
|
||||
|
||||
# Don't commit pubspec lock file
|
||||
# (Library packages only! Remove pattern if developing an application package)
|
||||
pubspec.lock
|
||||
### 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:
|
||||
.idea/**/workspace.xml
|
||||
.idea/**/tasks.xml
|
||||
.idea/dictionaries
|
||||
|
||||
# Sensitive or high-churn files:
|
||||
.idea/**/dataSources/
|
||||
.idea/**/dataSources.ids
|
||||
.idea/**/dataSources.xml
|
||||
.idea/**/dataSources.local.xml
|
||||
.idea/**/sqlDataSources.xml
|
||||
.idea/**/dynamic.xml
|
||||
.idea/**/uiDesigner.xml
|
||||
|
||||
# Gradle:
|
||||
.idea/**/gradle.xml
|
||||
.idea/**/libraries
|
||||
|
||||
# Mongo Explorer plugin:
|
||||
.idea/**/mongoSettings.xml
|
||||
|
||||
## File-based project format:
|
||||
*.iws
|
||||
|
||||
## Plugin-specific files:
|
||||
|
||||
# IntelliJ
|
||||
/out/
|
||||
|
||||
# mpeltonen/sbt-idea plugin
|
||||
.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
|
||||
.dart_tool
|
1
packages/mock_request/.travis.yml
Normal file
1
packages/mock_request/.travis.yml
Normal file
|
@ -0,0 +1 @@
|
|||
language: dart
|
17
packages/mock_request/CHANGELOG.md
Normal file
17
packages/mock_request/CHANGELOG.md
Normal file
|
@ -0,0 +1,17 @@
|
|||
# 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
|
21
packages/mock_request/LICENSE
Normal file
21
packages/mock_request/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
MIT License
|
||||
|
||||
Copyright (c) 2017 Tobe O
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
25
packages/mock_request/README.md
Normal file
25
packages/mock_request/README.md
Normal file
|
@ -0,0 +1,25 @@
|
|||
# mock_request
|
||||
|
||||
[![Pub](https://img.shields.io/pub/v/mock_request.svg)](https://pub.dartlang.org/packages/mock_request)
|
||||
[![build status](https://travis-ci.org/thosakwe/mock_request.svg)](https://travis-ci.org/thosakwe/mock_request)
|
||||
|
||||
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 testing
|
||||
[Angel](https://github.com/angel-dart/angel/wiki)
|
||||
applications smoother, but works with any Dart-based server. :)
|
||||
|
||||
# Usage
|
||||
```dart
|
||||
var rq = new 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 tests.
|
4
packages/mock_request/analysis_options.yaml
Normal file
4
packages/mock_request/analysis_options.yaml
Normal file
|
@ -0,0 +1,4 @@
|
|||
include: package:pedantic/analysis_options.yaml
|
||||
analyzer:
|
||||
strong-mode:
|
||||
implicit-casts: false
|
7
packages/mock_request/example/main.dart
Normal file
7
packages/mock_request/example/main.dart
Normal file
|
@ -0,0 +1,7 @@
|
|||
import 'dart:async';
|
||||
import 'package:mock_request/mock_request.dart';
|
||||
|
||||
Future<void> main() async {
|
||||
var rq = MockHttpRequest('GET', Uri.parse('/foo'));
|
||||
await rq.close();
|
||||
}
|
6
packages/mock_request/lib/mock_request.dart
Normal file
6
packages/mock_request/lib/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';
|
10
packages/mock_request/lib/src/connection_info.dart
Normal file
10
packages/mock_request/lib/src/connection_info.dart
Normal file
|
@ -0,0 +1,10 @@
|
|||
import 'dart:io';
|
||||
|
||||
class MockHttpConnectionInfo implements HttpConnectionInfo {
|
||||
@override
|
||||
final InternetAddress remoteAddress;
|
||||
@override
|
||||
final int localPort, remotePort;
|
||||
|
||||
MockHttpConnectionInfo({this.remoteAddress, this.localPort, this.remotePort});
|
||||
}
|
151
packages/mock_request/lib/src/headers.dart
Normal file
151
packages/mock_request/lib/src/headers.dart
Normal file
|
@ -0,0 +1,151 @@
|
|||
import 'dart:io';
|
||||
|
||||
class MockHttpHeaders extends HttpHeaders {
|
||||
final Map<String, List<String>> _data = {};
|
||||
final List<String> _noFolding = [];
|
||||
Uri _host;
|
||||
|
||||
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 null;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
set contentType(ContentType value) =>
|
||||
set(HttpHeaders.contentTypeHeader, value.value);
|
||||
|
||||
@override
|
||||
DateTime get date => _data.containsKey(HttpHeaders.dateHeader)
|
||||
? HttpDate.parse(_data[HttpHeaders.dateHeader].join(','))
|
||||
: null;
|
||||
|
||||
@override
|
||||
set date(DateTime value) =>
|
||||
set(HttpHeaders.dateHeader, HttpDate.format(value));
|
||||
|
||||
@override
|
||||
DateTime get expires => _data.containsKey(HttpHeaders.expiresHeader)
|
||||
? HttpDate.parse(_data[HttpHeaders.expiresHeader].join(','))
|
||||
: null;
|
||||
|
||||
@override
|
||||
set expires(DateTime value) =>
|
||||
set(HttpHeaders.expiresHeader, HttpDate.format(value));
|
||||
|
||||
@override
|
||||
DateTime get ifModifiedSince =>
|
||||
_data.containsKey(HttpHeaders.ifModifiedSinceHeader)
|
||||
? HttpDate.parse(_data[HttpHeaders.ifModifiedSinceHeader].join(','))
|
||||
: null;
|
||||
|
||||
@override
|
||||
set ifModifiedSince(DateTime value) =>
|
||||
set(HttpHeaders.ifModifiedSinceHeader, HttpDate.format(value));
|
||||
|
||||
@override
|
||||
String get host {
|
||||
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 {
|
||||
host; // Parse it
|
||||
return _host?.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) f) {
|
||||
_data.forEach(f);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
}
|
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);
|
||||
}
|
||||
}
|
||||
}
|
317
packages/mock_request/lib/src/request.dart
Normal file
317
packages/mock_request/lib/src/request.dart
Normal file
|
@ -0,0 +1,317 @@
|
|||
import 'dart:async';
|
||||
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;
|
||||
BytesBuilder _buf;
|
||||
final Completer _done = Completer();
|
||||
final LockableMockHttpHeaders _headers = LockableMockHttpHeaders();
|
||||
Uri _requestedUri;
|
||||
MockHttpSession _session;
|
||||
final StreamController<Uint8List> _stream = StreamController<Uint8List>();
|
||||
|
||||
@override
|
||||
final List<Cookie> cookies = [];
|
||||
|
||||
@override
|
||||
HttpConnectionInfo connectionInfo =
|
||||
MockHttpConnectionInfo(remoteAddress: InternetAddress.loopbackIPv4);
|
||||
|
||||
@override
|
||||
MockHttpResponse response = MockHttpResponse();
|
||||
|
||||
@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}) {
|
||||
_buf = BytesBuilder(copy: copyBuffer != false);
|
||||
_session = MockHttpSession(id: sessionId ?? 'mock-http-session');
|
||||
this.protocolVersion =
|
||||
protocolVersion?.isNotEmpty == true ? 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
|
||||
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);
|
||||
}
|
150
packages/mock_request/lib/src/response.dart
Normal file
150
packages/mock_request/lib/src/response.dart
Normal file
|
@ -0,0 +1,150 @@
|
|||
import 'dart:async';
|
||||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
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,
|
||||
this.statusCode,
|
||||
this.reasonPhrase,
|
||||
this.contentLength,
|
||||
this.deadline,
|
||||
this.encoding,
|
||||
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 ?? HttpStatus.movedTemporarily;
|
||||
}
|
||||
|
||||
@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({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) f) {
|
||||
_data.forEach(f);
|
||||
}
|
||||
|
||||
@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.');
|
||||
}
|
||||
}
|
13
packages/mock_request/pubspec.yaml
Normal file
13
packages/mock_request/pubspec.yaml
Normal file
|
@ -0,0 +1,13 @@
|
|||
name: mock_request
|
||||
version: 2.0.0
|
||||
description: Manufacture dart:io HttpRequests, HttpResponses, HttpHeaders, etc.
|
||||
author: Tobe O <thosakwe@gmail.com>
|
||||
homepage: https://github.com/thosakwe/mock_request
|
||||
environment:
|
||||
sdk: ">=2.0.0 <3.0.0"
|
||||
dependencies:
|
||||
charcode: ">=1.0.0 <2.0.0"
|
||||
dev_dependencies:
|
||||
angel_framework: ^2.1.0
|
||||
http: ^0.12.0
|
||||
test: ^1.16.8
|
66
packages/mock_request/test/all_test.dart
Normal file
66
packages/mock_request/test/all_test.dart
Normal file
|
@ -0,0 +1,66 @@
|
|||
import 'dart:convert';
|
||||
import 'dart:io';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_framework/http.dart';
|
||||
import 'package:mock_request/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