Skip to content
New issue

Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? # to your account

Todos: Add search functionality #18

Merged
merged 1 commit into from
Oct 16, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions src/helpers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use diesel::prelude::*;
use diesel::SqliteConnection;
use rocket::local::blocking::Client;
use rocket::{self, routes};
use crate::todos::{get_todos, add_todo, delete_todo, update_todo, complete_todo};
use crate::todos::{get_todos, add_todo, delete_todo, update_todo, complete_todo, search_todos};
use crate::user::{create_user, get_user_by_id};
use diesel::sql_query;
use diesel::r2d2::{self, ConnectionManager};
Expand Down Expand Up @@ -35,7 +35,7 @@ pub fn setup_rocket() -> Client {

let rocket = rocket::build()
.manage(pool)
.mount("/", routes![get_todos, add_todo, delete_todo, update_todo, complete_todo, create_user, get_user_by_id]);
.mount("/", routes![get_todos, add_todo, delete_todo, update_todo, complete_todo, create_user, get_user_by_id, search_todos]);
Client::tracked(rocket).expect("valid rocket instance")
}

Expand Down
2 changes: 1 addition & 1 deletion src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -30,5 +30,5 @@ fn rocket() -> _ {
info!("Rocket has launched successfully!");
})))
.manage(pool)
.mount("/", routes![todos::get_todos, todos::add_todo, todos::delete_todo, todos::update_todo, todos::complete_todo, user::create_user, user::get_user_by_id])
.mount("/", routes![todos::get_todos, todos::add_todo, todos::delete_todo, todos::update_todo, todos::complete_todo, user::create_user, user::get_user_by_id, todos::search_todos])
}
20 changes: 20 additions & 0 deletions src/todos.rs
Original file line number Diff line number Diff line change
Expand Up @@ -147,4 +147,24 @@ pub fn complete_todo(pool: &State<DbPool>, id: i32) -> Result<&'static str, (Sta
.map_err(|_| (Status::InternalServerError, "Failed to complete todo"))?;

Ok("Todo marked as completed!")
}

#[get("/todos/search?<query>&<user_id>")]
pub fn search_todos(pool: &State<DbPool>, query: Option<String>, user_id: i32) -> Result<Json<Vec<TodoItem>>, (Status, &'static str)> {
let mut connection = pool.get().map_err(|_| (Status::InternalServerError, "Failed to get connection from pool"))?;

let results: Vec<TodoItem> = if let Some(query) = query {
todos::table
.filter(todos::dsl::title.like(format!("%{}%", query))) // Search by title
.filter(todos::dsl::user_id.eq(user_id)) // Ensure user_id matches
.load(&mut connection)
.map_err(|_| (Status::InternalServerError, "Failed to search todos"))?
} else {
todos::table
.filter(todos::dsl::user_id.eq(user_id)) // Fetch todos only for the user
.load(&mut connection)
.map_err(|_| (Status::InternalServerError, "Failed to fetch todos"))?
};

Ok(Json(results))
}
134 changes: 134 additions & 0 deletions tests/search_todos.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
use rocket::http::Status;
use dooly::helpers::{cleanup_database, establish_test_connection, run_seed_script, setup_rocket};
use serde_json::json;
use rocket::http::ContentType;

#[test]
fn test_search_todos_with_results() {
let mut pool = establish_test_connection();
cleanup_database(&mut pool).unwrap(); // Clean up the database before starting the test
run_seed_script(&mut pool).unwrap(); // Seed the database with initial data

let client = setup_rocket();

// Create a few todos with user_id 1
let new_todo_1 = json!({
"title": "Test Todo 1",
"completed": false,
"user_id": 1
});

let new_todo_2 = json!({
"title": "Important Task",
"completed": false,
"user_id": 1
});

client.post("/todos")
.header(ContentType::JSON)
.body(new_todo_1.to_string())
.dispatch();

client.post("/todos")
.header(ContentType::JSON)
.body(new_todo_2.to_string())
.dispatch();

// Search for todos with user_id 1 containing "Task"
let response = client.get("/todos/search?query=Task&user_id=1").dispatch();

assert_eq!(response.status(), Status::Ok);
let todos: Vec<serde_json::Value> = serde_json::from_str(&response.into_string().unwrap()).unwrap();

assert_eq!(todos.len(), 1);
assert_eq!(todos[0]["title"], "Important Task");
}


#[test]
fn test_search_todos_no_results() {
let mut pool = establish_test_connection();
cleanup_database(&mut pool).unwrap(); // Clean up the database before starting the test
run_seed_script(&mut pool).unwrap(); // Seed the database with initial data

let client = setup_rocket();

// Create a todo with user_id 1
let new_todo = json!({
"title": "Unrelated Task",
"completed": false,
"user_id": 1
});

client.post("/todos")
.header(ContentType::JSON)
.body(new_todo.to_string())
.dispatch();

// Search for todos with user_id 1 and query that doesn't match
let response = client.get("/todos/search?query=NotInDatabase&user_id=1").dispatch();

assert_eq!(response.status(), Status::Ok);
let todos: Vec<serde_json::Value> = serde_json::from_str(&response.into_string().unwrap()).unwrap();

assert_eq!(todos.len(), 0);
}

#[test]
fn test_search_todos_no_query() {
let mut pool = establish_test_connection();
cleanup_database(&mut pool).unwrap(); // Clean up the database before starting the test
run_seed_script(&mut pool).unwrap(); // Seed the database with initial data

let client = setup_rocket();

// Create a todo with user_id 1
let new_todo = json!({
"title": "General Todo",
"completed": false,
"user_id": 1
});

client.post("/todos")
.header(ContentType::JSON)
.body(new_todo.to_string())
.dispatch();

// Search without a query, but for user_id 1 (should return all todos for user_id 1)
let response = client.get("/todos/search?user_id=1").dispatch();

assert_eq!(response.status(), Status::Ok);
let todos: Vec<serde_json::Value> = serde_json::from_str(&response.into_string().unwrap()).unwrap();

assert_eq!(todos.len(), 3); // 2 seeded + 1 new added
assert_eq!(todos[2]["title"], "General Todo");
}

#[test]
fn test_search_todos_wrong_user() {
let mut pool = establish_test_connection();
cleanup_database(&mut pool).unwrap(); // Clean up the database before starting the test
run_seed_script(&mut pool).unwrap(); // Seed the database with initial data

let client = setup_rocket();

// Create a todo with user_id 1
let new_todo = json!({
"title": "User 1's Todo",
"completed": false,
"user_id": 1
});

client.post("/todos")
.header(ContentType::JSON)
.body(new_todo.to_string())
.dispatch();

// Attempt to search with a different user_id (e.g., user_id=2)
let response = client.get("/todos/search?user_id=2").dispatch();

assert_eq!(response.status(), Status::Ok);
let todos: Vec<serde_json::Value> = serde_json::from_str(&response.into_string().unwrap()).unwrap();

assert_eq!(todos.len(), 0); // Should return no results for user_id=2
}
Loading