use actix_web::{web, HttpRequest, HttpResponse, Responder}; use diesel::prelude::*; use crate::{ auth::extractor::AuthUser, database::establish_connection, error::AppError, json_serialization::feed_info::{FeedInfo, FeedInfoList}, json_serialization::user::JsonUser, schema::feed::{self, user_id}, }; pub async fn list_feeds( path: web::Path, req: HttpRequest, auth_user: AuthUser, ) -> Result { let request = req.clone(); let req_user_id = path.user_id; if auth_user.0 != req_user_id { return Ok(HttpResponse::Forbidden().finish()); } let mut connection = establish_connection(); let feeds = feed::table .filter(user_id.eq(req_user_id)) .select((feed::id, feed::title, feed::url)) .load::<(i32, String, String)>(&mut connection)?; let feed_list: Vec = feeds .into_iter() .map(|(id, title, url)| FeedInfo { id, title, url }) .collect(); Ok(FeedInfoList { feeds: feed_list } .respond_to(&request) .map_into_boxed_body()) } #[cfg(test)] mod tests { use actix_service::Service; use actix_web::http::StatusCode; use actix_web::{test, web, App, HttpMessage}; use super::list_feeds; use crate::auth::extractor::AuthUser; use crate::database::establish_connection; use crate::test_helpers::{delete_feed, delete_user, insert_feed, insert_user}; #[actix_web::test] async fn list_feeds_returns_feeds_for_user() { let mut connection = establish_connection(); let user = insert_user(&mut connection, "secret"); let feed = insert_feed(&mut connection, user.id); let user_id = user.id; let app = test::init_service( App::new() .wrap_fn(move |req, srv| { req.extensions_mut().insert(AuthUser(user_id)); srv.call(req) }) .route("/feeds/{user_id}", web::get().to(list_feeds)), ) .await; let req = test::TestRequest::get() .uri(&format!("/feeds/{}", user.id)) .to_request(); let resp = test::call_service(&app, req).await; assert_eq!(StatusCode::OK, resp.status()); let body = test::read_body(resp).await; let body_str = String::from_utf8(body.to_vec()).unwrap(); assert!(body_str.contains(&feed.title)); assert!(body_str.contains(&feed.url)); delete_feed(&mut connection, feed.id); delete_user(&mut connection, user.id); } #[actix_web::test] async fn list_feeds_returns_empty_list_for_user_with_no_feeds() { let mut connection = establish_connection(); let user = insert_user(&mut connection, "secret"); let user_id = user.id; let app = test::init_service( App::new() .wrap_fn(move |req, srv| { req.extensions_mut().insert(AuthUser(user_id)); srv.call(req) }) .route("/feeds/{user_id}", web::get().to(list_feeds)), ) .await; let req = test::TestRequest::get() .uri(&format!("/feeds/{}", user.id)) .to_request(); let resp = test::call_service(&app, req).await; assert_eq!(StatusCode::OK, resp.status()); let body = test::read_body(resp).await; let body_str = String::from_utf8(body.to_vec()).unwrap(); assert!(body_str.contains("\"feeds\":[]")); delete_user(&mut connection, user.id); } #[actix_web::test] async fn list_feeds_rejects_requests_for_another_user() { let mut connection = establish_connection(); let user_a = insert_user(&mut connection, "secret"); let user_b = insert_user(&mut connection, "secret"); let feed_b = insert_feed(&mut connection, user_b.id); let user_a_id = user_a.id; let app = test::init_service( App::new() .wrap_fn(move |req, srv| { req.extensions_mut().insert(AuthUser(user_a_id)); srv.call(req) }) .route("/feeds/{user_id}", web::get().to(list_feeds)), ) .await; let req = test::TestRequest::get() .uri(&format!("/feeds/{}", user_b.id)) .to_request(); let resp = test::call_service(&app, req).await; assert_eq!(StatusCode::FORBIDDEN, resp.status()); delete_feed(&mut connection, feed_b.id); delete_user(&mut connection, user_a.id); delete_user(&mut connection, user_b.id); } }