mirror of https://github.com/buster-so/buster.git
bulk on favorite reqs
This commit is contained in:
parent
94be03ee93
commit
a2273a81b5
|
@ -3,28 +3,37 @@ use chrono::Utc;
|
|||
use diesel::{insert_into, update, upsert::excluded, ExpressionMethods};
|
||||
use diesel_async::RunQueryDsl;
|
||||
use middleware::AuthenticatedUser;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use uuid::Uuid;
|
||||
|
||||
use database::{
|
||||
enums::AssetType,
|
||||
pool::get_pg_pool,
|
||||
models::UserFavorite,
|
||||
schema::user_favorites,
|
||||
};
|
||||
use database::{enums::AssetType, models::UserFavorite, pool::get_pg_pool, schema::user_favorites};
|
||||
|
||||
use super::favorites_utils::{list_user_favorites, FavoriteObject};
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
pub struct CreateFavoriteReq {
|
||||
pub id: Uuid,
|
||||
#[serde(alias = "type")]
|
||||
pub asset_type: AssetType,
|
||||
pub index: Option<usize>,
|
||||
}
|
||||
|
||||
// Maintain backward compatibility with single item operations
|
||||
pub async fn create_favorite(
|
||||
user: &AuthenticatedUser,
|
||||
req: &CreateFavoriteReq,
|
||||
) -> Result<Vec<FavoriteObject>> {
|
||||
let index = req.index.unwrap_or(0);
|
||||
create_favorites_bulk(user, &[req.clone()]).await
|
||||
}
|
||||
|
||||
// New function to handle bulk operations
|
||||
pub async fn create_favorites_bulk(
|
||||
user: &AuthenticatedUser,
|
||||
favorites: &[CreateFavoriteReq],
|
||||
) -> Result<Vec<FavoriteObject>> {
|
||||
if favorites.is_empty() {
|
||||
return list_user_favorites(user).await;
|
||||
}
|
||||
|
||||
let mut conn = match get_pg_pool().get().await {
|
||||
Ok(conn) => conn,
|
||||
|
@ -34,28 +43,45 @@ pub async fn create_favorite(
|
|||
}
|
||||
};
|
||||
|
||||
// Find the minimum index to know where to start shifting existing favorites
|
||||
let min_index = favorites
|
||||
.iter()
|
||||
.map(|f| f.index.unwrap_or(0))
|
||||
.min()
|
||||
.unwrap_or(0);
|
||||
|
||||
// Shift existing favorites to make room for new ones (one operation for all)
|
||||
match update(user_favorites::table)
|
||||
.set(user_favorites::order_index.eq(user_favorites::order_index + 1))
|
||||
.set(user_favorites::order_index.eq(user_favorites::order_index + favorites.len() as i32))
|
||||
.filter(user_favorites::user_id.eq(user.id))
|
||||
.filter(user_favorites::order_index.ge(index as i32))
|
||||
.filter(user_favorites::order_index.ge(min_index as i32))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(e) => return Err(anyhow!("Error updating user favorite: {}", e)),
|
||||
Err(e) => return Err(anyhow!("Error updating user favorites: {}", e)),
|
||||
};
|
||||
|
||||
let user_favorite = UserFavorite {
|
||||
asset_type: req.asset_type,
|
||||
user_id: user.id,
|
||||
asset_id: req.id,
|
||||
order_index: index as i32,
|
||||
created_at: Utc::now(),
|
||||
deleted_at: None,
|
||||
};
|
||||
// Prepare all new favorites for bulk insertion
|
||||
let user_favorites: Vec<UserFavorite> = favorites
|
||||
.iter()
|
||||
.enumerate()
|
||||
.map(|(i, req)| {
|
||||
let index = req.index.unwrap_or(0) + i;
|
||||
UserFavorite {
|
||||
asset_type: req.asset_type,
|
||||
user_id: user.id,
|
||||
asset_id: req.id,
|
||||
order_index: index as i32,
|
||||
created_at: Utc::now(),
|
||||
deleted_at: None,
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
// Insert all favorites in a single operation
|
||||
match insert_into(user_favorites::table)
|
||||
.values(user_favorite)
|
||||
.values(&user_favorites)
|
||||
.on_conflict((
|
||||
user_favorites::user_id,
|
||||
user_favorites::asset_id,
|
||||
|
@ -70,8 +96,86 @@ pub async fn create_favorite(
|
|||
.await
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(e) => return Err(anyhow!("Error inserting or updating user favorite: {}", e)),
|
||||
Err(e) => return Err(anyhow!("Error inserting or updating user favorites: {}", e)),
|
||||
};
|
||||
|
||||
list_user_favorites(user).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use anyhow::Result;
|
||||
use chrono::Utc;
|
||||
use database::enums::AssetType;
|
||||
use middleware::AuthenticatedUser;
|
||||
use mock_pool::MockPool;
|
||||
use tokio;
|
||||
use uuid::Uuid;
|
||||
|
||||
// We need to mock the database pool
|
||||
// This is a very simple mock implementation for testing
|
||||
mod mock_pool {
|
||||
use anyhow::Result;
|
||||
use diesel_async::{AsyncConnection, AsyncPgConnection};
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
pub struct MockConn;
|
||||
|
||||
impl MockConn {
|
||||
pub async fn execute(&self, _query: String) -> Result<()> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub struct MockPool {
|
||||
pub connections: Arc<Mutex<Vec<MockConn>>>,
|
||||
}
|
||||
|
||||
impl MockPool {
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
connections: Arc::new(Mutex::new(vec![MockConn])),
|
||||
}
|
||||
}
|
||||
|
||||
pub async fn get(&self) -> Result<MockConn> {
|
||||
let conn = self.connections.lock().unwrap().pop().unwrap();
|
||||
Ok(conn)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Mock the database functions
|
||||
// This is just a placeholder for the actual unit test
|
||||
// A real implementation would use a proper testing framework
|
||||
#[tokio::test]
|
||||
async fn test_create_favorites_bulk() -> Result<()> {
|
||||
// Create test user
|
||||
let user = AuthenticatedUser {
|
||||
id: Uuid::new_v4(),
|
||||
// Add other fields as needed
|
||||
};
|
||||
|
||||
// Create test favorites
|
||||
let favorites = vec![
|
||||
CreateFavoriteReq {
|
||||
id: Uuid::new_v4(),
|
||||
asset_type: AssetType::DashboardFile,
|
||||
index: None,
|
||||
},
|
||||
CreateFavoriteReq {
|
||||
id: Uuid::new_v4(),
|
||||
asset_type: AssetType::Collection,
|
||||
index: Some(1),
|
||||
},
|
||||
];
|
||||
|
||||
// In a real test, we would use a test database
|
||||
// For now, we'll just assert that the function doesn't panic
|
||||
let result = create_favorites_bulk(&user, &favorites).await;
|
||||
assert!(result.is_ok() || result.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,22 +12,67 @@ use database::{
|
|||
|
||||
use super::favorites_utils::{list_user_favorites, FavoriteObject};
|
||||
|
||||
// Maintain backward compatibility with single item operations
|
||||
pub async fn delete_favorite(user: &AuthenticatedUser, id: &Uuid) -> Result<Vec<FavoriteObject>> {
|
||||
delete_favorites_bulk(user, &[*id]).await
|
||||
}
|
||||
|
||||
// New function to handle bulk operations
|
||||
pub async fn delete_favorites_bulk(user: &AuthenticatedUser, ids: &[Uuid]) -> Result<Vec<FavoriteObject>> {
|
||||
if ids.is_empty() {
|
||||
return list_user_favorites(user).await;
|
||||
}
|
||||
|
||||
let mut conn = match get_pg_pool().get().await {
|
||||
Ok(conn) => conn,
|
||||
Err(e) => return Err(anyhow!("Error getting connection from pool: {:?}", e)),
|
||||
};
|
||||
|
||||
// Delete all favorites in a single operation
|
||||
match update(user_favorites::table)
|
||||
.set(user_favorites::deleted_at.eq(Some(Utc::now())))
|
||||
.filter(user_favorites::user_id.eq(user.id))
|
||||
.filter(user_favorites::asset_id.eq(id))
|
||||
.filter(user_favorites::asset_id.eq_any(ids))
|
||||
.execute(&mut conn)
|
||||
.await
|
||||
{
|
||||
Ok(_) => (),
|
||||
Err(e) => return Err(anyhow!("Error deleting favorite: {:?}", e)),
|
||||
Err(e) => return Err(anyhow!("Error deleting favorites: {:?}", e)),
|
||||
};
|
||||
|
||||
list_user_favorites(user).await
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::*;
|
||||
use database::enums::AssetType;
|
||||
use middleware::AuthenticatedUser;
|
||||
use uuid::Uuid;
|
||||
use tokio;
|
||||
use anyhow::Result;
|
||||
|
||||
// Placeholder for a real test
|
||||
#[tokio::test]
|
||||
async fn test_delete_favorites_bulk() -> Result<()> {
|
||||
// Create test user
|
||||
let user = AuthenticatedUser {
|
||||
id: Uuid::new_v4(),
|
||||
// Add other fields as needed
|
||||
};
|
||||
|
||||
// Create test favorite ids
|
||||
let favorite_ids = vec![
|
||||
Uuid::new_v4(),
|
||||
Uuid::new_v4(),
|
||||
Uuid::new_v4(),
|
||||
];
|
||||
|
||||
// In a real test, we would use a test database
|
||||
// For now, we'll just assert that the function doesn't panic
|
||||
let result = delete_favorites_bulk(&user, &favorite_ids).await;
|
||||
assert!(result.is_ok() || result.is_err());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@ mod delete_favorite;
|
|||
mod update_favorites;
|
||||
|
||||
pub use list_favorites::list_favorites;
|
||||
pub use create_favorite::{create_favorite, CreateFavoriteReq};
|
||||
pub use delete_favorite::delete_favorite;
|
||||
pub use create_favorite::{create_favorite, create_favorites_bulk, CreateFavoriteReq};
|
||||
pub use delete_favorite::{delete_favorite, delete_favorites_bulk};
|
||||
pub use update_favorites::update_favorites;
|
||||
pub use favorites_utils::{FavoriteObject, FavoriteIdAndType, UserFavoritesReq};
|
||||
|
|
|
@ -1,24 +1,18 @@
|
|||
use axum::{extract::Json, http::StatusCode, Extension};
|
||||
use handlers::favorites::{create_favorite, CreateFavoriteReq, FavoriteObject, FavoriteIdAndType};
|
||||
use handlers::favorites::{create_favorites_bulk, CreateFavoriteReq, FavoriteObject};
|
||||
use middleware::AuthenticatedUser;
|
||||
|
||||
pub async fn create_favorite_handler(
|
||||
Extension(user): Extension<AuthenticatedUser>,
|
||||
Json(payload): Json<FavoriteIdAndType>,
|
||||
Json(favorites): Json<Vec<CreateFavoriteReq>>,
|
||||
) -> Result<Json<Vec<FavoriteObject>>, (StatusCode, String)> {
|
||||
let req = CreateFavoriteReq {
|
||||
id: payload.id,
|
||||
asset_type: payload.type_,
|
||||
index: None,
|
||||
};
|
||||
|
||||
match create_favorite(&user, &req).await {
|
||||
match create_favorites_bulk(&user, &favorites).await {
|
||||
Ok(favorites) => Ok(Json(favorites)),
|
||||
Err(e) => {
|
||||
tracing::error!("Error creating favorite: {:?}", e);
|
||||
tracing::error!("Error creating favorites: {:?}", e);
|
||||
Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Error creating favorite: {:?}", e),
|
||||
format!("Error creating favorites: {:?}", e),
|
||||
))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
use axum::{
|
||||
extract::{Json, Path, Extension},
|
||||
extract::{Extension, Json, Path},
|
||||
http::StatusCode,
|
||||
};
|
||||
use uuid::Uuid;
|
||||
use handlers::favorites::{delete_favorite, FavoriteObject};
|
||||
use handlers::favorites::{delete_favorite, delete_favorites_bulk, FavoriteObject};
|
||||
use middleware::AuthenticatedUser;
|
||||
use uuid::Uuid;
|
||||
|
||||
// Handler for DELETE /:id path parameter
|
||||
pub async fn delete_favorite_handler(
|
||||
Extension(user): Extension<AuthenticatedUser>,
|
||||
Path(id): Path<Uuid>,
|
||||
|
@ -21,3 +22,20 @@ pub async fn delete_favorite_handler(
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handler for DELETE / with request body
|
||||
pub async fn delete_favorites_bulk_handler(
|
||||
Extension(user): Extension<AuthenticatedUser>,
|
||||
Json(ids): Json<Vec<Uuid>>,
|
||||
) -> Result<Json<Vec<FavoriteObject>>, (StatusCode, String)> {
|
||||
match delete_favorites_bulk(&user, &ids).await {
|
||||
Ok(favorites) => Ok(Json(favorites)),
|
||||
Err(e) => {
|
||||
tracing::error!("Error deleting favorites: {:?}", e);
|
||||
Err((
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
format!("Error deleting favorites: {:?}", e),
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -14,5 +14,6 @@ pub fn router() -> Router {
|
|||
.route("/", get(list_favorites::list_favorites_handler))
|
||||
.route("/", post(create_favorite::create_favorite_handler))
|
||||
.route("/:id", delete(delete_favorite::delete_favorite_handler))
|
||||
.route("/", delete(delete_favorite::delete_favorites_bulk_handler))
|
||||
.route("/", put(update_favorites::update_favorites_handler))
|
||||
}
|
||||
|
|
|
@ -0,0 +1,114 @@
|
|||
use anyhow::Result;
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use database::enums::AssetType;
|
||||
use crate::common::{
|
||||
setup_test_env, TestApp, TestDb, TestTaggable, FixtureBuilder,
|
||||
assertions::ResponseAssertions,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_single_favorite() -> Result<()> {
|
||||
// Setup
|
||||
setup_test_env();
|
||||
let test_db = TestDb::new().await?;
|
||||
test_db.setup_test_data().await?;
|
||||
|
||||
let test_app = TestApp::new().await?;
|
||||
let client = test_app.client();
|
||||
|
||||
// Create a test user and dashboard to favorite
|
||||
let user = FixtureBuilder::create_user().build();
|
||||
let dashboard = FixtureBuilder::create_dashboard().with_user(&user).build();
|
||||
|
||||
// Save fixtures to database
|
||||
let mut conn = test_db.pool.get().await?;
|
||||
user.save(&mut conn).await?;
|
||||
dashboard.save(&mut conn).await?;
|
||||
|
||||
// Test single favorite creation
|
||||
let response = client
|
||||
.post(&format!("/api/users/me/favorites"))
|
||||
.json(&json!({
|
||||
"id": dashboard.id,
|
||||
"asset_type": "DashboardFile"
|
||||
}))
|
||||
.header("x-user-id", user.id.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
response.assert_status_ok()?;
|
||||
let body = response.json::<Vec<serde_json::Value>>().await?;
|
||||
|
||||
// Assert that response contains the favorited dashboard
|
||||
assert!(!body.is_empty());
|
||||
let favorited_item = body.iter().find(|item| {
|
||||
item["id"] == dashboard.id.to_string() && item["asset_type"] == "DashboardFile"
|
||||
});
|
||||
assert!(favorited_item.is_some());
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_create_multiple_favorites() -> Result<()> {
|
||||
// Setup
|
||||
setup_test_env();
|
||||
let test_db = TestDb::new().await?;
|
||||
test_db.setup_test_data().await?;
|
||||
|
||||
let test_app = TestApp::new().await?;
|
||||
let client = test_app.client();
|
||||
|
||||
// Create a test user and multiple assets to favorite
|
||||
let user = FixtureBuilder::create_user().build();
|
||||
let dashboard1 = FixtureBuilder::create_dashboard().with_user(&user).build();
|
||||
let dashboard2 = FixtureBuilder::create_dashboard().with_user(&user).build();
|
||||
let collection = FixtureBuilder::create_collection().with_user(&user).build();
|
||||
|
||||
// Save fixtures to database
|
||||
let mut conn = test_db.pool.get().await?;
|
||||
user.save(&mut conn).await?;
|
||||
dashboard1.save(&mut conn).await?;
|
||||
dashboard2.save(&mut conn).await?;
|
||||
collection.save(&mut conn).await?;
|
||||
|
||||
// Test bulk favorite creation
|
||||
let response = client
|
||||
.post(&format!("/api/users/me/favorites"))
|
||||
.json(&json!([
|
||||
{
|
||||
"id": dashboard1.id,
|
||||
"asset_type": "DashboardFile"
|
||||
},
|
||||
{
|
||||
"id": dashboard2.id,
|
||||
"asset_type": "DashboardFile"
|
||||
},
|
||||
{
|
||||
"id": collection.id,
|
||||
"asset_type": "Collection"
|
||||
}
|
||||
]))
|
||||
.header("x-user-id", user.id.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
response.assert_status_ok()?;
|
||||
let body = response.json::<Vec<serde_json::Value>>().await?;
|
||||
|
||||
// Assert that response contains all favorited items
|
||||
assert!(body.len() >= 3);
|
||||
|
||||
// Check if all three assets are in the favorites
|
||||
let dashboard1_found = body.iter().any(|item| item["id"] == dashboard1.id.to_string());
|
||||
let dashboard2_found = body.iter().any(|item| item["id"] == dashboard2.id.to_string());
|
||||
let collection_found = body.iter().any(|item| item["id"] == collection.id.to_string());
|
||||
|
||||
assert!(dashboard1_found, "Dashboard 1 not found in favorites");
|
||||
assert!(dashboard2_found, "Dashboard 2 not found in favorites");
|
||||
assert!(collection_found, "Collection not found in favorites");
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,128 @@
|
|||
use anyhow::Result;
|
||||
use serde_json::json;
|
||||
use uuid::Uuid;
|
||||
|
||||
use database::enums::AssetType;
|
||||
use handlers::favorites::{create_favorite, CreateFavoriteReq};
|
||||
use crate::common::{
|
||||
setup_test_env, TestApp, TestDb, TestTaggable, FixtureBuilder,
|
||||
assertions::ResponseAssertions,
|
||||
};
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_single_favorite_by_id() -> Result<()> {
|
||||
// Setup
|
||||
setup_test_env();
|
||||
let test_db = TestDb::new().await?;
|
||||
test_db.setup_test_data().await?;
|
||||
|
||||
let test_app = TestApp::new().await?;
|
||||
let client = test_app.client();
|
||||
|
||||
// Create a test user and dashboard to favorite
|
||||
let user = FixtureBuilder::create_user().build();
|
||||
let dashboard = FixtureBuilder::create_dashboard().with_user(&user).build();
|
||||
|
||||
// Save fixtures to database
|
||||
let mut conn = test_db.pool.get().await?;
|
||||
user.save(&mut conn).await?;
|
||||
dashboard.save(&mut conn).await?;
|
||||
|
||||
// Create favorite first
|
||||
let response = client
|
||||
.post(&format!("/api/users/me/favorites"))
|
||||
.json(&json!({
|
||||
"id": dashboard.id,
|
||||
"asset_type": "DashboardFile"
|
||||
}))
|
||||
.header("x-user-id", user.id.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
response.assert_status_ok()?;
|
||||
|
||||
// Now delete the favorite using path parameter
|
||||
let response = client
|
||||
.delete(&format!("/api/users/me/favorites/{}", dashboard.id))
|
||||
.header("x-user-id", user.id.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
response.assert_status_ok()?;
|
||||
let body = response.json::<Vec<serde_json::Value>>().await?;
|
||||
|
||||
// Assert that the favorite is no longer in the list
|
||||
let still_favorited = body.iter().any(|item| item["id"] == dashboard.id.to_string());
|
||||
assert!(!still_favorited, "Dashboard is still in favorites after deletion");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_delete_multiple_favorites_bulk() -> Result<()> {
|
||||
// Setup
|
||||
setup_test_env();
|
||||
let test_db = TestDb::new().await?;
|
||||
test_db.setup_test_data().await?;
|
||||
|
||||
let test_app = TestApp::new().await?;
|
||||
let client = test_app.client();
|
||||
|
||||
// Create a test user and multiple assets to favorite
|
||||
let user = FixtureBuilder::create_user().build();
|
||||
let dashboard1 = FixtureBuilder::create_dashboard().with_user(&user).build();
|
||||
let dashboard2 = FixtureBuilder::create_dashboard().with_user(&user).build();
|
||||
let collection = FixtureBuilder::create_collection().with_user(&user).build();
|
||||
|
||||
// Save fixtures to database
|
||||
let mut conn = test_db.pool.get().await?;
|
||||
user.save(&mut conn).await?;
|
||||
dashboard1.save(&mut conn).await?;
|
||||
dashboard2.save(&mut conn).await?;
|
||||
collection.save(&mut conn).await?;
|
||||
|
||||
// Create favorites first
|
||||
let response = client
|
||||
.post(&format!("/api/users/me/favorites"))
|
||||
.json(&json!([
|
||||
{
|
||||
"id": dashboard1.id,
|
||||
"asset_type": "DashboardFile"
|
||||
},
|
||||
{
|
||||
"id": dashboard2.id,
|
||||
"asset_type": "DashboardFile"
|
||||
},
|
||||
{
|
||||
"id": collection.id,
|
||||
"asset_type": "Collection"
|
||||
}
|
||||
]))
|
||||
.header("x-user-id", user.id.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
response.assert_status_ok()?;
|
||||
|
||||
// Now delete multiple favorites using bulk endpoint
|
||||
let response = client
|
||||
.delete(&format!("/api/users/me/favorites"))
|
||||
.json(&json!([dashboard1.id, dashboard2.id]))
|
||||
.header("x-user-id", user.id.to_string())
|
||||
.send()
|
||||
.await?;
|
||||
|
||||
response.assert_status_ok()?;
|
||||
let body = response.json::<Vec<serde_json::Value>>().await?;
|
||||
|
||||
// Assert that the deleted favorites are no longer in the list
|
||||
let dashboard1_found = body.iter().any(|item| item["id"] == dashboard1.id.to_string());
|
||||
let dashboard2_found = body.iter().any(|item| item["id"] == dashboard2.id.to_string());
|
||||
let collection_found = body.iter().any(|item| item["id"] == collection.id.to_string());
|
||||
|
||||
assert!(!dashboard1_found, "Dashboard 1 is still in favorites after deletion");
|
||||
assert!(!dashboard2_found, "Dashboard 2 is still in favorites after deletion");
|
||||
assert!(collection_found, "Collection should still be in favorites");
|
||||
|
||||
Ok(())
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
pub mod create_favorites_bulk_test;
|
||||
pub mod delete_favorites_bulk_test;
|
|
@ -3,6 +3,7 @@ pub mod chats;
|
|||
pub mod dashboards;
|
||||
pub mod collections;
|
||||
pub mod data_sources;
|
||||
pub mod favorites;
|
||||
pub mod metrics;
|
||||
pub mod organizations;
|
||||
pub mod routes;
|
||||
|
|
Loading…
Reference in New Issue