FEATURE: Provide a task to cleanup the database before uninstalling the plugin (#56)

FEATURE: Provide a task to cleanup the database before uninstalling the plugin (#56)

diff --git a/README.md b/README.md
index f2e90e7..18bb35d 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,13 @@ Help make this plugin better by submitting a PR.  It's as easy as 1-2-3.
 
 This project uses the MIT-LICENSE.
 
+## Uninstallation
+
+If you wish to uninstall this plugin permanently, you'll have to remove the objects it created **first**. You could do so by executing the following rake task:
+
+`bundle exec rake akismet_uninstall:delete_reviewables`
+
+:warning: THIS ACTION CANNOT BE UNDONE. BE SURE YOU REALLY WANT TO UNINSTALL THE PLUGIN :warning:
 
 ## Issues
 
diff --git a/lib/tasks/uninstall.rake b/lib/tasks/uninstall.rake
new file mode 100644
index 0000000..07c16aa
--- /dev/null
+++ b/lib/tasks/uninstall.rake
@@ -0,0 +1,19 @@
+# frozen_string_literal: true
+
+desc 'Delete reviewables and post custom fields created by this plugin'
+task 'akismet_uninstall:delete_reviewables' => :environment do
+  PostCustomField.where(name: DiscourseAkismet::Bouncer::AKISMET_STATE).delete_all
+
+  delete_association(ReviewableScore)
+  delete_association(ReviewableHistory)
+
+  ReviewableAkismetPost.delete_all
+  ReviewableAkismetUser.delete_all
+end
+
+def delete_association(klass)
+  klass
+    .joins(:reviewable)
+    .where(reviewables: { type: %w[ReviewableAkismetPost ReviewableAkismetUser] })
+    .delete_all
+end
diff --git a/spec/fabricators/reviewable_akismet_post_fabricator.rb b/spec/fabricators/reviewable_akismet_post_fabricator.rb
index 692db7a..df3bd7e 100644
--- a/spec/fabricators/reviewable_akismet_post_fabricator.rb
+++ b/spec/fabricators/reviewable_akismet_post_fabricator.rb
@@ -3,8 +3,16 @@
 Fabricator(:reviewable_akismet_post) do
   reviewable_by_moderator true
   type 'ReviewableAkismetPost'
-  created_by { Fabricate(:user) }
+  created_by { Discourse.system_user }
   topic
   target_type 'Post'
   target { Fabricate(:post) }
 end
+
+Fabricator(:reviewable_akismet_user) do
+  reviewable_by_moderator true
+  type 'ReviewableAkismetUser'
+  created_by { Discourse.system_user }
+  target_type 'User'
+  target { Fabricate(:user) }
+end
diff --git a/spec/lib/tasks/uninstall_spec.rb b/spec/lib/tasks/uninstall_spec.rb
new file mode 100644
index 0000000..81bdf11
--- /dev/null
+++ b/spec/lib/tasks/uninstall_spec.rb
@@ -0,0 +1,86 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+describe 'Uninstall plugin rake task' do
+  describe '#remove_reviewables' do
+    let(:flagged_post) { Fabricate(:reviewable_flagged_post) }
+
+    let(:akismet_flagged_reviewable) { Fabricate(:reviewable_akismet_post) }
+    let(:akismet_flagged_post) { akismet_flagged_reviewable.target }
+    let(:akismet_flagged_user) { Fabricate(:reviewable_akismet_user) }
+
+    before do
+      Rake::Task.clear
+      Discourse::Application.load_tasks
+
+      SiteSetting.akismet_api_key = 'fake_key'
+      DiscourseAkismet::Bouncer.new.move_to_state(akismet_flagged_post, 'confirmed_spam')
+
+      add_score(flagged_post)
+      add_score(akismet_flagged_user)
+      add_score(akismet_flagged_reviewable)
+    end
+
+    it 'deletes reviewable objects' do
+      run_task
+
+      expect { akismet_flagged_reviewable.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect { akismet_flagged_user.reload }.to raise_error(ActiveRecord::RecordNotFound)
+      expect(flagged_post.reload).to be_present
+    end
+
+    it 'deletes the scores of those reviewable objects' do
+      expect(object_exists?(ReviewableScore, akismet_flagged_reviewable)).to eq(true)
+      expect(object_exists?(ReviewableScore, akismet_flagged_user)).to eq(true)
+      expect(object_exists?(ReviewableScore, flagged_post)).to eq(true)
+
+      run_task
+
+      expect(object_exists?(ReviewableScore, akismet_flagged_reviewable)).to eq(false)
+      expect(object_exists?(ReviewableScore, akismet_flagged_user)).to eq(false)
+      expect(object_exists?(ReviewableScore, flagged_post)).to eq(true)
+    end
+
+    it 'deletes the history of those reviewable objects' do
+      admin = Fabricate(:admin)
+      akismet_flagged_reviewable.perform(admin, :not_spam)
+      akismet_flagged_user.perform(admin, :not_spam)
+      flagged_post.perform(admin, :ignore)
+
+      expect(object_exists?(ReviewableHistory, akismet_flagged_reviewable)).to eq(true)
+      expect(object_exists?(ReviewableHistory, akismet_flagged_user)).to eq(true)
+      expect(object_exists?(ReviewableHistory, flagged_post)).to eq(true)
+
+      run_task
+
+      expect(object_exists?(ReviewableHistory, akismet_flagged_reviewable)).to eq(false)
+      expect(object_exists?(ReviewableHistory, akismet_flagged_user)).to eq(false)
+      expect(object_exists?(ReviewableHistory, flagged_post)).to eq(true)
+    end
+
+    it 'removes akismet custom fields' do
+      run_task
+
+      akismet_custom_field = akismet_flagged_post
+        .reload.custom_fields[DiscourseAkismet::Bouncer::AKISMET_STATE]
+
+      expect(akismet_custom_field).to be_nil
+    end
+
+    def object_exists?(klass, reviewable)
+      klass.where(reviewable_id: reviewable.id).exists?
+    end
+
+    def add_score(reviewable)
+      reviewable.add_score(
+        reviewable.created_by, PostActionType.types[:spam],
+        created_at: reviewable.created_at, reason: 'spam'
+      )
+    end
+
+    def run_task
+      Rake::Task['akismet_uninstall:delete_reviewables'].invoke
+    end
+  end
+end

GitHub sha: fef2664e

This commit appears in #56 which was approved by eviltrout. It was merged by romanrizzi.