184 lines
5.9 KiB
Rust
184 lines
5.9 KiB
Rust
use actix_web::{web, HttpResponse};
|
|
use diesel::prelude::*;
|
|
|
|
use crate::{
|
|
auth::extractor::AuthUser,
|
|
database::establish_connection,
|
|
schema::{feed, feed_item},
|
|
};
|
|
|
|
pub async fn delete_feed(path: web::Path<i32>, auth_user: AuthUser) -> HttpResponse {
|
|
let feed_id = path.into_inner();
|
|
let mut connection = establish_connection();
|
|
|
|
let owner: Option<i32> = feed::table
|
|
.find(feed_id)
|
|
.select(feed::user_id)
|
|
.first(&mut connection)
|
|
.optional()
|
|
.unwrap_or(None);
|
|
|
|
// Treat "doesn't exist" and "not yours" the same, so callers can't probe
|
|
// for other users' feed ids.
|
|
if owner != Some(auth_user.0) {
|
|
return HttpResponse::NotFound().finish();
|
|
}
|
|
|
|
diesel::delete(feed_item::table.filter(feed_item::feed_id.eq(feed_id)))
|
|
.execute(&mut connection)
|
|
.ok();
|
|
|
|
diesel::delete(feed::table.filter(feed::id.eq(feed_id)))
|
|
.execute(&mut connection)
|
|
.ok();
|
|
|
|
HttpResponse::NoContent().finish()
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use actix_service::Service;
|
|
use actix_web::http::StatusCode;
|
|
use actix_web::{test, web, App, HttpMessage};
|
|
use diesel::prelude::*;
|
|
|
|
use super::delete_feed;
|
|
use crate::auth::extractor::AuthUser;
|
|
use crate::database::establish_connection;
|
|
use crate::schema::{feed, feed_item};
|
|
use crate::test_helpers::{
|
|
delete_feed as cleanup_feed, delete_user, insert_feed, insert_feed_item, insert_user,
|
|
};
|
|
|
|
#[actix_web::test]
|
|
async fn delete_feed_removes_feed_and_items() {
|
|
let mut connection = establish_connection();
|
|
let user = insert_user(&mut connection, "secret");
|
|
let f = insert_feed(&mut connection, user.id);
|
|
let item = insert_feed_item(&mut connection, f.id, false);
|
|
|
|
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("/feed/{feed_id}", web::delete().to(delete_feed)),
|
|
)
|
|
.await;
|
|
let req = test::TestRequest::delete()
|
|
.uri(&format!("/feed/{}", f.id))
|
|
.to_request();
|
|
let resp = test::call_service(&app, req).await;
|
|
|
|
assert_eq!(StatusCode::NO_CONTENT, resp.status());
|
|
|
|
let feed_exists: i64 = feed::table
|
|
.filter(feed::id.eq(f.id))
|
|
.count()
|
|
.get_result(&mut connection)
|
|
.unwrap();
|
|
assert_eq!(0, feed_exists);
|
|
|
|
let item_exists: i64 = feed_item::table
|
|
.filter(feed_item::id.eq(item.id))
|
|
.count()
|
|
.get_result(&mut connection)
|
|
.unwrap();
|
|
assert_eq!(0, item_exists);
|
|
|
|
delete_user(&mut connection, user.id);
|
|
}
|
|
|
|
#[actix_web::test]
|
|
async fn delete_feed_returns_404_for_nonexistent_feed() {
|
|
let app = test::init_service(
|
|
App::new()
|
|
.wrap_fn(move |req, srv| {
|
|
req.extensions_mut().insert(AuthUser(1));
|
|
srv.call(req)
|
|
})
|
|
.route("/feed/{feed_id}", web::delete().to(delete_feed)),
|
|
)
|
|
.await;
|
|
let req = test::TestRequest::delete()
|
|
.uri("/feed/999999999")
|
|
.to_request();
|
|
let resp = test::call_service(&app, req).await;
|
|
|
|
assert_eq!(StatusCode::NOT_FOUND, resp.status());
|
|
}
|
|
|
|
#[actix_web::test]
|
|
async fn delete_feed_does_not_affect_other_feeds() {
|
|
let mut connection = establish_connection();
|
|
let user = insert_user(&mut connection, "secret");
|
|
let feed_a = insert_feed(&mut connection, user.id);
|
|
let feed_b = 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("/feed/{feed_id}", web::delete().to(delete_feed)),
|
|
)
|
|
.await;
|
|
let req = test::TestRequest::delete()
|
|
.uri(&format!("/feed/{}", feed_a.id))
|
|
.to_request();
|
|
let resp = test::call_service(&app, req).await;
|
|
|
|
assert_eq!(StatusCode::NO_CONTENT, resp.status());
|
|
|
|
let feed_b_exists: i64 = feed::table
|
|
.filter(feed::id.eq(feed_b.id))
|
|
.count()
|
|
.get_result(&mut connection)
|
|
.unwrap();
|
|
assert_eq!(1, feed_b_exists);
|
|
|
|
cleanup_feed(&mut connection, feed_b.id);
|
|
delete_user(&mut connection, user.id);
|
|
}
|
|
|
|
#[actix_web::test]
|
|
async fn delete_feed_rejects_other_users_feed() {
|
|
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("/feed/{feed_id}", web::delete().to(delete_feed)),
|
|
)
|
|
.await;
|
|
let req = test::TestRequest::delete()
|
|
.uri(&format!("/feed/{}", feed_b.id))
|
|
.to_request();
|
|
let resp = test::call_service(&app, req).await;
|
|
|
|
assert_eq!(StatusCode::NOT_FOUND, resp.status());
|
|
|
|
let feed_b_exists: i64 = feed::table
|
|
.filter(feed::id.eq(feed_b.id))
|
|
.count()
|
|
.get_result(&mut connection)
|
|
.unwrap();
|
|
assert_eq!(1, feed_b_exists);
|
|
|
|
cleanup_feed(&mut connection, feed_b.id);
|
|
delete_user(&mut connection, user_a.id);
|
|
delete_user(&mut connection, user_b.id);
|
|
}
|
|
}
|