tenant deletion/reset
This commit is contained in:
@@ -0,0 +1,272 @@
|
||||
use anyhow::{anyhow, Result};
|
||||
use axum::http::StatusCode;
|
||||
use diesel::prelude::*;
|
||||
use papercrate::jobs::{enqueue_job, JOB_DELETE_TENANT};
|
||||
use papercrate::models::TenantStatus;
|
||||
use papercrate::schema::{documents, tenants, user_memberships};
|
||||
use papercrate::test_support::{acquire_db_lock, body_to_vec, TestApp, TestUserRole};
|
||||
use papercrate::workers::tenants::DeleteTenantJob;
|
||||
use papercrate::workers::JobHandler;
|
||||
use serde_json::json;
|
||||
use std::sync::Arc;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_tenant_job_keeps_tenant_when_requested() -> Result<()> {
|
||||
let _lock = acquire_db_lock().await;
|
||||
let app = TestApp::new().await?;
|
||||
|
||||
let password = "delete-keep";
|
||||
app.insert_user("tenant-keep", TestUserRole::Owner).await?;
|
||||
let token = app.login_token("tenant-keep", password).await?;
|
||||
|
||||
let upload = app
|
||||
.upload_document(
|
||||
"/api/documents",
|
||||
"keep.pdf",
|
||||
"application/pdf",
|
||||
b"keep",
|
||||
None,
|
||||
&token,
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(upload.status(), StatusCode::CREATED);
|
||||
body_to_vec(upload.into_body()).await?;
|
||||
|
||||
let tenant_id = app
|
||||
.state
|
||||
.tenants
|
||||
.get_by_name("test_tenant")
|
||||
.map_err(|err| anyhow!("tenant lookup failed: {err:?}"))?
|
||||
.id;
|
||||
set_tenant_status(&app, tenant_id, TenantStatus::Deleting).await?;
|
||||
|
||||
let job = enqueue_delete_job(&app, tenant_id, false).await?;
|
||||
let handler = DeleteTenantJob::new();
|
||||
let state = Arc::new(app.state.clone());
|
||||
let storage = app
|
||||
.state
|
||||
.storage_for_tenant(tenant_id)
|
||||
.map_err(|err| anyhow!("storage unavailable: {err:?}"))?;
|
||||
|
||||
let execution = handler.handle(state, job, storage).await;
|
||||
assert!(matches!(
|
||||
execution,
|
||||
papercrate::workers::JobExecution::Success
|
||||
));
|
||||
|
||||
assert_eq!(app.storage().object_count().await, 0);
|
||||
|
||||
let tenant_id_for_check = tenant_id;
|
||||
app.with_conn(move |conn| {
|
||||
use diesel::dsl::count_star;
|
||||
|
||||
let doc_count: i64 = documents::table.select(count_star()).get_result(conn)?;
|
||||
assert_eq!(doc_count, 0);
|
||||
|
||||
let membership_count: i64 = user_memberships::table
|
||||
.select(count_star())
|
||||
.get_result(conn)?;
|
||||
assert!(membership_count > 0, "memberships should remain");
|
||||
|
||||
let status: TenantStatus = tenants::table
|
||||
.find(tenant_id_for_check)
|
||||
.select(tenants::status)
|
||||
.first(conn)?;
|
||||
assert_eq!(status, TenantStatus::Suspended);
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
app.cleanup().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_tenant_job_can_reset_tenant_to_active() -> Result<()> {
|
||||
let _lock = acquire_db_lock().await;
|
||||
let app = TestApp::new().await?;
|
||||
|
||||
let password = "delete-reset";
|
||||
app.insert_user("tenant-reset", TestUserRole::Owner).await?;
|
||||
let token = app.login_token("tenant-reset", password).await?;
|
||||
|
||||
let upload = app
|
||||
.upload_document(
|
||||
"/api/documents",
|
||||
"reset.pdf",
|
||||
"application/pdf",
|
||||
b"reset",
|
||||
None,
|
||||
&token,
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(upload.status(), StatusCode::CREATED);
|
||||
body_to_vec(upload.into_body()).await?;
|
||||
|
||||
let tenant_id = app
|
||||
.state
|
||||
.tenants
|
||||
.get_by_name("test_tenant")
|
||||
.map_err(|err| anyhow!("tenant lookup failed: {err:?}"))?
|
||||
.id;
|
||||
set_tenant_status(&app, tenant_id, TenantStatus::Deleting).await?;
|
||||
|
||||
let job = enqueue_delete_job_with_status(&app, tenant_id, false, Some("active")).await?;
|
||||
let handler = DeleteTenantJob::new();
|
||||
let state = Arc::new(app.state.clone());
|
||||
let storage = app
|
||||
.state
|
||||
.storage_for_tenant(tenant_id)
|
||||
.map_err(|err| anyhow!("storage unavailable: {err:?}"))?;
|
||||
|
||||
let execution = handler.handle(state, job, storage).await;
|
||||
assert!(matches!(
|
||||
execution,
|
||||
papercrate::workers::JobExecution::Success
|
||||
));
|
||||
|
||||
assert_eq!(app.storage().object_count().await, 0);
|
||||
|
||||
let tenant_id_for_check = tenant_id;
|
||||
app.with_conn(move |conn| {
|
||||
use diesel::dsl::count_star;
|
||||
|
||||
let doc_count: i64 = documents::table.select(count_star()).get_result(conn)?;
|
||||
assert_eq!(doc_count, 0);
|
||||
|
||||
let membership_count: i64 = user_memberships::table
|
||||
.select(count_star())
|
||||
.get_result(conn)?;
|
||||
assert!(membership_count > 0, "memberships should remain");
|
||||
|
||||
let status: TenantStatus = tenants::table
|
||||
.find(tenant_id_for_check)
|
||||
.select(tenants::status)
|
||||
.first(conn)?;
|
||||
assert_eq!(status, TenantStatus::Active);
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
app.cleanup().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn delete_tenant_job_removes_tenant_entirely() -> Result<()> {
|
||||
let _lock = acquire_db_lock().await;
|
||||
let app = TestApp::new().await?;
|
||||
|
||||
let password = "delete-remove";
|
||||
app.insert_user("tenant-remove", TestUserRole::Owner)
|
||||
.await?;
|
||||
let token = app.login_token("tenant-remove", password).await?;
|
||||
|
||||
let upload = app
|
||||
.upload_document(
|
||||
"/api/documents",
|
||||
"remove.pdf",
|
||||
"application/pdf",
|
||||
b"remove",
|
||||
None,
|
||||
&token,
|
||||
)
|
||||
.await?;
|
||||
assert_eq!(upload.status(), StatusCode::CREATED);
|
||||
body_to_vec(upload.into_body()).await?;
|
||||
|
||||
let tenant_id = app
|
||||
.state
|
||||
.tenants
|
||||
.get_by_name("test_tenant")
|
||||
.map_err(|err| anyhow!("tenant lookup failed: {err:?}"))?
|
||||
.id;
|
||||
set_tenant_status(&app, tenant_id, TenantStatus::Deleting).await?;
|
||||
|
||||
let job = enqueue_delete_job(&app, tenant_id, true).await?;
|
||||
let handler = DeleteTenantJob::new();
|
||||
let state = Arc::new(app.state.clone());
|
||||
let storage = app
|
||||
.state
|
||||
.storage_for_tenant(tenant_id)
|
||||
.map_err(|err| anyhow!("storage unavailable: {err:?}"))?;
|
||||
|
||||
let execution = handler.handle(state, job, storage).await;
|
||||
assert!(matches!(
|
||||
execution,
|
||||
papercrate::workers::JobExecution::Success
|
||||
));
|
||||
|
||||
assert_eq!(app.storage().object_count().await, 0);
|
||||
|
||||
let tenant_id_for_check = tenant_id;
|
||||
app.with_conn(move |conn| {
|
||||
use diesel::dsl::count_star;
|
||||
|
||||
let doc_count: i64 = documents::table.select(count_star()).get_result(conn)?;
|
||||
assert_eq!(doc_count, 0);
|
||||
|
||||
let membership_count: i64 = user_memberships::table
|
||||
.select(count_star())
|
||||
.get_result(conn)?;
|
||||
assert_eq!(membership_count, 0, "memberships should be removed");
|
||||
|
||||
let exists: Option<TenantStatus> = tenants::table
|
||||
.find(tenant_id_for_check)
|
||||
.select(tenants::status)
|
||||
.first(conn)
|
||||
.optional()?;
|
||||
assert!(exists.is_none(), "tenant row should be deleted");
|
||||
Ok(())
|
||||
})
|
||||
.await?;
|
||||
|
||||
app.cleanup().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
async fn set_tenant_status(app: &TestApp, tenant_id: Uuid, status: TenantStatus) -> Result<()> {
|
||||
app.with_conn(move |conn| {
|
||||
diesel::update(tenants::table.find(tenant_id))
|
||||
.set(tenants::status.eq(status))
|
||||
.execute(conn)?;
|
||||
Ok(())
|
||||
})
|
||||
.await
|
||||
}
|
||||
|
||||
async fn enqueue_delete_job(
|
||||
app: &TestApp,
|
||||
tenant_id: Uuid,
|
||||
remove_tenant: bool,
|
||||
) -> Result<papercrate::models::Job> {
|
||||
enqueue_delete_job_with_status(app, tenant_id, remove_tenant, None).await
|
||||
}
|
||||
|
||||
async fn enqueue_delete_job_with_status(
|
||||
app: &TestApp,
|
||||
tenant_id: Uuid,
|
||||
remove_tenant: bool,
|
||||
final_status: Option<&'static str>,
|
||||
) -> Result<papercrate::models::Job> {
|
||||
app.with_conn(move |conn| {
|
||||
let tenant_name: String = tenants::table
|
||||
.find(tenant_id)
|
||||
.select(tenants::name)
|
||||
.first(conn)
|
||||
.map_err(|err| anyhow!("tenant lookup failed: {err}"))?;
|
||||
|
||||
let mut payload = json!({
|
||||
"remove_tenant": remove_tenant,
|
||||
"tenant_name": tenant_name,
|
||||
});
|
||||
if let Some(status) = final_status {
|
||||
payload["final_status"] = json!(status);
|
||||
}
|
||||
|
||||
enqueue_job(conn, tenant_id, JOB_DELETE_TENANT, payload, None)
|
||||
.map_err(|err| anyhow!("enqueue failed: {err}"))
|
||||
})
|
||||
.await
|
||||
}
|
||||
Reference in New Issue
Block a user