1 /** 2 * Copyright © DiamondMVC 2019 3 * License: MIT (https://github.com/DiamondMVC/Diamond/blob/master/LICENSE) 4 * Author: Jacob Jensen (bausshf) 5 */ 6 module diamond.unittesting.request; 7 8 import diamond.core.apptype; 9 10 static if (isWeb && isTesting) 11 { 12 import std.conv : to; 13 14 import vibe.d : HTTPClientRequest, HTTPClientResponse, requestHTTP, HTTPMethod, Json; 15 16 public import diamond.http.method; 17 public import diamond.http.status; 18 19 /// Wrapper around a http unittet result. 20 final class HttpUnitTestResult 21 { 22 private: 23 /// The response. 24 HTTPClientResponse _response; 25 26 /// The body data. 27 string _bodyData; 28 29 /** 30 * Creates a new http unittest result. 31 * Params: 32 * response = The response. 33 */ 34 this(HTTPClientResponse response) 35 { 36 _response = response; 37 } 38 39 public: 40 final: 41 @property 42 { 43 /// Gets the raw vibe.d response. 44 HTTPClientResponse rawResponse() { return _response; } 45 46 /// Gets the raw vibe.d response stream. 47 auto responseStream() { return _response.bodyReader; } 48 49 /// Gets the status code. 50 HttpStatus statusCode() { return cast(HttpStatus)_response.statusCode; } 51 52 /// Gets the content type. 53 string contentType() { return _response.contentType; } 54 55 /// Gets the json. 56 auto json() { return _response.readJson(); } 57 58 /// Gets the body data. 59 auto bodyData() 60 { 61 import vibe.stream.operations : readAllUTF8; 62 63 if (!_bodyData) 64 { 65 _bodyData = _response.bodyReader.readAllUTF8(); 66 } 67 68 return _bodyData; 69 } 70 } 71 72 /** 73 * Gets a cookie. 74 * Params: 75 * name = The name of the cookie. 76 * defaultValue = The default value. 77 * Returns: 78 * The value of the cookie if present, default value otherwise. 79 */ 80 string getCookie(string name, lazy string defaultValue = null) 81 { 82 if (name !in _response.cookies) 83 { 84 return defaultValue; 85 } 86 87 return _response.cookies[name].value; 88 } 89 90 /** 91 * Checks whether a cookie is present or not. 92 * Params: 93 * name = The name of the cookie. 94 * Returns: 95 * True if the cookie is present, false otherwise. 96 */ 97 bool hasCookie(string name) 98 { 99 return cast(bool)(name in _response.cookies); 100 } 101 102 /** 103 * Gets a header. 104 * Params: 105 * name = The name of the header. 106 * defaultValue = The default value. 107 * Returns: 108 * The value if present, default value otherwise. 109 */ 110 string getHeader(string name, lazy string defaultValue) 111 { 112 return _response.headers.get(name, defaultValue); 113 } 114 115 /** 116 * Checks whether a header is present or not. 117 * Params: 118 * name = The name of the header. 119 * Returns: 120 * True if the header is present, false otherwise. 121 */ 122 bool hasHeader(string name) 123 { 124 return getHeader(name, null) !is null; 125 } 126 127 /// Gets a model from the response's json. 128 T getModelFromJson(T, CTORARGS...)(CTORARGS args) 129 { 130 import vibe.data.json; 131 132 static if (is(T == struct)) 133 { 134 T value; 135 136 value.deserializeJson(json); 137 138 return value; 139 } 140 else static if (is(T == class)) 141 { 142 auto value = new T(args); 143 144 value.deserializeJson(json); 145 146 return value; 147 } 148 else 149 { 150 static assert(0); 151 } 152 } 153 } 154 155 /** 156 * Creates an internal test request. 157 * Params: 158 * route = The route (not URL!) to call. 159 * method = The method to use for thr request. 160 * responder = The handler for the unittest result. 161 * requester = Custom handler for the raw vibe.d request. Can be used to setup the request data etc. 162 */ 163 void testRequest 164 ( 165 string route, HttpMethod method, 166 scope void delegate(scope HttpUnitTestResult) responder, 167 scope void delegate(scope HTTPClientRequest) requester = null, 168 ) 169 { 170 import diamond.errors.checks; 171 172 enforce(responder !is null, "No responder defined."); 173 174 import diamond.core.webconfig; 175 auto address = webConfig.addresses[0]; 176 177 if (route[0] != '/') 178 { 179 route = "/" ~ route; 180 } 181 182 auto ipAddress = address.ipAddresses[0]; 183 184 if (ipAddress == "::1") 185 { 186 ipAddress = "127.0.0.1"; 187 } 188 189 auto url = "http://" ~ ipAddress ~ ":" ~ to!string(address.port) ~ route; 190 191 requestHTTP 192 ( 193 url, 194 (scope request) 195 { 196 request.method = cast(HTTPMethod)method; 197 198 if (requester !is null) 199 { 200 requester(request); 201 } 202 }, 203 (scope response) 204 { 205 responder(new HttpUnitTestResult(response)); 206 } 207 ); 208 } 209 }