1 //! Various helpers for Actix applications to use during testing.
2
3 use std::{net::SocketAddr, rc::Rc};
4
5 pub use actix_http::test::TestBuffer;
6 use actix_http::{
7 body,
8 http::{header::IntoHeaderPair, Method, StatusCode, Uri, Version},
9 test::TestRequest as HttpTestRequest,
10 Extensions, Request,
11 };
12 use actix_router::{Path, ResourceDef, Url};
13 use actix_service::{IntoService, IntoServiceFactory, Service, ServiceFactory};
14 use actix_utils::future::{ok, poll_fn};
15 use futures_core::Stream;
16 use futures_util::StreamExt as _;
17 use serde::{de::DeserializeOwned, Serialize};
18
19 #[cfg(feature = "cookies")]
20 use crate::cookie::{Cookie, CookieJar};
21 use crate::{
22 app_service::AppInitServiceState,
23 config::AppConfig,
24 data::Data,
25 dev::{Body, MessageBody, Payload},
26 http::header::ContentType,
27 rmap::ResourceMap,
28 service::{ServiceRequest, ServiceResponse},
29 web::{Bytes, BytesMut},
30 Error, HttpRequest, HttpResponse, HttpResponseBuilder,
31 };
32
33 /// Create service that always responds with `HttpResponse::Ok()` and no body.
ok_service( ) -> impl Service<ServiceRequest, Response = ServiceResponse<Body>, Error = Error>34 pub fn ok_service(
35 ) -> impl Service<ServiceRequest, Response = ServiceResponse<Body>, Error = Error> {
36 default_service(StatusCode::OK)
37 }
38
39 /// Create service that always responds with given status code and no body.
default_service( status_code: StatusCode, ) -> impl Service<ServiceRequest, Response = ServiceResponse<Body>, Error = Error>40 pub fn default_service(
41 status_code: StatusCode,
42 ) -> impl Service<ServiceRequest, Response = ServiceResponse<Body>, Error = Error> {
43 (move |req: ServiceRequest| {
44 ok(req.into_response(HttpResponseBuilder::new(status_code).finish()))
45 })
46 .into_service()
47 }
48
49 /// Initialize service from application builder instance.
50 ///
51 /// ```
52 /// use actix_service::Service;
53 /// use actix_web::{test, web, App, HttpResponse, http::StatusCode};
54 ///
55 /// #[actix_rt::test]
56 /// async fn test_init_service() {
57 /// let app = test::init_service(
58 /// App::new()
59 /// .service(web::resource("/test").to(|| async { HttpResponse::Ok() }))
60 /// ).await;
61 ///
62 /// // Create request object
63 /// let req = test::TestRequest::with_uri("/test").to_request();
64 ///
65 /// // Execute application
66 /// let resp = app.call(req).await.unwrap();
67 /// assert_eq!(resp.status(), StatusCode::OK);
68 /// }
69 /// ```
init_service<R, S, B, E>( app: R, ) -> impl Service<Request, Response = ServiceResponse<B>, Error = E> where R: IntoServiceFactory<S, Request>, S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>, S::InitError: std::fmt::Debug,70 pub async fn init_service<R, S, B, E>(
71 app: R,
72 ) -> impl Service<Request, Response = ServiceResponse<B>, Error = E>
73 where
74 R: IntoServiceFactory<S, Request>,
75 S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>,
76 S::InitError: std::fmt::Debug,
77 {
78 try_init_service(app)
79 .await
80 .expect("service initialization failed")
81 }
82
83 /// Fallible version of [`init_service`] that allows testing initialization errors.
try_init_service<R, S, B, E>( app: R, ) -> Result<impl Service<Request, Response = ServiceResponse<B>, Error = E>, S::InitError> where R: IntoServiceFactory<S, Request>, S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>, S::InitError: std::fmt::Debug,84 pub(crate) async fn try_init_service<R, S, B, E>(
85 app: R,
86 ) -> Result<impl Service<Request, Response = ServiceResponse<B>, Error = E>, S::InitError>
87 where
88 R: IntoServiceFactory<S, Request>,
89 S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>,
90 S::InitError: std::fmt::Debug,
91 {
92 let srv = app.into_factory();
93 srv.new_service(AppConfig::default()).await
94 }
95
96 /// Calls service and waits for response future completion.
97 ///
98 /// ```
99 /// use actix_web::{test, web, App, HttpResponse, http::StatusCode};
100 ///
101 /// #[actix_rt::test]
102 /// async fn test_response() {
103 /// let app = test::init_service(
104 /// App::new()
105 /// .service(web::resource("/test").to(|| async {
106 /// HttpResponse::Ok()
107 /// }))
108 /// ).await;
109 ///
110 /// // Create request object
111 /// let req = test::TestRequest::with_uri("/test").to_request();
112 ///
113 /// // Call application
114 /// let resp = test::call_service(&app, req).await;
115 /// assert_eq!(resp.status(), StatusCode::OK);
116 /// }
117 /// ```
call_service<S, R, B, E>(app: &S, req: R) -> S::Response where S: Service<R, Response = ServiceResponse<B>, Error = E>, E: std::fmt::Debug,118 pub async fn call_service<S, R, B, E>(app: &S, req: R) -> S::Response
119 where
120 S: Service<R, Response = ServiceResponse<B>, Error = E>,
121 E: std::fmt::Debug,
122 {
123 app.call(req).await.unwrap()
124 }
125
126 /// Helper function that returns a response body of a TestRequest
127 ///
128 /// ```
129 /// use actix_web::{test, web, App, HttpResponse, http::header};
130 /// use bytes::Bytes;
131 ///
132 /// #[actix_rt::test]
133 /// async fn test_index() {
134 /// let app = test::init_service(
135 /// App::new().service(
136 /// web::resource("/index.html")
137 /// .route(web::post().to(|| async {
138 /// HttpResponse::Ok().body("welcome!")
139 /// })))
140 /// ).await;
141 ///
142 /// let req = test::TestRequest::post()
143 /// .uri("/index.html")
144 /// .header(header::CONTENT_TYPE, "application/json")
145 /// .to_request();
146 ///
147 /// let result = test::read_response(&app, req).await;
148 /// assert_eq!(result, Bytes::from_static(b"welcome!"));
149 /// }
150 /// ```
read_response<S, B>(app: &S, req: Request) -> Bytes where S: Service<Request, Response = ServiceResponse<B>, Error = Error>, B: MessageBody + Unpin, B::Error: Into<Error>,151 pub async fn read_response<S, B>(app: &S, req: Request) -> Bytes
152 where
153 S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
154 B: MessageBody + Unpin,
155 B::Error: Into<Error>,
156 {
157 let resp = app
158 .call(req)
159 .await
160 .unwrap_or_else(|e| panic!("read_response failed at application call: {}", e));
161
162 let body = resp.into_body();
163 let mut bytes = BytesMut::new();
164
165 actix_rt::pin!(body);
166 while let Some(item) = poll_fn(|cx| body.as_mut().poll_next(cx)).await {
167 bytes.extend_from_slice(&item.map_err(Into::into).unwrap());
168 }
169
170 bytes.freeze()
171 }
172
173 /// Helper function that returns a response body of a ServiceResponse.
174 ///
175 /// ```
176 /// use actix_web::{test, web, App, HttpResponse, http::header};
177 /// use bytes::Bytes;
178 ///
179 /// #[actix_rt::test]
180 /// async fn test_index() {
181 /// let app = test::init_service(
182 /// App::new().service(
183 /// web::resource("/index.html")
184 /// .route(web::post().to(|| async {
185 /// HttpResponse::Ok().body("welcome!")
186 /// })))
187 /// ).await;
188 ///
189 /// let req = test::TestRequest::post()
190 /// .uri("/index.html")
191 /// .header(header::CONTENT_TYPE, "application/json")
192 /// .to_request();
193 ///
194 /// let resp = test::call_service(&app, req).await;
195 /// let result = test::read_body(resp).await;
196 /// assert_eq!(result, Bytes::from_static(b"welcome!"));
197 /// }
198 /// ```
read_body<B>(res: ServiceResponse<B>) -> Bytes where B: MessageBody + Unpin, B::Error: Into<Error>,199 pub async fn read_body<B>(res: ServiceResponse<B>) -> Bytes
200 where
201 B: MessageBody + Unpin,
202 B::Error: Into<Error>,
203 {
204 let body = res.into_body();
205 let mut bytes = BytesMut::new();
206
207 actix_rt::pin!(body);
208 while let Some(item) = poll_fn(|cx| body.as_mut().poll_next(cx)).await {
209 bytes.extend_from_slice(&item.map_err(Into::into).unwrap());
210 }
211
212 bytes.freeze()
213 }
214
215 /// Helper function that returns a deserialized response body of a ServiceResponse.
216 ///
217 /// ```
218 /// use actix_web::{App, test, web, HttpResponse, http::header};
219 /// use serde::{Serialize, Deserialize};
220 ///
221 /// #[derive(Serialize, Deserialize)]
222 /// pub struct Person {
223 /// id: String,
224 /// name: String,
225 /// }
226 ///
227 /// #[actix_rt::test]
228 /// async fn test_post_person() {
229 /// let app = test::init_service(
230 /// App::new().service(
231 /// web::resource("/people")
232 /// .route(web::post().to(|person: web::Json<Person>| async {
233 /// HttpResponse::Ok()
234 /// .json(person)})
235 /// ))
236 /// ).await;
237 ///
238 /// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
239 ///
240 /// let resp = test::TestRequest::post()
241 /// .uri("/people")
242 /// .header(header::CONTENT_TYPE, "application/json")
243 /// .set_payload(payload)
244 /// .send_request(&mut app)
245 /// .await;
246 ///
247 /// assert!(resp.status().is_success());
248 ///
249 /// let result: Person = test::read_body_json(resp).await;
250 /// }
251 /// ```
read_body_json<T, B>(res: ServiceResponse<B>) -> T where B: MessageBody + Unpin, B::Error: Into<Error>, T: DeserializeOwned,252 pub async fn read_body_json<T, B>(res: ServiceResponse<B>) -> T
253 where
254 B: MessageBody + Unpin,
255 B::Error: Into<Error>,
256 T: DeserializeOwned,
257 {
258 let body = read_body(res).await;
259
260 serde_json::from_slice(&body).unwrap_or_else(|e| {
261 panic!(
262 "read_response_json failed during deserialization of body: {:?}, {}",
263 body, e
264 )
265 })
266 }
267
load_stream<S>(mut stream: S) -> Result<Bytes, Error> where S: Stream<Item = Result<Bytes, Error>> + Unpin,268 pub async fn load_stream<S>(mut stream: S) -> Result<Bytes, Error>
269 where
270 S: Stream<Item = Result<Bytes, Error>> + Unpin,
271 {
272 let mut data = BytesMut::new();
273 while let Some(item) = stream.next().await {
274 data.extend_from_slice(&item?);
275 }
276 Ok(data.freeze())
277 }
278
load_body<B>(body: B) -> Result<Bytes, Error> where B: MessageBody + Unpin, B::Error: Into<Error>,279 pub async fn load_body<B>(body: B) -> Result<Bytes, Error>
280 where
281 B: MessageBody + Unpin,
282 B::Error: Into<Error>,
283 {
284 body::to_bytes(body).await.map_err(Into::into)
285 }
286
287 /// Helper function that returns a deserialized response body of a TestRequest
288 ///
289 /// ```
290 /// use actix_web::{App, test, web, HttpResponse, http::header};
291 /// use serde::{Serialize, Deserialize};
292 ///
293 /// #[derive(Serialize, Deserialize)]
294 /// pub struct Person {
295 /// id: String,
296 /// name: String
297 /// }
298 ///
299 /// #[actix_rt::test]
300 /// async fn test_add_person() {
301 /// let app = test::init_service(
302 /// App::new().service(
303 /// web::resource("/people")
304 /// .route(web::post().to(|person: web::Json<Person>| async {
305 /// HttpResponse::Ok()
306 /// .json(person)})
307 /// ))
308 /// ).await;
309 ///
310 /// let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
311 ///
312 /// let req = test::TestRequest::post()
313 /// .uri("/people")
314 /// .header(header::CONTENT_TYPE, "application/json")
315 /// .set_payload(payload)
316 /// .to_request();
317 ///
318 /// let result: Person = test::read_response_json(&mut app, req).await;
319 /// }
320 /// ```
read_response_json<S, B, T>(app: &S, req: Request) -> T where S: Service<Request, Response = ServiceResponse<B>, Error = Error>, B: MessageBody + Unpin, B::Error: Into<Error>, T: DeserializeOwned,321 pub async fn read_response_json<S, B, T>(app: &S, req: Request) -> T
322 where
323 S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
324 B: MessageBody + Unpin,
325 B::Error: Into<Error>,
326 T: DeserializeOwned,
327 {
328 let body = read_response(app, req).await;
329
330 serde_json::from_slice(&body).unwrap_or_else(|_| {
331 panic!(
332 "read_response_json failed during deserialization of body: {:?}",
333 body
334 )
335 })
336 }
337
338 /// Test `Request` builder.
339 ///
340 /// For unit testing, actix provides a request builder type and a simple handler runner. TestRequest implements a builder-like pattern.
341 /// You can generate various types of request via TestRequest's methods:
342 /// * `TestRequest::to_request` creates `actix_http::Request` instance.
343 /// * `TestRequest::to_srv_request` creates `ServiceRequest` instance, which is used for testing middlewares and chain adapters.
344 /// * `TestRequest::to_srv_response` creates `ServiceResponse` instance.
345 /// * `TestRequest::to_http_request` creates `HttpRequest` instance, which is used for testing handlers.
346 ///
347 /// ```
348 /// use actix_web::{test, HttpRequest, HttpResponse, HttpMessage};
349 /// use actix_web::http::{header, StatusCode};
350 ///
351 /// async fn index(req: HttpRequest) -> HttpResponse {
352 /// if let Some(hdr) = req.headers().get(header::CONTENT_TYPE) {
353 /// HttpResponse::Ok().into()
354 /// } else {
355 /// HttpResponse::BadRequest().into()
356 /// }
357 /// }
358 ///
359 /// #[test]
360 /// fn test_index() {
361 /// let req = test::TestRequest::default().insert_header("content-type", "text/plain")
362 /// .to_http_request();
363 ///
364 /// let resp = index(req).await.unwrap();
365 /// assert_eq!(resp.status(), StatusCode::OK);
366 ///
367 /// let req = test::TestRequest::default().to_http_request();
368 /// let resp = index(req).await.unwrap();
369 /// assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
370 /// }
371 /// ```
372 pub struct TestRequest {
373 req: HttpTestRequest,
374 rmap: ResourceMap,
375 config: AppConfig,
376 path: Path<Url>,
377 peer_addr: Option<SocketAddr>,
378 app_data: Extensions,
379 #[cfg(feature = "cookies")]
380 cookies: CookieJar,
381 }
382
383 impl Default for TestRequest {
default() -> TestRequest384 fn default() -> TestRequest {
385 TestRequest {
386 req: HttpTestRequest::default(),
387 rmap: ResourceMap::new(ResourceDef::new("")),
388 config: AppConfig::default(),
389 path: Path::new(Url::new(Uri::default())),
390 peer_addr: None,
391 app_data: Extensions::new(),
392 #[cfg(feature = "cookies")]
393 cookies: CookieJar::new(),
394 }
395 }
396 }
397
398 #[allow(clippy::wrong_self_convention)]
399 impl TestRequest {
400 /// Create TestRequest and set request uri
with_uri(path: &str) -> TestRequest401 pub fn with_uri(path: &str) -> TestRequest {
402 TestRequest::default().uri(path)
403 }
404
405 /// Create TestRequest and set method to `Method::GET`
get() -> TestRequest406 pub fn get() -> TestRequest {
407 TestRequest::default().method(Method::GET)
408 }
409
410 /// Create TestRequest and set method to `Method::POST`
post() -> TestRequest411 pub fn post() -> TestRequest {
412 TestRequest::default().method(Method::POST)
413 }
414
415 /// Create TestRequest and set method to `Method::PUT`
put() -> TestRequest416 pub fn put() -> TestRequest {
417 TestRequest::default().method(Method::PUT)
418 }
419
420 /// Create TestRequest and set method to `Method::PATCH`
patch() -> TestRequest421 pub fn patch() -> TestRequest {
422 TestRequest::default().method(Method::PATCH)
423 }
424
425 /// Create TestRequest and set method to `Method::DELETE`
delete() -> TestRequest426 pub fn delete() -> TestRequest {
427 TestRequest::default().method(Method::DELETE)
428 }
429
430 /// Set HTTP version of this request
version(mut self, ver: Version) -> Self431 pub fn version(mut self, ver: Version) -> Self {
432 self.req.version(ver);
433 self
434 }
435
436 /// Set HTTP method of this request
method(mut self, meth: Method) -> Self437 pub fn method(mut self, meth: Method) -> Self {
438 self.req.method(meth);
439 self
440 }
441
442 /// Set HTTP Uri of this request
uri(mut self, path: &str) -> Self443 pub fn uri(mut self, path: &str) -> Self {
444 self.req.uri(path);
445 self
446 }
447
448 /// Insert a header, replacing any that were set with an equivalent field name.
insert_header<H>(mut self, header: H) -> Self where H: IntoHeaderPair,449 pub fn insert_header<H>(mut self, header: H) -> Self
450 where
451 H: IntoHeaderPair,
452 {
453 self.req.insert_header(header);
454 self
455 }
456
457 /// Append a header, keeping any that were set with an equivalent field name.
append_header<H>(mut self, header: H) -> Self where H: IntoHeaderPair,458 pub fn append_header<H>(mut self, header: H) -> Self
459 where
460 H: IntoHeaderPair,
461 {
462 self.req.append_header(header);
463 self
464 }
465
466 /// Set cookie for this request.
467 #[cfg(feature = "cookies")]
cookie(mut self, cookie: Cookie<'_>) -> Self468 pub fn cookie(mut self, cookie: Cookie<'_>) -> Self {
469 self.cookies.add(cookie.into_owned());
470 self
471 }
472
473 /// Set request path pattern parameter
param(mut self, name: &'static str, value: &'static str) -> Self474 pub fn param(mut self, name: &'static str, value: &'static str) -> Self {
475 self.path.add_static(name, value);
476 self
477 }
478
479 /// Set peer addr
peer_addr(mut self, addr: SocketAddr) -> Self480 pub fn peer_addr(mut self, addr: SocketAddr) -> Self {
481 self.peer_addr = Some(addr);
482 self
483 }
484
485 /// Set request payload
set_payload<B: Into<Bytes>>(mut self, data: B) -> Self486 pub fn set_payload<B: Into<Bytes>>(mut self, data: B) -> Self {
487 self.req.set_payload(data);
488 self
489 }
490
491 /// Serialize `data` to a URL encoded form and set it as the request payload. The `Content-Type`
492 /// header is set to `application/x-www-form-urlencoded`.
set_form<T: Serialize>(mut self, data: &T) -> Self493 pub fn set_form<T: Serialize>(mut self, data: &T) -> Self {
494 let bytes = serde_urlencoded::to_string(data)
495 .expect("Failed to serialize test data as a urlencoded form");
496 self.req.set_payload(bytes);
497 self.req.insert_header(ContentType::form_url_encoded());
498 self
499 }
500
501 /// Serialize `data` to JSON and set it as the request payload. The `Content-Type` header is
502 /// set to `application/json`.
set_json<T: Serialize>(mut self, data: &T) -> Self503 pub fn set_json<T: Serialize>(mut self, data: &T) -> Self {
504 let bytes = serde_json::to_string(data).expect("Failed to serialize test data to json");
505 self.req.set_payload(bytes);
506 self.req.insert_header(ContentType::json());
507 self
508 }
509
510 /// Set application data. This is equivalent of `App::data()` method
511 /// for testing purpose.
data<T: 'static>(mut self, data: T) -> Self512 pub fn data<T: 'static>(mut self, data: T) -> Self {
513 self.app_data.insert(Data::new(data));
514 self
515 }
516
517 /// Set application data. This is equivalent of `App::app_data()` method
518 /// for testing purpose.
app_data<T: 'static>(mut self, data: T) -> Self519 pub fn app_data<T: 'static>(mut self, data: T) -> Self {
520 self.app_data.insert(data);
521 self
522 }
523
524 #[cfg(test)]
525 /// Set request config
rmap(mut self, rmap: ResourceMap) -> Self526 pub(crate) fn rmap(mut self, rmap: ResourceMap) -> Self {
527 self.rmap = rmap;
528 self
529 }
530
finish(&mut self) -> Request531 fn finish(&mut self) -> Request {
532 // mut used when cookie feature is enabled
533 #[allow(unused_mut)]
534 let mut req = self.req.finish();
535
536 #[cfg(feature = "cookies")]
537 {
538 use actix_http::http::header::{HeaderValue, COOKIE};
539
540 let cookie: String = self
541 .cookies
542 .delta()
543 // ensure only name=value is written to cookie header
544 .map(|c| c.stripped().encoded().to_string())
545 .collect::<Vec<_>>()
546 .join("; ");
547
548 if !cookie.is_empty() {
549 req.headers_mut()
550 .insert(COOKIE, HeaderValue::from_str(&cookie).unwrap());
551 }
552 }
553
554 req
555 }
556
557 /// Complete request creation and generate `Request` instance
to_request(mut self) -> Request558 pub fn to_request(mut self) -> Request {
559 let mut req = self.finish();
560 req.head_mut().peer_addr = self.peer_addr;
561 req
562 }
563
564 /// Complete request creation and generate `ServiceRequest` instance
to_srv_request(mut self) -> ServiceRequest565 pub fn to_srv_request(mut self) -> ServiceRequest {
566 let (mut head, payload) = self.finish().into_parts();
567 head.peer_addr = self.peer_addr;
568 self.path.get_mut().update(&head.uri);
569
570 let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
571
572 ServiceRequest::new(
573 HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data)),
574 payload,
575 )
576 }
577
578 /// Complete request creation and generate `ServiceResponse` instance
to_srv_response<B>(self, res: HttpResponse<B>) -> ServiceResponse<B>579 pub fn to_srv_response<B>(self, res: HttpResponse<B>) -> ServiceResponse<B> {
580 self.to_srv_request().into_response(res)
581 }
582
583 /// Complete request creation and generate `HttpRequest` instance
to_http_request(mut self) -> HttpRequest584 pub fn to_http_request(mut self) -> HttpRequest {
585 let (mut head, _) = self.finish().into_parts();
586 head.peer_addr = self.peer_addr;
587 self.path.get_mut().update(&head.uri);
588
589 let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
590
591 HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data))
592 }
593
594 /// Complete request creation and generate `HttpRequest` and `Payload` instances
to_http_parts(mut self) -> (HttpRequest, Payload)595 pub fn to_http_parts(mut self) -> (HttpRequest, Payload) {
596 let (mut head, payload) = self.finish().into_parts();
597 head.peer_addr = self.peer_addr;
598 self.path.get_mut().update(&head.uri);
599
600 let app_state = AppInitServiceState::new(Rc::new(self.rmap), self.config.clone());
601
602 let req = HttpRequest::new(self.path, head, app_state, Rc::new(self.app_data));
603
604 (req, payload)
605 }
606
607 /// Complete request creation, calls service and waits for response future completion.
send_request<S, B, E>(self, app: &S) -> S::Response where S: Service<Request, Response = ServiceResponse<B>, Error = E>, E: std::fmt::Debug,608 pub async fn send_request<S, B, E>(self, app: &S) -> S::Response
609 where
610 S: Service<Request, Response = ServiceResponse<B>, Error = E>,
611 E: std::fmt::Debug,
612 {
613 let req = self.to_request();
614 call_service(app, req).await
615 }
616
617 #[cfg(test)]
set_server_hostname(&mut self, host: &str)618 pub fn set_server_hostname(&mut self, host: &str) {
619 self.config.set_host(host)
620 }
621 }
622
623 #[cfg(test)]
624 mod tests {
625 use std::time::SystemTime;
626
627 use actix_http::HttpMessage;
628 use serde::{Deserialize, Serialize};
629
630 use super::*;
631 use crate::{http::header, web, App, HttpResponse, Responder};
632
633 #[actix_rt::test]
test_basics()634 async fn test_basics() {
635 let req = TestRequest::default()
636 .version(Version::HTTP_2)
637 .insert_header(header::ContentType::json())
638 .insert_header(header::Date(SystemTime::now().into()))
639 .param("test", "123")
640 .data(10u32)
641 .app_data(20u64)
642 .peer_addr("127.0.0.1:8081".parse().unwrap())
643 .to_http_request();
644 assert!(req.headers().contains_key(header::CONTENT_TYPE));
645 assert!(req.headers().contains_key(header::DATE));
646 assert_eq!(
647 req.head().peer_addr,
648 Some("127.0.0.1:8081".parse().unwrap())
649 );
650 assert_eq!(&req.match_info()["test"], "123");
651 assert_eq!(req.version(), Version::HTTP_2);
652 let data = req.app_data::<Data<u32>>().unwrap();
653 assert!(req.app_data::<Data<u64>>().is_none());
654 assert_eq!(*data.get_ref(), 10);
655
656 assert!(req.app_data::<u32>().is_none());
657 let data = req.app_data::<u64>().unwrap();
658 assert_eq!(*data, 20);
659 }
660
661 #[actix_rt::test]
test_request_methods()662 async fn test_request_methods() {
663 let app = init_service(
664 App::new().service(
665 web::resource("/index.html")
666 .route(web::put().to(|| HttpResponse::Ok().body("put!")))
667 .route(web::patch().to(|| HttpResponse::Ok().body("patch!")))
668 .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))),
669 ),
670 )
671 .await;
672
673 let put_req = TestRequest::put()
674 .uri("/index.html")
675 .insert_header((header::CONTENT_TYPE, "application/json"))
676 .to_request();
677
678 let result = read_response(&app, put_req).await;
679 assert_eq!(result, Bytes::from_static(b"put!"));
680
681 let patch_req = TestRequest::patch()
682 .uri("/index.html")
683 .insert_header((header::CONTENT_TYPE, "application/json"))
684 .to_request();
685
686 let result = read_response(&app, patch_req).await;
687 assert_eq!(result, Bytes::from_static(b"patch!"));
688
689 let delete_req = TestRequest::delete().uri("/index.html").to_request();
690 let result = read_response(&app, delete_req).await;
691 assert_eq!(result, Bytes::from_static(b"delete!"));
692 }
693
694 #[actix_rt::test]
test_response()695 async fn test_response() {
696 let app = init_service(
697 App::new().service(
698 web::resource("/index.html")
699 .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))),
700 ),
701 )
702 .await;
703
704 let req = TestRequest::post()
705 .uri("/index.html")
706 .insert_header((header::CONTENT_TYPE, "application/json"))
707 .to_request();
708
709 let result = read_response(&app, req).await;
710 assert_eq!(result, Bytes::from_static(b"welcome!"));
711 }
712
713 #[actix_rt::test]
test_send_request()714 async fn test_send_request() {
715 let app = init_service(
716 App::new().service(
717 web::resource("/index.html")
718 .route(web::get().to(|| HttpResponse::Ok().body("welcome!"))),
719 ),
720 )
721 .await;
722
723 let resp = TestRequest::get()
724 .uri("/index.html")
725 .send_request(&app)
726 .await;
727
728 let result = read_body(resp).await;
729 assert_eq!(result, Bytes::from_static(b"welcome!"));
730 }
731
732 #[derive(Serialize, Deserialize)]
733 pub struct Person {
734 id: String,
735 name: String,
736 }
737
738 #[actix_rt::test]
test_response_json()739 async fn test_response_json() {
740 let app = init_service(App::new().service(web::resource("/people").route(
741 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
742 )))
743 .await;
744
745 let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
746
747 let req = TestRequest::post()
748 .uri("/people")
749 .insert_header((header::CONTENT_TYPE, "application/json"))
750 .set_payload(payload)
751 .to_request();
752
753 let result: Person = read_response_json(&app, req).await;
754 assert_eq!(&result.id, "12345");
755 }
756
757 #[actix_rt::test]
test_body_json()758 async fn test_body_json() {
759 let app = init_service(App::new().service(web::resource("/people").route(
760 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
761 )))
762 .await;
763
764 let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
765
766 let resp = TestRequest::post()
767 .uri("/people")
768 .insert_header((header::CONTENT_TYPE, "application/json"))
769 .set_payload(payload)
770 .send_request(&app)
771 .await;
772
773 let result: Person = read_body_json(resp).await;
774 assert_eq!(&result.name, "User name");
775 }
776
777 #[actix_rt::test]
test_request_response_form()778 async fn test_request_response_form() {
779 let app = init_service(App::new().service(web::resource("/people").route(
780 web::post().to(|person: web::Form<Person>| HttpResponse::Ok().json(person)),
781 )))
782 .await;
783
784 let payload = Person {
785 id: "12345".to_string(),
786 name: "User name".to_string(),
787 };
788
789 let req = TestRequest::post()
790 .uri("/people")
791 .set_form(&payload)
792 .to_request();
793
794 assert_eq!(req.content_type(), "application/x-www-form-urlencoded");
795
796 let result: Person = read_response_json(&app, req).await;
797 assert_eq!(&result.id, "12345");
798 assert_eq!(&result.name, "User name");
799 }
800
801 #[actix_rt::test]
test_request_response_json()802 async fn test_request_response_json() {
803 let app = init_service(App::new().service(web::resource("/people").route(
804 web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
805 )))
806 .await;
807
808 let payload = Person {
809 id: "12345".to_string(),
810 name: "User name".to_string(),
811 };
812
813 let req = TestRequest::post()
814 .uri("/people")
815 .set_json(&payload)
816 .to_request();
817
818 assert_eq!(req.content_type(), "application/json");
819
820 let result: Person = read_response_json(&app, req).await;
821 assert_eq!(&result.id, "12345");
822 assert_eq!(&result.name, "User name");
823 }
824
825 #[actix_rt::test]
test_async_with_block()826 async fn test_async_with_block() {
827 async fn async_with_block() -> Result<HttpResponse, Error> {
828 let res = web::block(move || Some(4usize).ok_or("wrong")).await;
829
830 match res {
831 Ok(value) => Ok(HttpResponse::Ok()
832 .content_type("text/plain")
833 .body(format!("Async with block value: {:?}", value))),
834 Err(_) => panic!("Unexpected"),
835 }
836 }
837
838 let app =
839 init_service(App::new().service(web::resource("/index.html").to(async_with_block)))
840 .await;
841
842 let req = TestRequest::post().uri("/index.html").to_request();
843 let res = app.call(req).await.unwrap();
844 assert!(res.status().is_success());
845 }
846
847 // allow deprecated App::data
848 #[allow(deprecated)]
849 #[actix_rt::test]
test_server_data()850 async fn test_server_data() {
851 async fn handler(data: web::Data<usize>) -> impl Responder {
852 assert_eq!(**data, 10);
853 HttpResponse::Ok()
854 }
855
856 let app = init_service(
857 App::new()
858 .data(10usize)
859 .service(web::resource("/index.html").to(handler)),
860 )
861 .await;
862
863 let req = TestRequest::post().uri("/index.html").to_request();
864 let res = app.call(req).await.unwrap();
865 assert!(res.status().is_success());
866 }
867 }
868