[deploy] Finalize deleting user associations (#5624)

* Prepare factories and associations for Users::DeleteActivity specs

* Altered user associations, delete the needed associations in Users::DeleteActivity

* Moved and refined user deletion spec

* Removed useless commented user associations

* Refactored BackupData factory

* Refactored ProfilePin factory

* Refactored Reaction factory

* Fixed specs: Keep default _type in factories or specify it in tests

* Ok, notes shouldn't be deleted on user deletion

* Fixed creating reactable for specs

* Specify commentable/commentable_type explicitly when creating data for tests

* Fixed typo in the comments spec

* Removed unnneeded space

* Added aggregate_failures for testing user associations deletion

* Keep feedback_messages while deleting user activities
This commit is contained in:
Anna Buianova 2020-01-23 19:29:47 +03:00 committed by Andy Zhao
parent 7d93594447
commit 01456fe029
48 changed files with 221 additions and 84 deletions

View file

@ -1,8 +1,7 @@
class FeedbackMessage < ApplicationRecord
belongs_to :offender, foreign_key: "offender_id", class_name: "User", optional: true, inverse_of: :feedback_messages
belongs_to :reviewer, foreign_key: "reviewer_id", class_name: "User", optional: true, inverse_of: :feedback_messages
belongs_to :reporter, foreign_key: "reporter_id", class_name: "User", optional: true, inverse_of: :feedback_messages
belongs_to :affected, foreign_key: "affected_id", class_name: "User", optional: true, inverse_of: :feedback_messages
belongs_to :offender, foreign_key: "offender_id", class_name: "User", optional: true, inverse_of: :offender_feedback_messages
belongs_to :reporter, foreign_key: "reporter_id", class_name: "User", optional: true, inverse_of: :reporter_feedback_messages
belongs_to :affected, foreign_key: "affected_id", class_name: "User", optional: true, inverse_of: :affected_feedback_messages
has_many :notes, as: :noteable, inverse_of: :noteable, dependent: :destroy
validates :feedback_type, :message, presence: true

View file

@ -21,34 +21,39 @@ class User < ApplicationRecord
has_many :badges, through: :badge_achievements
has_many :collections, dependent: :destroy
has_many :comments, dependent: :destroy
has_many :email_messages, class_name: "Ahoy::Message"
has_many :email_messages, class_name: "Ahoy::Message", dependent: :destroy
has_many :github_repos, dependent: :destroy
has_many :identities, dependent: :destroy
has_many :mentions, dependent: :destroy
has_many :messages, dependent: :destroy
has_many :notes, as: :noteable, inverse_of: :noteable
has_many :profile_pins, as: :profile, inverse_of: :profile
has_many :authored_notes, as: :author, inverse_of: :author, class_name: "Note"
has_many :profile_pins, as: :profile, inverse_of: :profile, dependent: :delete_all
has_many :authored_notes, inverse_of: :author, class_name: "Note", foreign_key: :author_id, dependent: :delete_all
has_many :notifications, dependent: :destroy
has_many :reactions, dependent: :destroy
has_many :tweets, dependent: :destroy
has_many :chat_channel_memberships, dependent: :destroy
has_many :chat_channels, through: :chat_channel_memberships
has_many :notification_subscriptions, dependent: :destroy
has_many :feedback_messages
has_many :rating_votes
has_many :offender_feedback_messages, class_name: "FeedbackMessage", inverse_of: :offender, foreign_key: :offender_id, dependent: :nullify
has_many :reporter_feedback_messages, class_name: "FeedbackMessage", inverse_of: :reporter, foreign_key: :reporter_id, dependent: :nullify
has_many :affected_feedback_messages, class_name: "FeedbackMessage", inverse_of: :affected, foreign_key: :affected_id, dependent: :nullify
has_many :rating_votes, dependent: :destroy
has_many :html_variants, dependent: :destroy
has_many :page_views
has_many :credits
has_many :page_views, dependent: :destroy
has_many :credits, dependent: :destroy
has_many :classified_listings
has_many :poll_votes
has_many :poll_skips
has_many :backup_data, foreign_key: "instance_user_id", inverse_of: :instance_user, class_name: "BackupData"
has_many :display_ad_events
has_many :poll_votes, dependent: :destroy
has_many :poll_skips, dependent: :destroy
has_many :backup_data, foreign_key: "instance_user_id", inverse_of: :instance_user, class_name: "BackupData", dependent: :delete_all
has_many :display_ad_events, dependent: :destroy
has_many :access_grants, class_name: "Doorkeeper::AccessGrant", foreign_key: :resource_owner_id, inverse_of: :resource_owner, dependent: :delete_all
has_many :access_tokens, class_name: "Doorkeeper::AccessToken", foreign_key: :resource_owner_id, inverse_of: :resource_owner, dependent: :delete_all
has_many :webhook_endpoints, class_name: "Webhook::Endpoint", foreign_key: :user_id, inverse_of: :user, dependent: :delete_all
has_many :user_blocks
has_many :blocker_blocks, class_name: "UserBlock", foreign_key: :blocker_id, inverse_of: :blocker, dependent: :delete_all
has_many :blocked_blocks, class_name: "UserBlock", foreign_key: :blocked_id, inverse_of: :blocked, dependent: :delete_all
has_one :pro_membership, dependent: :destroy
has_many :created_podcasts, class_name: "Podcast", foreign_key: :creator_id, inverse_of: :creator, dependent: :nullify

View file

@ -1,6 +1,6 @@
class UserBlock < ApplicationRecord
belongs_to :blocker, foreign_key: "blocker_id", class_name: "User", inverse_of: :user_blocks
belongs_to :blocked, foreign_key: "blocked_id", class_name: "User", inverse_of: :user_blocks
belongs_to :blocker, foreign_key: "blocker_id", class_name: "User", inverse_of: :blocker_blocks
belongs_to :blocked, foreign_key: "blocked_id", class_name: "User", inverse_of: :blocked_blocks
validates :blocked_id, :blocker_id, :config, presence: true
validates :blocked_id, uniqueness: { scope: %i[blocker_id] }

View file

@ -12,6 +12,38 @@ module Users
user.mentions.delete_all
user.badge_achievements.delete_all
user.github_repos.delete_all
user.access_grants.delete_all
user.access_tokens.delete_all
user.created_podcasts.update_all(creator_id: nil)
user.blocker_blocks.delete_all
user.blocked_blocks.delete_all
user.webhook_endpoints.delete_all
user.authored_notes.delete_all
user.backup_data.delete_all
user.collections.delete_all
user.credits.delete_all
user.display_ad_events.delete_all
user.email_messages.delete_all
user.html_variants.delete_all
user.organization_memberships.delete_all
user.page_views.delete_all
user.poll_skips.delete_all
user.poll_votes.delete_all
user.pro_membership.delete if user.pro_membership.present?
user.profile_pins.delete_all
user.rating_votes.delete_all
user.tweets.delete_all
handle_feedback_messages(user)
end
# delete_all will nullify the corresponding foreign_key field bacause of the dependent: :nullify strategy
def handle_feedback_messages(user)
user.offender_feedback_messages.delete_all
user.reporter_feedback_messages.delete_all
user.affected_feedback_messages.delete_all
end
end
end

View file

@ -0,0 +1,5 @@
FactoryBot.define do
factory :ahoy_message, class: "Ahoy::Message" do
user
end
end

View file

@ -0,0 +1,9 @@
FactoryBot.define do
factory :backup_data do
association :instance_user, factory: :user
after(:build) do |backup_data|
backup_data.instance = backup_data.instance_user.identities.first || create(:identity, user: backup_data.instance_user)
backup_data.json_data = backup_data.instance.attributes
end
end
end

View file

@ -1,4 +1,6 @@
FactoryBot.define do
factory :chat_channel_membership do
user
association :chat_channel, channel_type: "open"
end
end

View file

@ -1,11 +1,7 @@
FactoryBot.define do
factory :comment do
factory :comment, aliases: [:article_comment] do
user
body_markdown { Faker::Hipster.paragraph(sentence_count: 1) }
commentable_id { rand(1000) }
commentable_type { "Article" }
factory :article_comment do
association :commentable, factory: :article
end
body_markdown { Faker::Hipster.paragraph(sentence_count: 1) }
association :commentable, factory: :article
end
end

View file

@ -2,5 +2,6 @@ FactoryBot.define do
factory :display_ad_event do
category { "impression" }
context_type { "home" }
display_ad
end
end

View file

@ -2,5 +2,6 @@ FactoryBot.define do
factory :display_ad do
placement_area { "sidebar_left" }
body_markdown { "Hello _hey_ Hey hey" }
organization
end
end

View file

@ -0,0 +1,7 @@
FactoryBot.define do
factory :doorkeeper_access_grant, class: "Doorkeeper::AccessGrant" do
application
expires_in { 600 }
redirect_uri { "urn:ietf:wg:oauth:2.0:oob" }
end
end

View file

@ -1,8 +1,9 @@
FactoryBot.define do
factory :feedback_message do
transient do
reporter_id { 1 }
end
feedback_type { "abuse-reports" }
message { Faker::Hipster.paragraph(sentence_count: 1) }
category { "rude or vulgar" }
reported_url { "/" }
after(:create) do |feedback_message, evaluator|
feedback_message.update(reporter_id: evaluator.reporter_id)

7
spec/factories/note.rb Normal file
View file

@ -0,0 +1,7 @@
FactoryBot.define do
factory :note do
association :noteable, factory: :user
content { Faker::Book.title }
reason { "misc_note" }
end
end

View file

@ -1,4 +1,6 @@
FactoryBot.define do
factory :notification_subscription do
user
association :notifiable, factory: :article
end
end

View file

@ -1,8 +1,7 @@
FactoryBot.define do
factory :organization_membership do
# TODO: replace with `user` and `organization` since this currently doesn't have a belongs_to relationship
user_id { rand(6) }
organization_id { rand(6) }
user
organization
type_of_user { "member" }
end
end

View file

@ -1,5 +1,6 @@
FactoryBot.define do
factory :poll_option do
poll
markdown { Faker::Hipster.words(number: 3) }
end
end

View file

@ -1,4 +1,6 @@
FactoryBot.define do
factory :poll_skip do
poll
user
end
end

View file

@ -1,4 +1,7 @@
FactoryBot.define do
factory :poll_vote do
user
poll
poll_option
end
end

View file

@ -1,5 +1,6 @@
FactoryBot.define do
factory :poll do
article
prompt_markdown { Faker::Hipster.words(number: 5) }
poll_options_input_array { [rand(5).to_s, rand(5).to_s, rand(5).to_s, rand(5).to_s] }
end

View file

@ -1,6 +1,8 @@
FactoryBot.define do
factory :profile_pin do
pinnable_type { "Article" }
profile_type { "User" }
association :profile, factory: :user
after(:build) do |profile_pin|
profile_pin.pinnable ||= create(:article, user: profile_pin.profile)
end
end
end

View file

@ -1,6 +1,10 @@
FactoryBot.define do
factory :rating_vote do
user
group { "experience_level" }
rating { rand(1.0..8.0) }
after(:build) do |vote|
vote.article ||= create(:article, user: vote.user)
end
end
end

View file

@ -1,14 +1,13 @@
FactoryBot.define do
factory :reaction do
reactable_id { rand(10_000) }
user
reactable_type { "Article" }
association :reactable, factory: :article
category { "like" }
end
factory :reading_reaction, class: "Reaction" do
user
reactable { create(:article) }
association :reactable, factory: :article
category { "readinglist" }
end
end

6
spec/factories/tweets.rb Normal file
View file

@ -0,0 +1,6 @@
FactoryBot.define do
factory :tweet do
twitter_id_code { rand(10_000) }
full_fetched_object_serialized { Faker::Book.title }
end
end

View file

@ -1,7 +1,7 @@
FactoryBot.define do
factory :user_block do
blocker { user }
blocked { user }
association :blocker, factory: :user
association :blocked, factory: :user
config { "default" }
end
end

View file

@ -94,7 +94,7 @@ FactoryBot.define do
after(:create) do |user|
other_user = create(:user)
article = create(:article, user_id: other_user.id)
create(:comment, user_id: user.id, commentable_id: article.id)
create(:comment, user_id: user.id, commentable: article)
user.update(comments_count: 1)
end
end
@ -102,7 +102,7 @@ FactoryBot.define do
trait :with_article_and_comment do
after(:create) do |user|
article = create(:article, user_id: user.id)
create(:comment, user_id: user.id, commentable_id: article.id)
create(:comment, user_id: user.id, commentable: article)
user.update(articles_count: 1, comments_count: 1)
end
end

View file

@ -4,7 +4,7 @@ RSpec.describe CacheBuster, type: :labor do
let(:cache_buster) { described_class }
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
let(:organization) { create(:organization) }
let(:listing) { create(:classified_listing, user_id: user.id, category: "cfp") }
let(:podcast) { create(:podcast) }

View file

@ -17,13 +17,13 @@ RSpec.describe RateLimitChecker, type: :labor do
end
it "returns true if too many comments at once" do
create_list(:comment, 2, user_id: user.id, commentable_id: article.id)
create_list(:comment, 2, user_id: user.id, commentable: article)
expect(rate_limit_checker.limit_by_action("comment_creation")).to eq(true)
end
it "triggers ping admin when too many comments" do
allow(RateLimitCheckerWorker).to receive(:perform_async)
create_list(:comment, 2, user_id: user.id, commentable_id: article.id)
create_list(:comment, 2, user_id: user.id, commentable: article)
rate_limit_checker.limit_by_action("comment_creation")
expect(RateLimitCheckerWorker).to have_received(:perform_async).with(user.id, "comment_creation")
end

View file

@ -4,7 +4,7 @@ RSpec.describe NotifyMailer, type: :mailer do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
describe "#new_reply_email" do
let(:email) { described_class.new_reply_email(comment) }

View file

@ -7,21 +7,22 @@ RSpec.describe ProfilePin, type: :model do
describe "number of pins" do
let_it_be(:articles) { create_list(:article, 4, user: user) }
let_it_be(:pins) do
articles.each { |article| create(:profile_pin, pinnable_id: article.id, profile_id: user.id) }
articles.each { |article| create(:profile_pin, pinnable: article, profile: user) }
end
let(:fifth_article) { create(:article, user: user) }
let(:sixth_article) { create(:article, user: user) }
it "allows up to five pins per user" do
pin = build(:profile_pin, pinnable_id: fifth_article.id, profile_id: user.id)
pin = build(:profile_pin, pinnable: fifth_article, profile: user)
expect(pin).to be_valid
end
it "disallows the sixth pin" do
create(:profile_pin, pinnable_id: fifth_article.id, profile_id: user.id)
create(:profile_pin, pinnable: fifth_article, profile: user)
user.reload
expect do
create(:profile_pin, pinnable_id: sixth_article.id, profile_id: user.id)
create(:profile_pin, pinnable: sixth_article, profile: user)
end.to raise_error(ActiveRecord::RecordInvalid)
end
end
@ -30,13 +31,13 @@ RSpec.describe ProfilePin, type: :model do
let_it_be(:article) { create(:article, user: user) }
it "ensures pinnable belongs to the same profile" do
pin = build(:profile_pin, pinnable_id: article.id, profile_id: create(:user).id)
pin = build(:profile_pin, pinnable: article, profile: create(:user))
expect(pin).not_to be_valid
end
it "ensures one pin per pinnable per profile" do
create(:profile_pin, pinnable_id: article.id, profile_id: user.id)
other_pin = build(:profile_pin, pinnable_id: article.id, profile_id: user.id)
create(:profile_pin, pinnable: article, profile: user)
other_pin = build(:profile_pin, pinnable: article, profile: user)
expect(other_pin).not_to be_valid
end
end

View file

@ -5,7 +5,7 @@ RSpec.describe "BufferUpdates", type: :request do
let(:mod_user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:mod_article) { create(:article, user_id: mod_user.id) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
context "when trusted user is logged in" do
before do

View file

@ -10,7 +10,7 @@ RSpec.describe "CommentsDestroy", type: :request do
describe "GET /:username/comment/:id_code/delete_confirm" do
it "renders the confirmation message" do
comment = create(:comment, user_id: user.id, commentable_id: article.id)
comment = create(:comment, user_id: user.id, commentable: article)
get comment.path + "/delete_confirm"
expect(response.body).to include("Are you sure you want to delete this comment")
end
@ -19,19 +19,19 @@ RSpec.describe "CommentsDestroy", type: :request do
describe "DELETE /comments/:id" do
context "when comment has no children" do
it "destroys the comment" do
comment = create(:comment, user_id: user.id, commentable_id: article.id)
comment = create(:comment, user_id: user.id, commentable: article)
delete "/comments/#{comment.id}"
expect(Comment.all.size).to eq(0)
end
end
context "when comment has children" do
let(:parent_comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:parent_comment) { create(:comment, user_id: user.id, commentable: article) }
let(:child_comment) do
create(
:comment,
user_id: user.id,
commentable_id: article.id,
commentable: article,
parent_id: parent_comment.id,
)
end

View file

@ -3,7 +3,7 @@ require "rails_helper"
RSpec.describe "CommentsUpdate", type: :request do
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
before do
sign_in user

View file

@ -4,7 +4,7 @@ require "requests/shared_examples/internal_policy_dependant_request"
RSpec.describe "/internal/buffer_updates", type: :request do
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
it_behaves_like "an InternalPolicy dependant request", BufferUpdate do
let(:request) { post "/internal/buffer_updates" }

View file

@ -11,7 +11,7 @@ RSpec.describe "/internal/reactions", type: :request do
sign_in admin
end
let(:reaction) { create(:reaction, category: "vomit", user_id: user.id, reactable_id: article.id) }
let(:reaction) { create(:reaction, category: "vomit", user_id: user.id, reactable: article) }
it "updates reaction to be confirmed" do
put "/internal/reactions/#{reaction.id}", params: {
@ -34,7 +34,7 @@ RSpec.describe "/internal/reactions", type: :request do
sign_in user
end
let(:reaction) { create(:reaction, category: "vomit", user_id: user.id, reactable_id: article.id) }
let(:reaction) { create(:reaction, category: "vomit", user_id: user.id, reactable: article) }
it "updates reaction to be confirmed" do
invalid_request = lambda do

View file

@ -170,7 +170,7 @@ RSpec.describe "Internal::Users", type: :request do
:comment,
body_markdown: "Hello @#{user.username}, you are cool.",
user_id: user2.id,
commentable_id: article2.id,
commentable: article2,
)
perform_enqueued_jobs do
Mention.create_all(comment)

View file

@ -4,7 +4,7 @@ RSpec.describe "OrganizationsUpdate", type: :request do
let(:user) { create(:user, :org_admin) }
let(:org_id) { user.organizations.first.id }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
before do
sign_in user

View file

@ -33,7 +33,7 @@ RSpec.describe "ProfilePins", type: :request do
describe "PUT /profile_pins/:id" do # delete
it "adds pin on behalf of current user" do
profile_pin = create(:profile_pin, pinnable_id: article.id, profile_id: user.id)
profile_pin = create(:profile_pin, pinnable: article, profile: user)
put "/profile_pins/#{profile_pin.id}"
expect(ProfilePin.all.size).to eq(0)
end

View file

@ -3,7 +3,7 @@ require "rails_helper"
RSpec.describe "Reactions", type: :request do
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, commentable_id: article.id) }
let(:comment) { create(:comment, commentable: article) }
let(:max_age) { FastlyRails.configuration.max_age }
let(:stale_if_error) { FastlyRails.configuration.stale_if_error }

View file

@ -3,7 +3,7 @@ require "rails_helper"
RSpec.describe "ReadingListItems", type: :request do
let(:user) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:reaction) { create(:reaction, reactable_id: article.id, user_id: user.id) }
let(:reaction) { create(:reaction, reactable: article, user_id: user.id) }
before do
sign_in user

View file

@ -5,7 +5,7 @@ RSpec.describe "SocialPreviews", type: :request do
let(:tag) { create(:tag, badge: create(:badge)) }
let(:organization) { create(:organization) }
let(:article) { create(:article, user_id: user.id, tags: tag.name) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
let(:image_url) { "https://hcti.io/v1/image/6c52de9d-4d37-4008-80f8-67155589e1a1" }
let(:listing) { create(:classified_listing, user_id: user.id, category: "cfp") }

View file

@ -14,7 +14,7 @@ RSpec.describe "UserProfiles", type: :request do
create(:article, user_id: user.id)
create(:article, user_id: user.id)
last_article = create(:article, user_id: user.id)
create(:profile_pin, pinnable_id: last_article.id, profile_id: user.id)
create(:profile_pin, pinnable: last_article, profile: user)
get "/#{user.username}"
expect(response.body).to include "Pinned"
end

View file

@ -4,13 +4,13 @@ RSpec.describe Mentions::CreateAll, type: :service do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, user_id: user2.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user2.id, commentable: article) }
let(:comment2) do
create(
:comment,
body_markdown: "Hello @#{user.username}, you are cool.",
user_id: user2.id,
commentable_id: article.id,
commentable: article,
)
end

View file

@ -4,8 +4,8 @@ RSpec.describe Notifications::RemoveAll, type: :service do
let(:user) { create(:user) }
let(:user2) { create(:user) }
let(:article) { create(:article, user_id: user.id) }
let(:comment) { create(:comment, user_id: user.id, commentable_id: article.id) }
let(:comment2) { create(:comment, user_id: user2.id, commentable_id: article.id) }
let(:comment) { create(:comment, user_id: user.id, commentable: article) }
let(:comment2) { create(:comment, user_id: user2.id, commentable: article) }
let(:mention) { create(:mention, user_id: user.id, mentionable_id: comment.id, mentionable_type: "Comment") }
let(:mention2) { create(:mention, user_id: user2.id, mentionable_id: comment2.id, mentionable_type: "Comment") }
let(:notifiable_collection_ids) { [mention.id, mention2.id] }

View file

@ -5,7 +5,7 @@ RSpec.describe Suggester::Articles::Boosted, type: :service do
let(:organization) { create(:organization) }
let(:tag) { create(:tag, supported: true) }
let(:article) { create(:article, tags: [tag.name], featured: true) }
let(:reaction) { create(:reaction, user_id: user.id, reactable_id: article.id) }
let(:reaction) { create(:reaction, user_id: user.id, reactable: article) }
it "returns an article" do
user.follow(tag)

View file

@ -4,12 +4,12 @@ RSpec.describe Suggester::Articles::Classic, type: :service do
let(:user) { create(:user) }
let(:tag) { create(:tag, supported: true) }
let(:article) { create(:article, tags: [tag.name], featured: true) }
let(:reaction) { create(:reaction, user_id: user.id, reactable_id: article.id) }
let(:reaction) { create(:reaction, user_id: user.id, reactable: article) }
it "returns an article" do
create(:reaction, user_id: user.id, reactable_id: article.id)
create(:reaction, user_id: user.id, reactable_id: article.id, category: "thinking")
create(:reaction, user_id: user.id, reactable_id: article.id, category: "unicorn")
create(:reaction, user_id: user.id, reactable: article)
create(:reaction, user_id: user.id, reactable: article, category: "thinking")
create(:reaction, user_id: user.id, reactable: article, category: "unicorn")
expect(described_class.new(article).get.first.id).to eq article.id
end
@ -20,14 +20,14 @@ RSpec.describe Suggester::Articles::Classic, type: :service do
it "returns single article if multiple qualify" do
user.follow(tag)
create(:reaction, user_id: user.id, reactable_id: article.id)
create(:reaction, user_id: user.id, reactable_id: article.id, category: "thinking")
create(:reaction, user_id: user.id, reactable_id: article.id, category: "unicorn")
create(:reaction, user_id: user.id, reactable: article)
create(:reaction, user_id: user.id, reactable: article, category: "thinking")
create(:reaction, user_id: user.id, reactable: article, category: "unicorn")
user2 = create(:user)
article2 = create(:article, user_id: user2.id)
create(:reaction, user_id: user2.id, reactable_id: article2.id)
create(:reaction, user_id: user2.id, reactable_id: article2.id, category: "thinking")
create(:reaction, user_id: user2.id, reactable_id: article2.id, category: "unicorn")
create(:reaction, user_id: user2.id, reactable: article2)
create(:reaction, user_id: user2.id, reactable: article2, category: "thinking")
create(:reaction, user_id: user2.id, reactable: article2, category: "unicorn")
expect(described_class.new(article).get.first&.id).to eq article.id
end
end

View file

@ -27,7 +27,7 @@ RSpec.describe Users::DeleteArticles, type: :service do
it "deletes articles' comments" do
described_class.call(user)
expect(Comment.where(commentable_id: article, commentable_type: "Article").any?).to be false
expect(Comment.where(commentable_id: article.id, commentable_type: "Article").any?).to be false
end
it "busts cache" do

View file

@ -34,4 +34,56 @@ RSpec.describe Users::Delete, type: :service do
described_class.call(user)
expect(Rails.cache).to have_received(:delete).with("user-destroy-token-#{user.id}")
end
# check that all the associated records are being destroyed, except for those that are kept explicitly (kept_associations)
describe "deleting associations" do
let(:kept_association_names) { %i[created_podcasts notes offender_feedback_messages reporter_feedback_messages affected_feedback_messages] }
let(:direct_associations) { User.reflect_on_all_associations.reject { |a| a.options.key?(:join_table) || a.options.key?(:through) } }
let!(:user_associations) do
create_associations(direct_associations.reject { |a| kept_association_names.include?(a.name) })
end
let!(:kept_associations) do
create_associations(direct_associations.select { |a| kept_association_names.include?(a.name) })
end
def create_associations(names)
associations = []
names.each do |association|
if user.public_send(association.name).present?
associations.push(*user.public_send(association.name))
else
singular_name = ActiveSupport::Inflector.singularize(association.name)
class_name = association.options[:class_name] || singular_name
possible_factory_name = class_name.underscore.tr("/", "_")
inverse_of = association.options[:inverse_of] || association.options[:as] || :user
record = create(possible_factory_name, inverse_of => user)
associations.push record
end
end
associations
end
it "keeps the kept associations" do
expect(kept_associations).not_to be_empty
user.reload
described_class.call(user)
aggregate_failures "associations should exist" do
kept_associations.each do |kept_association|
expect { kept_association.reload }.not_to raise_error
end
end
end
it "deletes all the associations" do
# making sure that the association records were actually created
expect(user_associations).not_to be_empty
user.reload
described_class.call(user)
aggregate_failures "associations should not exist" do
user_associations.each do |user_association|
expect { user_association.reload }.to raise_error(ActiveRecord::RecordNotFound)
end
end
end
end
end

View file

@ -36,7 +36,7 @@ RSpec.describe "Creating Comment", type: :system, js: true do
end
it "User replies to a comment" do
create(:comment, commentable_id: article.id, user_id: user.id)
create(:comment, commentable: article, user_id: user.id)
visit article.path.to_s
wait_for_javascript