1use std::error::Error as StdError;
2
3use actix_http::Request;
4use actix_service::IntoServiceFactory;
5use serde::de::DeserializeOwned;
6
7use crate::{
8    body::{self, MessageBody},
9    config::AppConfig,
10    dev::{Service, ServiceFactory},
11    service::ServiceResponse,
12    web::Bytes,
13    Error,
14};
15
16pub async fn init_service<R, S, B, E>(
42    app: R,
43) -> impl Service<Request, Response = ServiceResponse<B>, Error = E>
44where
45    R: IntoServiceFactory<S, Request>,
46    S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>,
47    S::InitError: std::fmt::Debug,
48{
49    try_init_service(app)
50        .await
51        .expect("service initialization failed")
52}
53
54pub(crate) async fn try_init_service<R, S, B, E>(
56    app: R,
57) -> Result<impl Service<Request, Response = ServiceResponse<B>, Error = E>, S::InitError>
58where
59    R: IntoServiceFactory<S, Request>,
60    S: ServiceFactory<Request, Config = AppConfig, Response = ServiceResponse<B>, Error = E>,
61    S::InitError: std::fmt::Debug,
62{
63    let srv = app.into_factory();
64    srv.new_service(AppConfig::default()).await
65}
66
67pub async fn call_service<S, R, B, E>(app: &S, req: R) -> S::Response
94where
95    S: Service<R, Response = ServiceResponse<B>, Error = E>,
96    E: std::fmt::Debug,
97{
98    app.call(req)
99        .await
100        .expect("test service call returned error")
101}
102
103pub async fn try_call_service<S, R, B, E>(app: &S, req: R) -> Result<S::Response, E>
105where
106    S: Service<R, Response = ServiceResponse<B>, Error = E>,
107    E: std::fmt::Debug,
108{
109    app.call(req).await
110}
111
112pub async fn call_and_read_body<S, B>(app: &S, req: Request) -> Bytes
144where
145    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
146    B: MessageBody,
147{
148    let res = call_service(app, req).await;
149    read_body(res).await
150}
151
152#[doc(hidden)]
153#[deprecated(since = "4.0.0", note = "Renamed to `call_and_read_body`.")]
154pub async fn read_response<S, B>(app: &S, req: Request) -> Bytes
155where
156    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
157    B: MessageBody,
158{
159    let res = call_service(app, req).await;
160    read_body(res).await
161}
162
163pub async fn read_body<B>(res: ServiceResponse<B>) -> Bytes
194where
195    B: MessageBody,
196{
197    try_read_body(res)
198        .await
199        .map_err(Into::<Box<dyn StdError>>::into)
200        .expect("error reading test response body")
201}
202
203pub async fn try_read_body<B>(res: ServiceResponse<B>) -> Result<Bytes, <B as MessageBody>::Error>
205where
206    B: MessageBody,
207{
208    let body = res.into_body();
209    body::to_bytes(body).await
210}
211
212pub async fn read_body_json<T, B>(res: ServiceResponse<B>) -> T
256where
257    B: MessageBody,
258    T: DeserializeOwned,
259{
260    try_read_body_json(res).await.unwrap_or_else(|err| {
261        panic!(
262            "could not deserialize body into a {}\nerr: {}",
263            std::any::type_name::<T>(),
264            err,
265        )
266    })
267}
268
269pub async fn try_read_body_json<T, B>(res: ServiceResponse<B>) -> Result<T, Box<dyn StdError>>
271where
272    B: MessageBody,
273    T: DeserializeOwned,
274{
275    let body = try_read_body(res)
276        .await
277        .map_err(Into::<Box<dyn StdError>>::into)?;
278    serde_json::from_slice(&body).map_err(Into::<Box<dyn StdError>>::into)
279}
280
281pub async fn call_and_read_body_json<S, B, T>(app: &S, req: Request) -> T
323where
324    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
325    B: MessageBody,
326    T: DeserializeOwned,
327{
328    try_call_and_read_body_json(app, req).await.unwrap()
329}
330
331pub async fn try_call_and_read_body_json<S, B, T>(
333    app: &S,
334    req: Request,
335) -> Result<T, Box<dyn StdError>>
336where
337    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
338    B: MessageBody,
339    T: DeserializeOwned,
340{
341    let res = try_call_service(app, req)
342        .await
343        .map_err(Into::<Box<dyn StdError>>::into)?;
344    try_read_body_json(res).await
345}
346
347#[doc(hidden)]
348#[deprecated(since = "4.0.0", note = "Renamed to `call_and_read_body_json`.")]
349pub async fn read_response_json<S, B, T>(app: &S, req: Request) -> T
350where
351    S: Service<Request, Response = ServiceResponse<B>, Error = Error>,
352    B: MessageBody,
353    T: DeserializeOwned,
354{
355    call_and_read_body_json(app, req).await
356}
357
358#[cfg(test)]
359mod tests {
360    use serde::{Deserialize, Serialize};
361
362    use super::*;
363    use crate::{
364        dev::ServiceRequest, http::header, test::TestRequest, web, App, HttpMessage, HttpResponse,
365    };
366
367    #[actix_rt::test]
368    async fn test_request_methods() {
369        let app = init_service(
370            App::new().service(
371                web::resource("/index.html")
372                    .route(web::put().to(|| HttpResponse::Ok().body("put!")))
373                    .route(web::patch().to(|| HttpResponse::Ok().body("patch!")))
374                    .route(web::delete().to(|| HttpResponse::Ok().body("delete!"))),
375            ),
376        )
377        .await;
378
379        let put_req = TestRequest::put()
380            .uri("/index.html")
381            .insert_header((header::CONTENT_TYPE, "application/json"))
382            .to_request();
383
384        let result = call_and_read_body(&app, put_req).await;
385        assert_eq!(result, Bytes::from_static(b"put!"));
386
387        let patch_req = TestRequest::patch()
388            .uri("/index.html")
389            .insert_header((header::CONTENT_TYPE, "application/json"))
390            .to_request();
391
392        let result = call_and_read_body(&app, patch_req).await;
393        assert_eq!(result, Bytes::from_static(b"patch!"));
394
395        let delete_req = TestRequest::delete().uri("/index.html").to_request();
396        let result = call_and_read_body(&app, delete_req).await;
397        assert_eq!(result, Bytes::from_static(b"delete!"));
398    }
399
400    #[derive(Serialize, Deserialize, Debug)]
401    pub struct Person {
402        id: String,
403        name: String,
404    }
405
406    #[actix_rt::test]
407    async fn test_response_json() {
408        let app =
409            init_service(App::new().service(web::resource("/people").route(
410                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
411            )))
412            .await;
413
414        let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
415
416        let req = TestRequest::post()
417            .uri("/people")
418            .insert_header((header::CONTENT_TYPE, "application/json"))
419            .set_payload(payload)
420            .to_request();
421
422        let result: Person = call_and_read_body_json(&app, req).await;
423        assert_eq!(&result.id, "12345");
424    }
425
426    #[actix_rt::test]
427    async fn test_try_response_json_error() {
428        let app =
429            init_service(App::new().service(web::resource("/people").route(
430                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
431            )))
432            .await;
433
434        let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
435
436        let req = TestRequest::post()
437            .uri("/animals") .insert_header((header::CONTENT_TYPE, "application/json"))
439            .set_payload(payload)
440            .to_request();
441
442        let result: Result<Person, Box<dyn StdError>> =
443            try_call_and_read_body_json(&app, req).await;
444        assert!(result.is_err());
445    }
446
447    #[actix_rt::test]
448    async fn test_body_json() {
449        let app =
450            init_service(App::new().service(web::resource("/people").route(
451                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
452            )))
453            .await;
454
455        let payload = r#"{"id":"12345","name":"User name"}"#.as_bytes();
456
457        let res = TestRequest::post()
458            .uri("/people")
459            .insert_header((header::CONTENT_TYPE, "application/json"))
460            .set_payload(payload)
461            .send_request(&app)
462            .await;
463
464        let result: Person = read_body_json(res).await;
465        assert_eq!(&result.name, "User name");
466    }
467
468    #[actix_rt::test]
469    async fn test_try_body_json_error() {
470        let app =
471            init_service(App::new().service(web::resource("/people").route(
472                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
473            )))
474            .await;
475
476        let payload = r#"{"id":12345,"name":"User name"}"#.as_bytes();
478
479        let res = TestRequest::post()
480            .uri("/people")
481            .insert_header((header::CONTENT_TYPE, "application/json"))
482            .set_payload(payload)
483            .send_request(&app)
484            .await;
485
486        let result: Result<Person, Box<dyn StdError>> = try_read_body_json(res).await;
487        assert!(result.is_err());
488    }
489
490    #[actix_rt::test]
491    async fn test_request_response_form() {
492        let app =
493            init_service(App::new().service(web::resource("/people").route(
494                web::post().to(|person: web::Form<Person>| HttpResponse::Ok().json(person)),
495            )))
496            .await;
497
498        let payload = Person {
499            id: "12345".to_string(),
500            name: "User name".to_string(),
501        };
502
503        let req = TestRequest::post()
504            .uri("/people")
505            .set_form(&payload)
506            .to_request();
507
508        assert_eq!(req.content_type(), "application/x-www-form-urlencoded");
509
510        let result: Person = call_and_read_body_json(&app, req).await;
511        assert_eq!(&result.id, "12345");
512        assert_eq!(&result.name, "User name");
513    }
514
515    #[actix_rt::test]
516    async fn test_response() {
517        let app = init_service(
518            App::new().service(
519                web::resource("/index.html")
520                    .route(web::post().to(|| HttpResponse::Ok().body("welcome!"))),
521            ),
522        )
523        .await;
524
525        let req = TestRequest::post()
526            .uri("/index.html")
527            .insert_header((header::CONTENT_TYPE, "application/json"))
528            .to_request();
529
530        let result = call_and_read_body(&app, req).await;
531        assert_eq!(result, Bytes::from_static(b"welcome!"));
532    }
533
534    #[actix_rt::test]
535    async fn test_request_response_json() {
536        let app =
537            init_service(App::new().service(web::resource("/people").route(
538                web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
539            )))
540            .await;
541
542        let payload = Person {
543            id: "12345".to_string(),
544            name: "User name".to_string(),
545        };
546
547        let req = TestRequest::post()
548            .uri("/people")
549            .set_json(&payload)
550            .to_request();
551
552        assert_eq!(req.content_type(), "application/json");
553
554        let result: Person = call_and_read_body_json(&app, req).await;
555        assert_eq!(&result.id, "12345");
556        assert_eq!(&result.name, "User name");
557    }
558
559    #[actix_rt::test]
560    #[allow(dead_code)]
561    async fn return_opaque_types() {
562        fn test_app() -> App<
563            impl ServiceFactory<
564                ServiceRequest,
565                Config = (),
566                Response = ServiceResponse<impl MessageBody>,
567                Error = crate::Error,
568                InitError = (),
569            >,
570        > {
571            App::new().service(
572                web::resource("/people").route(
573                    web::post().to(|person: web::Json<Person>| HttpResponse::Ok().json(person)),
574                ),
575            )
576        }
577
578        async fn test_service(
579        ) -> impl Service<Request, Response = ServiceResponse<impl MessageBody>, Error = crate::Error>
580        {
581            init_service(test_app()).await
582        }
583
584        async fn compile_test(mut req: Vec<Request>) {
585            let svc = test_service().await;
586            call_service(&svc, req.pop().unwrap()).await;
587            call_and_read_body(&svc, req.pop().unwrap()).await;
588            read_body(call_service(&svc, req.pop().unwrap()).await).await;
589            let _: String = call_and_read_body_json(&svc, req.pop().unwrap()).await;
590            let _: String = read_body_json(call_service(&svc, req.pop().unwrap()).await).await;
591        }
592    }
593}