fluentasserts.vibe.request 189/197(95%) line coverage

      
10
20
30
40
50
60
70
80
90
100
110
120
130
140
150
160
170
180
190
2025
210
220
230
240
250
260
270
280
290
300
310
320
330
340
350
360
370
3825
390
4025
410
420
430
440
450
460
470
480
490
500
510
520
530
540
550
560
571
581
590
600
610
621
631
641
651
660
670
680
690
700
710
720
730
740
751
760
770
781
790
801
810
820
830
840
857
860
870
880
890
901
910
920
930
940
951
960
970
980
990
1001
1010
1020
1030
1040
10514
1060
1070
1080
1090
1101
1110
1120
1130
1140
11525
11625
1170
11825
1190
1200
1210
12225
1230
1240
1250
1260
1277
1289
1290
1300
1317
1320
1337
1340
1350
1360
1370
1383
1390
1403
1412
1421
1430
1440
1450
1463
1470
1483
1490
1500
1510
1520
1532
1540
1552
1562
1571
1580
1590
1600
1612
1620
1632
1640
1650
1660
1670
1682
1694
1701
1712
1720
1730
1742
1751
1760
1770
1780
1790
1801
1810
1820
1830
1842
1850
1860
1872
1880
1890
1900
1910
192104
19313
1940
1950
1960
1970
19824
1990
2000
2010
2020
2030
2040
2050
2060
20725
2080
20925
21025
21125
2120
2130
21425
2150
21625
2170
21825
21925
2200
22125
2220
22325
2240
22525
2260
2270
2280
22925
23013
2310
2320
23312
2340
2350
2360
2370
2380
2390
2400
2410
2420
2430
2440
2450
2460
2470
2480
2490
2500
25125
2520
25325
2540
25525
2560
25725
2580
25925
2600
26125
26225
2630
264168
2650
26631
26731
2680
2690
27025
2710
2720
2730
2740
2751
2760
2770
2781
2790
2800
2810
2820
2830
2841
2850
2860
2870
2880
2890
2900
2910
2920
2930
2940
2951
2960
2971
2980
2991
3000
3010
3021
3031
3040
3051
3062
3070
3080
3091
3100
3111
3122
3130
3140
3150
3160
3170
3181
3190
3201
3210
3221
3230
3240
3251
3261
3270
3281
3292
3300
3310
3321
3330
3341
3352
3360
3370
3380
3390
3400
3411
3420
3431
3440
3451
3460
3470
3481
3491
3500
3511
3522
3530
3540
3551
3560
3571
3582
3590
3600
3610
3620
3630
3641
3650
3661
3670
3681
3690
3700
3711
3721
3730
3741
3752
3760
3770
3781
3790
3801
3812
3820
3830
3840
3850
3860
3871
3880
3891
3900
3911
3920
3930
3941
3951
3960
3971
3982
3990
4000
4011
4020
4031
4042
4050
4060
4070
4080
4090
4101
4110
4121
4130
4141
4150
4160
4171
4180
4191
4200
4211
4222
4230
4240
4251
4260
4271
4282
4290
4300
4310
4320
4330
4341
4350
4361
4370
4382
4390
4400
4411
4420
4431
4440
4450
4460
4470
4480
4490
4500
4510
4520
4531
4540
4551
4560
4572
4580
4590
4601
4610
4621
4630
4640
4650
4660
4670
4680
4690
4700
4710
4720
4730
4740
4750
4760
4770
4780
4790
4800
4810
4820
4830
4840
4850
4860
4870
4880
4890
4900
4911
4920
4931
4940
4952
4960
4970
4981
4990
5001
5010
5020
5030
5040
5050
5060
5070
5081
5090
5101
5110
5121
5130
5140
5151
5160
5171
5180
5191
5202
5210
5220
5230
5240
5250
5261
5270
5281
5290
5301
5311
5320
5330
5341
5350
5361
5370
5380
5390
5400
5410
5423
5431
5440
5450
5460
5471
5480
5490
5500
5510
5520
5531
5540
5551
5560
5575
5585
5590
5600
5611
5620
5630
5640
5651
5660
5670
5680
5690
5700
5713
5721
5730
5740
5750
5761
5770
5783
5791
5800
5810
5820
5831
5840
5850
5861
5870
5880
5890
5900
5910
5923
5931
5940
5950
5960
5971
5980
5990
6001
6010
6020
6030
6040
6053
6061
6070
6080
6090
6101
6110
module fluentasserts.vibe.request; import vibe.inet.url; import vibe.http.router; import vibe.http.form; import vibe.data.json; import vibe.stream.memory; import std.conv, std.string, std.array; import std.algorithm, std.conv; import std.stdio; import std.exception; import fluentasserts.core.base; import fluentasserts.core.results; RequestRouter request(URLRouter router) { return new RequestRouter(router); } final class RequestRouter { private { alias ExpectedCallback = void delegate(Response res); ExpectedCallback[] expected; URLRouter router; HTTPServerRequest preparedRequest; string[string] headers; string responseBody; string requestBody; } this(URLRouter router) { this.router = router; } RequestRouter send(string[string] data) { auto dst = appender!string; dst.writeFormData(data); header("Content-Type", "application/x-www-form-urlencoded"); return send(dst.data); } RequestRouter send(T)(T data) { static if (is(T == string)) { requestBody = data; return this; } else static if (is(T == Json)) { requestBody = data.toPrettyString; preparedRequest.bodyReader = createMemoryStream(cast(ubyte[]) requestBody); preparedRequest.json = data; return this; } else { return send(data.serializeToJson()); } } RequestRouter header(string name, string value) { if(preparedRequest is null) { headers[name] = value; } else { preparedRequest.headers[name] = value; } return this; } RequestRouter post(string path) { return customMethod!(HTTPMethod.POST)(URL("http://localhost" ~ path)); } RequestRouter patch(string path) { return customMethod!(HTTPMethod.PATCH)(URL("http://localhost" ~ path)); } RequestRouter put(string path) { return customMethod!(HTTPMethod.PUT)(URL("http://localhost" ~ path)); } RequestRouter delete_(string path) { return customMethod!(HTTPMethod.DELETE)(URL("http://localhost" ~ path)); } RequestRouter get(string path) { return customMethod!(HTTPMethod.GET)(URL("http://localhost" ~ path)); } RequestRouter customMethod(HTTPMethod method)(string path) { return customMethod!method(URL("http://localhost" ~ path)); } RequestRouter customMethod(HTTPMethod method)(URL url) { preparedRequest = createTestHTTPServerRequest(url, method); preparedRequest.host = "localhost"; foreach(name, value; headers) { preparedRequest.headers[name] = value; } return this; } RequestRouter expectHeaderExist(string name, const string file = __FILE__, const size_t line = __LINE__) { void localExpectHeaderExist(Response res) { enforce(name in res.headers, "Response header `" ~ name ~ "` is missing.", file, line); } expected ~= &localExpectHeaderExist; return this; } RequestRouter expectHeader(string name, string value, const string file = __FILE__, const size_t line = __LINE__) { expectHeaderExist(name, file, line); void localExpectedHeader(Response res) { enforce(res.headers[name] == value, "Response header `" ~ name ~ "` has an unexpected value. Expected `" ~ value ~ "` != `" ~ res.headers[name].to!string ~ "`", file, line); } expected ~= &localExpectedHeader; return this; } RequestRouter expectHeaderContains(string name, string value, const string file = __FILE__, const size_t line = __LINE__) { expectHeaderExist(name, file, line); void expectHeaderContains(Response res) { enforce(res.headers[name].indexOf(value) != -1, "Response header `" ~ name ~ "` has an unexpected value. Expected `" ~ value ~ "` not found in `" ~ res.headers[name].to!string ~ "`", file, line); } expected ~= &expectHeaderContains; return this; } RequestRouter expectStatusCode(int code, const string file = __FILE__, const size_t line = __LINE__) { void localExpectStatusCode(Response res) { if(code != 404 && res.statusCode == 404) { writeln("\n\nIs your route defined here?"); router.getAllRoutes.map!(a => a.method.to!string ~ " " ~ a.pattern).each!writeln; } if(code != res.statusCode) { IResult[] results = [ cast(IResult) new MessageResult("Invalid status code."), cast(IResult) new ExpectedActualResult(code.to!string ~ " - " ~ httpStatusText(code), res.statusCode.to!string ~ " - " ~ httpStatusText(res.statusCode)), cast(IResult) new SourceResult(file, line) ]; throw new TestException(results, file, line); } } expected ~= &localExpectStatusCode; return this; } private void performExpected(Response res) { foreach(func; expected) { func(res); } } void end() { end((Response response) => { }); } void end(T)(T callback) { import vibe.stream.operations : readAllUTF8; import vibe.inet.webform; import vibe.stream.memory; auto data = new ubyte[5000]; MemoryStream stream = createMemoryStream(data); HTTPServerResponse res = createTestHTTPServerResponse(stream); res.statusCode = 404; import vibe.stream.memory; preparedRequest.bodyReader = createMemoryStream(cast(ubyte[]) requestBody); router.handleRequest(preparedRequest, res); string responseString = (cast(string) data).toStringz.to!string; checkResponse(responseString); auto response = new Response(responseString); callback(response)(); performExpected(response); } void checkResponse(ref string data) { if(data.length > 0) { return; } data = "HTTP/1.1 404 No Content\r\n\r\n"; } } class Response { string bodyString; private { Json _bodyJson; string responseLine; string data; } string[string] headers; int statusCode; this(string data) { this.data = data; auto bodyIndex = data.indexOf("\r\n\r\n"); assert(bodyIndex != -1, "Invalid response data: \n" ~ data ~ "\n\n"); auto headers = data[0 .. bodyIndex].split("\r\n").array; responseLine = headers[0]; statusCode = headers[0].split(" ")[1].to!int; foreach (i; 1 .. headers.length) { auto header = headers[i].split(": "); this.headers[header[0]] = header[1]; } bodyString = data[bodyIndex + 4 .. $]; } @property Json bodyJson() { if (_bodyJson.type == Json.Type.undefined) { try { _bodyJson = bodyString.parseJson; } catch(Exception e) { writeln("`" ~ bodyString ~ "` is not a json string"); } } return _bodyJson; } override string toString() { return data; } } @("Mocking a GET Request") unittest { auto router = new URLRouter(); void sayHello(HTTPServerRequest req, HTTPServerResponse res) { res.writeBody("hello"); } router.get("*", &sayHello); request(router) .get("/") .end((Response response) => { response.bodyString.should.equal("hello"); }); request(router) .post("/") .end((Response response) => { response.bodyString.should.not.equal("hello"); }); } @("Mocking a POST Request") unittest { auto router = new URLRouter(); void sayHello(HTTPServerRequest req, HTTPServerResponse res) { res.writeBody("hello"); } router.post("*", &sayHello); request(router) .post("/") .end((Response response) => { response.bodyString.should.equal("hello"); }); request(router) .get("/") .end((Response response) => { response.bodyString.should.not.equal("hello"); }); } @("Mocking a PATCH Request") unittest { auto router = new URLRouter(); void sayHello(HTTPServerRequest req, HTTPServerResponse res) { res.writeBody("hello"); } router.patch("*", &sayHello); request(router) .patch("/") .end((Response response) => { response.bodyString.should.equal("hello"); }); request(router) .get("/") .end((Response response) => { response.bodyString.should.not.equal("hello"); }); } @("Mocking a PUT Request") unittest { auto router = new URLRouter(); void sayHello(HTTPServerRequest req, HTTPServerResponse res) { res.writeBody("hello"); } router.put("*", &sayHello); request(router) .put("/") .end((Response response) => { response.bodyString.should.equal("hello"); }); request(router) .get("/") .end((Response response) => { response.bodyString.should.not.equal("hello"); }); } @("Mocking a DELETE Request") unittest { auto router = new URLRouter(); void sayHello(HTTPServerRequest req, HTTPServerResponse res) { res.writeBody("hello"); } router.delete_("*", &sayHello); request(router) .delete_("/") .end((Response response) => { response.bodyString.should.equal("hello"); }); request(router) .get("/") .end((Response response) => { response.bodyString.should.not.equal("hello"); }); } @("Mocking a ACL Request") unittest { auto router = new URLRouter(); void sayHello(HTTPServerRequest, HTTPServerResponse res) { res.writeBody("hello"); } router.match(HTTPMethod.ACL, "*", &sayHello); request(router) .customMethod!(HTTPMethod.ACL)("/") .end((Response response) => { response.bodyString.should.equal("hello"); }); request(router) .get("/") .end((Response response) => { response.bodyString.should.not.equal("hello"); }); } @("Sending headers") unittest { auto router = new URLRouter(); void checkHeaders(HTTPServerRequest req, HTTPServerResponse) { req.headers["Accept"].should.equal("application/json"); } router.any("*", &checkHeaders); request(router) .get("/") .header("Accept", "application/json") .end(); } @("Sending raw string") unittest { import std.string; auto router = new URLRouter(); void checkStringData(HTTPServerRequest req, HTTPServerResponse) { req.bodyReader.peek.assumeUTF.to!string.should.equal("raw string"); } router.any("*", &checkStringData); request(router) .post("/") .send("raw string") .end(); } /* @("Sending form data") unittest { auto router = new URLRouter(); void checkFormData(HTTPServerRequest req, HTTPServerResponse) { req.headers["content-type"].should.equal("application/x-www-form-urlencoded"); req.form["key1"].should.equal("value1"); req.form["key2"].should.equal("value2"); } router.any("*", &checkFormData); request(router) .post("/") .send(["key1": "value1", "key2": "value2"]) .end(); }*/ @("Sending json data") unittest { auto router = new URLRouter(); void checkJsonData(HTTPServerRequest req, HTTPServerResponse) { req.json["key"].to!string.should.equal("value"); } router.any("*", &checkJsonData); request(router) .post("/") .send(`{ "key": "value" }`.parseJsonString) .end(); } @("Receive json data") unittest { auto router = new URLRouter(); void respondJsonData(HTTPServerRequest, HTTPServerResponse res) { res.writeJsonBody(`{ "key": "value"}`.parseJsonString); } router.any("*", &respondJsonData); request(router) .get("/") .end((Response response) => { response.bodyJson["key"].to!string.should.equal("value"); }); } @("Expect status code") unittest { auto router = new URLRouter(); void respondStatus(HTTPServerRequest, HTTPServerResponse res) { res.statusCode = 200; res.writeBody(""); } router.get("*", &respondStatus); request(router) .get("/") .expectStatusCode(200) .end(); ({ request(router) .post("/") .expectStatusCode(200) .end(); }).should.throwException!TestException.msg.should.startWith("Invalid status code."); } @("Expect header") unittest { auto router = new URLRouter(); void respondHeader(HTTPServerRequest, HTTPServerResponse res) { res.headers["some-header"] = "some-value"; res.writeBody(""); } router.get("*", &respondHeader); // Check for the exact header value: request(router) .get("/") .expectHeader("some-header", "some-value") .end(); ({ request(router) .post("/") .expectHeader("some-header", "some-value") .end(); }).should.throwAnyException.msg.should.equal("Response header `some-header` is missing."); ({ request(router) .get("/") .expectHeader("some-header", "other-value") .end(); }).should.throwAnyException.msg.should.contain("Response header `some-header` has an unexpected value"); // Check if a header exists request(router) .get("/") .expectHeaderExist("some-header") .end(); ({ request(router) .post("/") .expectHeaderExist("some-header") .end(); }).should.throwAnyException.msg.should.equal("Response header `some-header` is missing."); // Check if a header contains a string request(router) .get("/") .expectHeaderContains("some-header", "value") .end(); ({ request(router) .get("/") .expectHeaderContains("some-header", "other") .end(); }).should.throwAnyException.msg.should.contain("Response header `some-header` has an unexpected value."); }