From efbb09169a4c7f35dea1fe7be29b34089b6576be Mon Sep 17 00:00:00 2001 From: Tobe O Date: Wed, 10 Apr 2019 13:52:45 -0400 Subject: [PATCH] Body parsing tests; allow setting body ONCE --- CHANGELOG.md | 1 + lib/src/core/request_context.dart | 25 ++++++++ test/body_test.dart | 99 +++++++++++++++++++++++++++++++ 3 files changed, 125 insertions(+) create mode 100644 test/body_test.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index 71ccd172..3f6ccd62 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * Add `AngelEnvironment` class. * Add `Angel.environment`. * Deprecated `app.isProduction` in favor of `app.environment.isProduction`. +* Allow setting of `bodyAsObject`, `bodyAsMap`, or `bodyAsList` **exactly once**. # 2.0.0-alpha.24 * Add `AngelEnv` class to `core`. diff --git a/lib/src/core/request_context.dart b/lib/src/core/request_context.dart index 1f9a81a9..8d0cc337 100644 --- a/lib/src/core/request_context.dart +++ b/lib/src/core/request_context.dart @@ -113,6 +113,11 @@ abstract class RequestContext { return _bodyFields; } + /// This setter allows you to explicitly set the request body **exactly once**. + /// + /// Use this if the format of the body is not natively parsed by Angel. + set bodyAsMap(Map value) => bodyAsObject = value; + /// Returns a *mutable* [List] parsed from the request [body]. /// /// Note that [parseBody] must be called first. @@ -126,6 +131,11 @@ abstract class RequestContext { return _bodyList; } + /// This setter allows you to explicitly set the request body **exactly once**. + /// + /// Use this if the format of the body is not natively parsed by Angel. + set bodyAsList(List value) => bodyAsObject = value; + /// Returns the parsed request body, whatever it may be (typically a [Map] or [List]). /// /// Note that [parseBody] must be called first. @@ -137,6 +147,21 @@ abstract class RequestContext { return _bodyObject; } + /// This setter allows you to explicitly set the request body **exactly once**. + /// + /// Use this if the format of the body is not natively parsed by Angel. + set bodyAsObject(value) { + if (_bodyObject != null) { + throw StateError( + 'The request body has already been parsed/set, and cannot be overwritten.'); + } else { + if (value is List) _bodyList = value; + if (value is Map) _bodyFields = value; + _bodyObject = value; + _hasParsedBody = true; + } + } + /// Returns a *mutable* map of the files parsed from the request [body]. /// /// Note that [parseBody] must be called first. diff --git a/test/body_test.dart b/test/body_test.dart new file mode 100644 index 00000000..3c085df1 --- /dev/null +++ b/test/body_test.dart @@ -0,0 +1,99 @@ +import 'dart:async'; +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 app = Angel(); + var http = AngelHttp(app); + + Future request( + {bool asJson: true, + bool parse: true, + Map bodyFields, + List bodyList}) async { + var rq = MockHttpRequest('POST', Uri(path: '/')); + + if (bodyFields != null) { + if (asJson) { + rq + ..headers.contentType = ContentType('application', 'json') + ..write(json.encode(bodyFields)); + } else { + var b = StringBuffer(); + var i = 0; + for (var entry in bodyFields.entries) { + if (i++ > 0) b.write('&'); + b.write(entry.key); + b.write('='); + b.write(Uri.encodeComponent(entry.value.toString())); + } + + rq + ..headers.contentType = + ContentType('application', 'x-www-form-urlencoded') + ..write(json.encode(b.toString())); + } + } else if (bodyList != null) { + rq + ..headers.contentType = ContentType('application', 'json') + ..write(json.encode(bodyList)); + } + + await rq.close(); + var req = await http.createRequestContext(rq, rq.response); + if (parse) await req.parseBody(); + return req; + } + + test('parses json maps', () async { + var req = await request(bodyFields: {'hello': 'world'}); + expect(req.bodyAsObject, TypeMatcher>()); + expect(req.bodyAsMap, {'hello': 'world'}); + }); + + test('parses json lists', () async { + var req = await request(bodyList: ['foo', 'bar']); + expect(req.bodyAsObject, TypeMatcher()); + expect(req.bodyAsList, ['foo', 'bar']); + }); + + test('throws when body has not been parsed', () async { + var req = await request(parse: false); + expect(() => req.bodyAsObject, throwsStateError); + expect(() => req.bodyAsMap, throwsStateError); + expect(() => req.bodyAsList, throwsStateError); + }); + + test('can set body object exactly once', () async { + var req = await request(parse: false); + req.bodyAsObject = 23; + expect(req.bodyAsObject, 23); + expect(() => req.bodyAsObject = {45.6: '34'}, throwsStateError); + }); + + test('can set body map exactly once', () async { + var req = await request(parse: false); + req.bodyAsMap = {'hey': 'yes'}; + expect(req.bodyAsMap, {'hey': 'yes'}); + expect(() => req.bodyAsMap = {'hm': 'ok'}, throwsStateError); + }); + + test('can set body list exactly once', () async { + var req = await request(parse: false); + req.bodyAsList = [ + {'hey': 'yes'} + ]; + expect(req.bodyAsList, [ + {'hey': 'yes'} + ]); + expect( + () => req.bodyAsList = [ + {'hm': 'ok'} + ], + throwsStateError); + }); +}