mirror of
https://github.com/discourse/discourse.git
synced 2024-12-27 08:34:03 +08:00
9655bf3e24
In #22851 we added a dependent strategy for deleting upload references when a draft is destroyed. This, however, didn't catch all cases, because we still have some code that issues DELETE drafts queries directly to the database. Specifically in the weekly cleanup job handled by Draft#cleanup!. This PR fixes that by turning the raw query into an ActiveRecord #destroy_all, which will invoke the dependent strategy that ultimately deletes the upload references. It also includes a post migration to clear orphaned upload references that are already in the database.
311 lines
9.6 KiB
Ruby
311 lines
9.6 KiB
Ruby
# frozen_string_literal: true
|
|
|
|
RSpec.describe Draft do
|
|
fab!(:user)
|
|
|
|
fab!(:post)
|
|
|
|
it { is_expected.to have_many(:upload_references).dependent(:delete_all) }
|
|
|
|
describe "system user" do
|
|
it "can not set drafts" do
|
|
# fake a sequence
|
|
DraftSequence.create!(user_id: Discourse.system_user.id, draft_key: "abc", sequence: 10)
|
|
|
|
seq = Draft.set(Discourse.system_user, "abc", 0, { reply: "hi" }.to_json)
|
|
expect(seq).to eq(0)
|
|
|
|
draft = Draft.get(Discourse.system_user, "abc", 0)
|
|
expect(draft).to eq(nil)
|
|
|
|
draft = Draft.get(Discourse.system_user, "abc", 1)
|
|
expect(draft).to eq(nil)
|
|
end
|
|
end
|
|
|
|
describe "backup_drafts_to_pm_length" do
|
|
it "correctly backs up drafts to a personal message" do
|
|
SiteSetting.backup_drafts_to_pm_length = 1
|
|
|
|
draft = { reply: "this is a reply", random_key: "random" }
|
|
|
|
seq = Draft.set(user, "xyz", 0, draft.to_json)
|
|
draft["reply"] = "test" * 100
|
|
|
|
half_grace = (SiteSetting.editing_grace_period / 2 + 1).seconds
|
|
|
|
freeze_time half_grace.from_now
|
|
seq = Draft.set(user, "xyz", seq, draft.to_json)
|
|
|
|
draft_post = BackupDraftPost.find_by(user_id: user.id, key: "xyz").post
|
|
|
|
expect(draft_post.revisions.count).to eq(0)
|
|
|
|
freeze_time half_grace.from_now
|
|
|
|
# this should trigger a post revision as 10 minutes have passed
|
|
draft["reply"] = "hello"
|
|
Draft.set(user, "xyz", seq, draft.to_json)
|
|
|
|
draft_topic = BackupDraftTopic.find_by(user_id: user.id)
|
|
expect(draft_topic.topic.posts_count).to eq(2)
|
|
|
|
draft_post.reload
|
|
expect(draft_post.revisions.count).to eq(1)
|
|
end
|
|
end
|
|
|
|
it "can get a draft by user" do
|
|
Draft.set(user, "test", 0, "data")
|
|
expect(Draft.get(user, "test", 0)).to eq "data"
|
|
end
|
|
|
|
it "uses the user id and key correctly" do
|
|
Draft.set(user, "test", 0, "data")
|
|
expect(Draft.get(Fabricate.build(:coding_horror), "test", 0)).to eq nil
|
|
end
|
|
|
|
it "should overwrite draft data correctly" do
|
|
seq = Draft.set(user, "test", 0, "data")
|
|
seq = Draft.set(user, "test", seq, "new data")
|
|
expect(Draft.get(user, "test", seq)).to eq "new data"
|
|
end
|
|
|
|
it "should increase the sequence on every save" do
|
|
seq = Draft.set(user, "test", 0, "data")
|
|
expect(seq).to eq(0)
|
|
seq = Draft.set(user, "test", 0, "data")
|
|
expect(seq).to eq(1)
|
|
end
|
|
|
|
it "should clear drafts on request" do
|
|
Draft.set(user, "test", 0, "data")
|
|
Draft.clear(user, "test", 0)
|
|
expect(Draft.get(user, "test", 0)).to eq nil
|
|
end
|
|
|
|
it "should cross check with DraftSequence table" do
|
|
Draft.set(user, "test", 0, "old")
|
|
expect(Draft.get(user, "test", 0)).to eq "old"
|
|
|
|
DraftSequence.next!(user, "test")
|
|
seq = DraftSequence.next!(user, "test")
|
|
expect(seq).to eq(2)
|
|
|
|
expect do Draft.set(user, "test", seq - 1, "error") end.to raise_error(Draft::OutOfSequence)
|
|
|
|
expect do Draft.set(user, "test", seq + 1, "error") end.to raise_error(Draft::OutOfSequence)
|
|
|
|
Draft.set(user, "test", seq, "data")
|
|
expect(Draft.get(user, "test", seq)).to eq "data"
|
|
|
|
expect do expect(Draft.get(user, "test", seq - 1)).to eq "data" end.to raise_error(
|
|
Draft::OutOfSequence,
|
|
)
|
|
|
|
expect do expect(Draft.get(user, "test", seq + 1)).to eq "data" end.to raise_error(
|
|
Draft::OutOfSequence,
|
|
)
|
|
end
|
|
|
|
it "should disregard old draft if sequence decreases" do
|
|
Draft.set(user, "test", 0, "data")
|
|
DraftSequence.next!(user, "test")
|
|
Draft.set(user, "test", 1, "hello")
|
|
|
|
expect do Draft.set(user, "test", 0, "foo") end.to raise_error(Draft::OutOfSequence)
|
|
|
|
expect do Draft.get(user, "test", 0) end.to raise_error(Draft::OutOfSequence)
|
|
|
|
expect(Draft.get(user, "test", 1)).to eq "hello"
|
|
end
|
|
|
|
it "should disregard draft sequence if force_save is true" do
|
|
Draft.set(user, "test", 0, "data")
|
|
DraftSequence.next!(user, "test")
|
|
Draft.set(user, "test", 1, "hello")
|
|
|
|
seq = Draft.set(user, "test", 0, "foo", nil, force_save: true)
|
|
expect(seq).to eq(2)
|
|
end
|
|
|
|
it "can cleanup old drafts" do
|
|
key = Draft::NEW_TOPIC
|
|
|
|
Draft.set(user, key, 0, "draft")
|
|
|
|
Draft.cleanup!
|
|
expect(Draft.count).to eq 1
|
|
expect(user.user_stat.draft_count).to eq(1)
|
|
|
|
seq = DraftSequence.next!(user, key)
|
|
|
|
Draft.set(user, key, seq, "draft")
|
|
DraftSequence.update_all("sequence = sequence + 1")
|
|
|
|
draft = Draft.first
|
|
draft.upload_references << UploadReference.create!(target: draft, upload: Fabricate(:upload))
|
|
|
|
expect(UploadReference.count).to eq(1)
|
|
|
|
Draft.cleanup!
|
|
|
|
expect(Draft.count).to eq 0
|
|
expect(user.reload.user_stat.draft_count).to eq(0)
|
|
expect(UploadReference.count).to eq(0)
|
|
|
|
Draft.set(Fabricate(:user), Draft::NEW_TOPIC, 0, "draft")
|
|
|
|
Draft.cleanup!
|
|
|
|
expect(Draft.count).to eq 1
|
|
|
|
# should cleanup drafts more than 180 days old
|
|
SiteSetting.delete_drafts_older_than_n_days = 180
|
|
|
|
Draft.last.update_columns(updated_at: 200.days.ago)
|
|
Draft.cleanup!
|
|
expect(Draft.count).to eq 0
|
|
end
|
|
|
|
it "updates draft count when a draft is created or destroyed" do
|
|
Draft.set(Fabricate(:user), Draft::NEW_TOPIC, 0, "data")
|
|
|
|
messages =
|
|
MessageBus.track_publish("/user-drafts/#{user.id}") do
|
|
Draft.set(user, Draft::NEW_TOPIC, 0, "data")
|
|
end
|
|
|
|
expect(messages.first.data[:draft_count]).to eq(1)
|
|
expect(messages.first.data[:has_topic_draft]).to eq(true)
|
|
expect(messages.first.user_ids).to contain_exactly(user.id)
|
|
|
|
messages =
|
|
MessageBus.track_publish("/user-drafts/#{user.id}") { Draft.where(user: user).destroy_all }
|
|
|
|
expect(messages.first.data[:draft_count]).to eq(0)
|
|
expect(messages.first.data[:has_topic_draft]).to eq(false)
|
|
expect(messages.first.user_ids).to contain_exactly(user.id)
|
|
end
|
|
|
|
describe "#stream" do
|
|
fab!(:public_post) { Fabricate(:post) }
|
|
let(:public_topic) { public_post.topic }
|
|
|
|
let(:stream) { Draft.stream(user: user) }
|
|
|
|
it "should include the correct number of drafts in the stream" do
|
|
Draft.set(user, "test", 0, '{"reply":"hey.","action":"createTopic","title":"Hey"}')
|
|
Draft.set(user, "test2", 0, '{"reply":"howdy"}')
|
|
expect(stream.count).to eq(2)
|
|
end
|
|
|
|
it "should include the right topic id in a draft reply in the stream" do
|
|
Draft.set(user, "topic_#{public_topic.id}", 0, '{"reply":"hi"}')
|
|
draft_row = stream.first
|
|
expect(draft_row.topic_id).to eq(public_topic.id)
|
|
end
|
|
|
|
it "should include the right draft username in the stream" do
|
|
Draft.set(user, "topic_#{public_topic.id}", 0, '{"reply":"hey"}')
|
|
draft_row = stream.first
|
|
expect(draft_row.user.username).to eq(user.username)
|
|
end
|
|
end
|
|
|
|
describe "key expiry" do
|
|
it "nukes new topic draft after a topic is created" do
|
|
Draft.set(user, Draft::NEW_TOPIC, 0, "my draft")
|
|
_t = Fabricate(:topic, user: user, advance_draft: true)
|
|
s = DraftSequence.current(user, Draft::NEW_TOPIC)
|
|
expect(Draft.get(user, Draft::NEW_TOPIC, s)).to eq nil
|
|
expect(Draft.count).to eq 0
|
|
end
|
|
|
|
it "nukes new pm draft after a pm is created" do
|
|
Draft.set(user, Draft::NEW_PRIVATE_MESSAGE, 0, "my draft")
|
|
t =
|
|
Fabricate(
|
|
:topic,
|
|
user: user,
|
|
archetype: Archetype.private_message,
|
|
category_id: nil,
|
|
advance_draft: true,
|
|
)
|
|
s = DraftSequence.current(t.user, Draft::NEW_PRIVATE_MESSAGE)
|
|
expect(Draft.get(user, Draft::NEW_PRIVATE_MESSAGE, s)).to eq nil
|
|
end
|
|
|
|
it "does not nuke new topic draft after a pm is created" do
|
|
Draft.set(user, Draft::NEW_TOPIC, 0, "my draft")
|
|
t = Fabricate(:topic, user: user, archetype: Archetype.private_message, category_id: nil)
|
|
s = DraftSequence.current(t.user, Draft::NEW_TOPIC)
|
|
expect(Draft.get(user, Draft::NEW_TOPIC, s)).to eq "my draft"
|
|
end
|
|
|
|
it "nukes the post draft when a post is created" do
|
|
topic = Fabricate(:topic)
|
|
|
|
Draft.set(user, topic.draft_key, 0, "hello")
|
|
|
|
p =
|
|
PostCreator.new(
|
|
user,
|
|
raw: Fabricate.build(:post).raw,
|
|
topic_id: topic.id,
|
|
advance_draft: true,
|
|
).create
|
|
|
|
expect(
|
|
Draft.get(p.user, p.topic.draft_key, DraftSequence.current(p.user, p.topic.draft_key)),
|
|
).to eq nil
|
|
end
|
|
|
|
it "nukes the post draft when a post is revised" do
|
|
Draft.set(post.user, post.topic.draft_key, 0, "hello")
|
|
post.revise(post.user, raw: "another test")
|
|
s = DraftSequence.current(post.user, post.topic.draft_key)
|
|
expect(Draft.get(post.user, post.topic.draft_key, s)).to eq nil
|
|
end
|
|
|
|
it "increases revision each time you set" do
|
|
Draft.set(user, "new_topic", 0, "hello")
|
|
Draft.set(user, "new_topic", 0, "goodbye")
|
|
|
|
expect(Draft.find_by(user_id: user.id, draft_key: "new_topic").revisions).to eq(2)
|
|
end
|
|
|
|
it "handles owner switching gracefully" do
|
|
draft_seq = Draft.set(user, "new_topic", 0, "hello", _owner = "ABCDEF")
|
|
expect(draft_seq).to eq(0)
|
|
|
|
draft_seq = Draft.set(user, "new_topic", 0, "hello world", _owner = "HIJKL")
|
|
expect(draft_seq).to eq(1)
|
|
|
|
draft_seq = Draft.set(user, "new_topic", 1, "hello world", _owner = "HIJKL")
|
|
expect(draft_seq).to eq(2)
|
|
end
|
|
|
|
it "can correctly preload drafts" do
|
|
Draft.set(
|
|
user,
|
|
"#{Draft::EXISTING_TOPIC}#{post.topic_id}",
|
|
0,
|
|
{ raw: "hello", postId: post.id }.to_json,
|
|
)
|
|
|
|
drafts = Draft.where(user_id: user.id).to_a
|
|
|
|
Draft.preload_data(drafts, user)
|
|
|
|
expect(drafts[0].topic_preloaded?).to eq(true)
|
|
expect(drafts[0].topic.id).to eq(post.topic_id)
|
|
|
|
expect(drafts[0].post_preloaded?).to eq(true)
|
|
expect(drafts[0].post.id).to eq(post.id)
|
|
end
|
|
end
|
|
|
|
it { is_expected.to validate_length_of(:draft_key).is_at_most(25) }
|
|
end
|