From b7f0988399a3cb9a33b8384ac019de8f08fce3d1 Mon Sep 17 00:00:00 2001 From: aecsocket Date: Fri, 31 Oct 2025 12:04:01 -0700 Subject: [PATCH] Decouple project deletion from thread deletion (#4673) * Decouple project deletion from thread deletion * Allow a thread to exist without a project * attempt 2 * Modify migration to set orphaned threads' mods to NULL instead of removing constraint entirely * Use mod PAT for mod threads --- ...0125413_decouple_threads_from_projects.sql | 5 +++ .../src/database/models/project_item.rs | 3 -- apps/labrinth/tests/project.rs | 44 +++++++++++++++++++ 3 files changed, 49 insertions(+), 3 deletions(-) create mode 100644 apps/labrinth/migrations/20251030125413_decouple_threads_from_projects.sql diff --git a/apps/labrinth/migrations/20251030125413_decouple_threads_from_projects.sql b/apps/labrinth/migrations/20251030125413_decouple_threads_from_projects.sql new file mode 100644 index 00000000..dd5bcc23 --- /dev/null +++ b/apps/labrinth/migrations/20251030125413_decouple_threads_from_projects.sql @@ -0,0 +1,5 @@ +ALTER TABLE threads +DROP CONSTRAINT threads_mod_id_fkey, +ADD CONSTRAINT threads_mod_id_fkey +FOREIGN KEY (mod_id) REFERENCES mods(id) +ON DELETE SET NULL; diff --git a/apps/labrinth/src/database/models/project_item.rs b/apps/labrinth/src/database/models/project_item.rs index 362907bd..3ed30b17 100644 --- a/apps/labrinth/src/database/models/project_item.rs +++ b/apps/labrinth/src/database/models/project_item.rs @@ -384,9 +384,6 @@ impl DBProject { .execute(&mut **transaction) .await?; - models::DBThread::remove_full(project.thread_id, transaction) - .await?; - sqlx::query!( " UPDATE reports diff --git a/apps/labrinth/tests/project.rs b/apps/labrinth/tests/project.rs index 893988a9..c337da78 100644 --- a/apps/labrinth/tests/project.rs +++ b/apps/labrinth/tests/project.rs @@ -1355,6 +1355,50 @@ async fn projects_various_visibility() { .await; } +#[actix_rt::test] +async fn test_thread_deleted_with_project() { + with_test_environment( + None, + |test_env: TestEnvironment| async move { + let api = &test_env.api; + + let alpha_project_id = &test_env.dummy.project_alpha.project_id; + let alpha_thread_id = &test_env.dummy.project_alpha.thread_id; + + // Verify the thread exists initially + let resp = api.get_thread(alpha_thread_id, USER_USER_PAT).await; + assert_status!(&resp, StatusCode::OK); + + // Write a message to the thread to confirm it's working + let resp = api + .write_to_thread( + alpha_thread_id, + "text", + "Test message before project deletion", + USER_USER_PAT, + ) + .await; + assert_status!(&resp, StatusCode::NO_CONTENT); + + // Check that the thread exists before project deletion + // Use a moderator PAT since moderation threads are not visible to users + let resp = api.get_thread(alpha_thread_id, MOD_USER_PAT).await; + assert_status!(&resp, StatusCode::OK); + + // Delete the project + let resp = + api.remove_project(alpha_project_id, USER_USER_PAT).await; + assert_status!(&resp, StatusCode::NO_CONTENT); + + // Check that the thread still exists after project deletion + // Also use mod PAT here + let resp = api.get_thread(alpha_thread_id, MOD_USER_PAT).await; + assert_status!(&resp, StatusCode::OK); + }, + ) + .await; +} + // Route tests: // TODO: Missing routes on projects // TODO: using permissions/scopes, can we SEE projects existence that we are not allowed to? (ie 401 instead of 404)