require 'rails_helper'
require_dependency 'post_destroyer'
describe Post do
before { Oneboxer.stubs :onebox }
describe '#hidden_reasons' do
context "verify enum sequence" do
before do
@hidden_reasons = Post.hidden_reasons
end
it "'flag_threshold_reached' should be at 1st position" do
expect(@hidden_reasons[:flag_threshold_reached]).to eq(1)
end
it "'flagged_by_tl3_user' should be at 4th position" do
expect(@hidden_reasons[:flagged_by_tl3_user]).to eq(4)
end
end
end
describe '#types' do
context "verify enum sequence" do
before do
@types = Post.types
end
it "'regular' should be at 1st position" do
expect(@types[:regular]).to eq(1)
end
it "'whisper' should be at 4th position" do
expect(@types[:whisper]).to eq(4)
end
end
end
describe '#cook_methods' do
context "verify enum sequence" do
before do
@cook_methods = Post.cook_methods
end
it "'regular' should be at 1st position" do
expect(@cook_methods[:regular]).to eq(1)
end
it "'email' should be at 3rd position" do
expect(@cook_methods[:email]).to eq(3)
end
end
end
# Help us build a post with a raw body
def post_with_body(body, user = nil)
args = post_args.merge(raw: body)
args[:user] = user if user.present?
Fabricate.build(:post, args)
end
it { is_expected.to validate_presence_of :raw }
# Min/max body lengths, respecting padding
it { is_expected.not_to allow_value("x").for(:raw) }
it { is_expected.not_to allow_value("x" * (SiteSetting.max_post_length + 1)).for(:raw) }
it { is_expected.not_to allow_value((" " * SiteSetting.min_post_length) + "x").for(:raw) }
it { is_expected.to rate_limit }
let(:topic) { Fabricate(:topic) }
let(:post_args) do
{ user: topic.user, topic: topic }
end
describe 'scopes' do
describe '#by_newest' do
it 'returns posts ordered by created_at desc' do
2.times do |t|
Fabricate(:post, created_at: t.seconds.from_now)
end
expect(Post.by_newest.first.created_at).to be > Post.by_newest.last.created_at
end
end
describe '#with_user' do
it 'gives you a user' do
Fabricate(:post, user: Fabricate.build(:user))
expect(Post.with_user.first.user).to be_a User
end
end
end
describe "revisions and deleting/recovery" do
context 'a post without links' do
let(:post) { Fabricate(:post, post_args) }
before do
post.trash!
post.reload
end
it "doesn't create a new revision when deleted" do
expect(post.revisions.count).to eq(0)
end
describe "recovery" do
before do
post.recover!
post.reload
end
it "doesn't create a new revision when recovered" do
expect(post.revisions.count).to eq(0)
end
end
end
context 'a post with links' do
let(:post) { Fabricate(:post_with_external_links) }
before do
post.trash!
post.reload
end
describe 'recovery' do
it 'recreates the topic_link records' do
TopicLink.expects(:extract_from).with(post)
post.recover!
end
end
end
context 'a post with notices' do
let(:post) {
post = Fabricate(:post, post_args)
post.custom_fields["post_notice_type"] = "returning"
post.custom_fields["post_notice_time"] = 1.day.ago
post.save_custom_fields
post
}
describe 'recovery' do
it 'deletes notices' do
expect { post.trash! }
.to change { post.custom_fields.length }.from(2).to(0)
end
end
end
end
describe 'flagging helpers' do
let(:post) { Fabricate(:post) }
let(:user) { Fabricate(:coding_horror) }
let(:admin) { Fabricate(:admin) }
it 'is_flagged? is accurate' do
PostAction.act(user, post, PostActionType.types[:off_topic])
post.reload
expect(post.is_flagged?).to eq(true)
PostAction.remove_act(user, post, PostActionType.types[:off_topic])
post.reload
expect(post.is_flagged?).to eq(false)
end
it 'is_flagged? is true if flag was deferred' do
PostAction.act(user, post, PostActionType.types[:off_topic])
PostAction.defer_flags!(post.reload, admin)
post.reload
expect(post.is_flagged?).to eq(true)
end
it 'is_flagged? is true if flag was cleared' do
PostAction.act(user, post, PostActionType.types[:off_topic])
PostAction.clear_flags!(post.reload, admin)
post.reload
expect(post.is_flagged?).to eq(true)
end
it 'has_active_flag? is false for deferred flags' do
PostAction.act(user, post, PostActionType.types[:spam])
post.reload
expect(post.has_active_flag?).to eq(true)
PostAction.defer_flags!(post, admin)
post.reload
expect(post.has_active_flag?).to eq(false)
end
it 'has_active_flag? is false for cleared flags' do
PostAction.act(user, post, PostActionType.types[:spam])
PostAction.clear_flags!(post.reload, admin)
post.reload
expect(post.has_active_flag?).to eq(false)
end
end
describe "maximum images" do
let(:newuser) { Fabricate(:user, trust_level: TrustLevel[0]) }
let(:post_no_images) { Fabricate.build(:post, post_args.merge(user: newuser)) }
let(:post_one_image) { post_with_body("![sherlock](http://bbc.co.uk/sherlock.jpg)", newuser) }
let(:post_two_images) { post_with_body("
", newuser) }
let(:post_with_avatars) { post_with_body('
', newuser) }
let(:post_with_favicon) { post_with_body('
', newuser) }
let(:post_image_within_quote) { post_with_body('[quote]
[/quote]', newuser) }
let(:post_image_within_code) { post_with_body('
', newuser) }
let(:post_image_within_pre) { post_with_body('
', newuser) } let(:post_with_thumbnail) { post_with_body('
http://www.google.com
", newuser) }
let(:post_two_links) { post_with_body("discourse twitter", newuser) }
let(:post_with_mentions) { post_with_body("hello @#{newuser.username} how are you doing?", newuser) }
it "returns 0 links for an empty post" do
expect(Fabricate.build(:post).link_count).to eq(0)
end
it "returns 0 links for a post with mentions" do
expect(post_with_mentions.link_count).to eq(0)
end
it "finds links from markdown" do
expect(post_one_link.link_count).to eq(1)
end
it "finds links from HTML" do
expect(post_two_links.link_count).to eq(2)
end
context "validation" do
before do
SiteSetting.newuser_max_links = 1
end
context 'newuser' do
it "returns true when within the amount of links allowed" do
expect(post_one_link).to be_valid
end
it "doesn't allow more links than allowed" do
expect(post_two_links).not_to be_valid
end
end
it "allows multiple links for basic accounts" do
post_two_links.user.trust_level = TrustLevel[1]
expect(post_two_links).to be_valid
end
context "min_trust_to_post_links" do
it "considers oneboxes links" do
SiteSetting.min_trust_to_post_links = 3
post_onebox.user.trust_level = TrustLevel[2]
expect(post_onebox).not_to be_valid
end
it "considers links within code" do
SiteSetting.min_trust_to_post_links = 3
post_onebox.user.trust_level = TrustLevel[2]
expect(post_code_link).not_to be_valid
end
it "doesn't allow allow links if `min_trust_to_post_links` is not met" do
SiteSetting.min_trust_to_post_links = 2
post_two_links.user.trust_level = TrustLevel[1]
expect(post_one_link).not_to be_valid
end
it "will skip the check for whitelisted domains" do
SiteSetting.whitelisted_link_domains = 'www.bbc.co.uk'
SiteSetting.min_trust_to_post_links = 2
post_two_links.user.trust_level = TrustLevel[1]
expect(post_one_link).to be_valid
end
end
end
end
describe "@mentions" do
context 'raw_mentions' do
it "returns an empty array with no matches" do
post = Fabricate.build(:post, post_args.merge(raw: "Hello Jake and Finn!"))
expect(post.raw_mentions).to eq([])
end
it "returns lowercase unique versions of the mentions" do
post = Fabricate.build(:post, post_args.merge(raw: "@Jake @Finn @Jake"))
expect(post.raw_mentions).to eq(['jake', 'finn'])
end
it "ignores pre" do
# we need to force an inline
post = Fabricate.build(:post, post_args.merge(raw: "p @Jake@Finn")) expect(post.raw_mentions).to eq(['finn']) end it "catches content between pre tags" do # per common mark we need to force an inline post = Fabricate.build(:post, post_args.merge(raw: "a
hello@Finn ")) expect(post.raw_mentions).to eq(['finn']) end it "ignores code" do post = Fabricate.build(:post, post_args.merge(raw: "@Jake `@Finn`")) expect(post.raw_mentions).to eq(['jake']) end it "ignores quotes" do post = Fabricate.build(:post, post_args.merge(raw: "[quote=\"Evil Trout\"]\n@Jake\n[/quote]\n@Finn")) expect(post.raw_mentions).to eq(['finn']) end it "handles underscore in username" do post = Fabricate.build(:post, post_args.merge(raw: "@Jake @Finn @Jake_Old")) expect(post.raw_mentions).to eq(['jake', 'finn', 'jake_old']) end it "handles hyphen in groupname" do post = Fabricate.build(:post, post_args.merge(raw: "@org-board")) expect(post.raw_mentions).to eq(['org-board']) end end context "max mentions" do let(:newuser) { Fabricate(:user, trust_level: TrustLevel[0]) } let(:post_with_one_mention) { post_with_body("@Jake is the person I'm mentioning", newuser) } let(:post_with_two_mentions) { post_with_body("@Jake @Finn are the people I'm mentioning", newuser) } context 'new user' do before do SiteSetting.newuser_max_mentions_per_post = 1 SiteSetting.max_mentions_per_post = 5 end it "allows a new user to have newuser_max_mentions_per_post mentions" do expect(post_with_one_mention).to be_valid end it "doesn't allow a new user to have more than newuser_max_mentions_per_post mentions" do expect(post_with_two_mentions).not_to be_valid end end context "not a new user" do before do SiteSetting.newuser_max_mentions_per_post = 0 SiteSetting.max_mentions_per_post = 1 end it "allows vmax_mentions_per_post mentions" do post_with_one_mention.user.trust_level = TrustLevel[1] expect(post_with_one_mention).to be_valid end it "doesn't allow to have more than max_mentions_per_post mentions" do post_with_two_mentions.user.trust_level = TrustLevel[1] expect(post_with_two_mentions).not_to be_valid end end end end context 'validation' do it 'validates our default post' do expect(Fabricate.build(:post, post_args)).to be_valid end it 'create blank posts as invalid' do expect(Fabricate.build(:post, raw: "")).not_to be_valid end end context "raw_hash" do let(:raw) { "this is our test post body" } let(:post) { post_with_body(raw) } it "returns a value" do expect(post.raw_hash).to be_present end it "returns blank for a nil body" do post.raw = nil expect(post.raw_hash).to be_blank end it "returns the same value for the same raw" do expect(post.raw_hash).to eq(post_with_body(raw).raw_hash) end it "returns a different value for a different raw" do expect(post.raw_hash).not_to eq(post_with_body("something else").raw_hash) end it "returns a different value with different text case" do expect(post.raw_hash).not_to eq(post_with_body("THIS is OUR TEST post BODy").raw_hash) end end context 'revise' do let(:post) { Fabricate(:post, post_args) } let(:first_version_at) { post.last_version_at } it 'has no revision' do expect(post.revisions.size).to eq(0) expect(first_version_at).to be_present expect(post.revise(post.user, raw: post.raw)).to eq(false) end describe 'with the same body' do it "doesn't change version" do expect { post.revise(post.user, raw: post.raw); post.reload }.not_to change(post, :version) end end describe 'ninja editing & edit windows' do before { SiteSetting.editing_grace_period = 1.minute.to_i } it 'works' do revised_at = post.updated_at + 2.minutes new_revised_at = revised_at + 2.minutes # ninja edit post.revise(post.user, { raw: 'updated body' }, revised_at: post.updated_at + 10.seconds) post.reload expect(post.version).to eq(1) expect(post.public_version).to eq(1) expect(post.revisions.size).to eq(0) expect(post.last_version_at.to_i).to eq(first_version_at.to_i) # revision much later post.revise(post.user, { raw: 'another updated body' }, revised_at: revised_at) post.reload expect(post.version).to eq(2) expect(post.public_version).to eq(2) expect(post.revisions.size).to eq(1) expect(post.last_version_at.to_i).to eq(revised_at.to_i) # new edit window post.revise(post.user, { raw: 'yet another updated body' }, revised_at: revised_at + 10.seconds) post.reload expect(post.version).to eq(2) expect(post.public_version).to eq(2) expect(post.revisions.size).to eq(1) expect(post.last_version_at.to_i).to eq(revised_at.to_i) # after second window post.revise(post.user, { raw: 'yet another, another updated body' }, revised_at: new_revised_at) post.reload expect(post.version).to eq(3) expect(post.public_version).to eq(3) expect(post.revisions.size).to eq(2) expect(post.last_version_at.to_i).to eq(new_revised_at.to_i) end end describe 'rate limiter' do let(:changed_by) { Fabricate(:coding_horror) } it "triggers a rate limiter" do EditRateLimiter.any_instance.expects(:performed!) post.revise(changed_by, raw: 'updated body') end end describe 'with a new body' do let(:changed_by) { Fabricate(:coding_horror) } let!(:result) { post.revise(changed_by, raw: 'updated body') } it 'acts correctly' do expect(result).to eq(true) expect(post.raw).to eq('updated body') expect(post.invalidate_oneboxes).to eq(true) expect(post.version).to eq(2) expect(post.public_version).to eq(2) expect(post.revisions.size).to eq(1) expect(post.revisions.first.user).to be_present end context 'second poster posts again quickly' do it 'is a ninja edit, because the second poster posted again quickly' do SiteSetting.editing_grace_period = 1.minute.to_i post.revise(changed_by, { raw: 'yet another updated body' }, revised_at: post.updated_at + 10.seconds) post.reload expect(post.version).to eq(2) expect(post.public_version).to eq(2) expect(post.revisions.size).to eq(1) end end end end describe 'before save' do let(:cooked) { "" } let(:post) do Fabricate(:post, raw: "
hello @#{group.name}
| ) end end describe 'when user can mention a group' do before do group.add(post.user) end it 'should create the mention' do post.update!(raw: "hello @#{group.name}") post.trigger_post_process post.reload expect(post.cooked).to eq( %Q|hello @#{group.name}
| ) end end end end describe "calculate_avg_time" do it "should not crash" do Post.calculate_avg_time Post.calculate_avg_time(1.day.ago) end end describe "has_host_spam" do let(:raw) { "hello from my site http://www.example.net http://#{GlobalSetting.hostname} http://#{RailsMultisite::ConnectionManagement.current_hostname}" } it "correctly detects host spam" do post = Fabricate(:post, raw: raw) expect(post.total_hosts_usage).to eq("www.example.net" => 1) post.acting_user.trust_level = 0 expect(post.has_host_spam?).to eq(false) SiteSetting.newuser_spam_host_threshold = 1 expect(post.has_host_spam?).to eq(true) SiteSetting.white_listed_spam_host_domains = "bla.com|boo.com | example.net " expect(post.has_host_spam?).to eq(false) end it "doesn't punish staged users" do SiteSetting.newuser_spam_host_threshold = 1 user = Fabricate(:user, staged: true, trust_level: 0) post = Fabricate(:post, raw: raw, user: user) expect(post.has_host_spam?).to eq(false) end it "punishes previously staged users that were created within 1 day" do SiteSetting.newuser_spam_host_threshold = 1 SiteSetting.newuser_max_links = 3 user = Fabricate(:user, staged: true, trust_level: 0) user.created_at = 1.hour.ago user.unstage post = Fabricate(:post, raw: raw, user: user) expect(post.has_host_spam?).to eq(true) end it "doesn't punish previously staged users over 1 day old" do SiteSetting.newuser_spam_host_threshold = 1 SiteSetting.newuser_max_links = 3 user = Fabricate(:user, staged: true, trust_level: 0) user.created_at = 1.day.ago user.unstage post = Fabricate(:post, raw: raw, user: user) expect(post.has_host_spam?).to eq(false) end it "ignores private messages" do SiteSetting.newuser_spam_host_threshold = 1 user = Fabricate(:user, trust_level: 0) post = Fabricate(:post, raw: raw, user: user, topic: Fabricate(:private_message_topic, user: user)) expect(post.has_host_spam?).to eq(false) end end it "has custom fields" do post = Fabricate(:post) expect(post.custom_fields["a"]).to eq(nil) post.custom_fields["Tommy"] = "Hanks" post.custom_fields["Vincent"] = "Vega" post.save post = Post.find(post.id) expect(post.custom_fields).to eq("Tommy" => "Hanks", "Vincent" => "Vega") end describe "#rebake!" do it "will rebake a post correctly" do post = create_post expect(post.baked_at).not_to eq(nil) first_baked = post.baked_at first_cooked = post.cooked DB.exec("UPDATE posts SET cooked = 'frogs' WHERE id = ?", [ post.id ]) post.reload post.expects(:publish_change_to_clients!).with(:rebaked) result = post.rebake! expect(post.baked_at).not_to eq(first_baked) expect(post.cooked).to eq(first_cooked) expect(result).to eq(true) end end describe "#set_owner" do let(:post) { Fabricate(:post) } let(:coding_horror) { Fabricate(:coding_horror) } it "will change owner of a post correctly" do post.set_owner(coding_horror, Discourse.system_user) post.reload expect(post.user).to eq(coding_horror) expect(post.revisions.size).to eq(1) end it "skips creating new post revision if skip_revision is true" do post.set_owner(coding_horror, Discourse.system_user, true) post.reload expect(post.user).to eq(coding_horror) expect(post.revisions.size).to eq(0) end it "uses default locale for edit reason" do I18n.locale = 'de' post.set_owner(coding_horror, Discourse.system_user) post.reload expected_reason = I18n.with_locale(SiteSetting.default_locale) do I18n.t('change_owner.post_revision_text') end expect(post.edit_reason).to eq(expected_reason) end end describe ".rebake_old" do it "will catch posts it needs to rebake" do post = create_post post.update_columns(baked_at: Time.new(2000, 1, 1), baked_version: -1) Post.rebake_old(100) post.reload expect(post.baked_at).to be > 1.day.ago baked = post.baked_at Post.rebake_old(100) post.reload expect(post.baked_at).to eq(baked) end it "will rate limit globally" do post1 = create_post post2 = create_post post3 = create_post Post.where(id: [post1.id, post2.id, post3.id]).update_all(baked_version: -1) global_setting :max_old_rebakes_per_15_minutes, 2 RateLimiter.clear_all_global! RateLimiter.enable Post.rebake_old(100) expect(post3.reload.baked_version).not_to eq(-1) expect(post2.reload.baked_version).not_to eq(-1) expect(post1.reload.baked_version).to eq(-1) end end describe ".unhide!" do before { SiteSetting.unique_posts_mins = 5 } it "will unhide the first post & make the topic visible" do hidden_topic = Fabricate(:topic, visible: false) post = create_post(topic: hidden_topic) post.update_columns(hidden: true, hidden_at: Time.now, hidden_reason_id: 1) post.reload expect(post.hidden).to eq(true) post.expects(:publish_change_to_clients!).with(:acted) post.unhide! post.reload hidden_topic.reload expect(post.hidden).to eq(false) expect(hidden_topic.visible).to eq(true) end end it "will unhide the post but will keep the topic invisible/unlisted" do hidden_topic = Fabricate(:topic, visible: false) create_post(topic: hidden_topic) second_post = create_post(topic: hidden_topic) second_post.update_columns(hidden: true, hidden_at: Time.now, hidden_reason_id: 1) second_post.expects(:publish_change_to_clients!).with(:acted) second_post.unhide! second_post.reload hidden_topic.reload expect(second_post.hidden).to eq(false) expect(hidden_topic.visible).to eq(false) end it "automatically orders post revisions by number ascending" do post = Fabricate(:post) post.revisions.create!(user_id: 1, post_id: post.id, number: 2) post.revisions.create!(user_id: 1, post_id: post.id, number: 1) expect(post.revisions.pluck(:number)).to eq([1, 2]) end describe '#link_post_uploads' do let(:video_upload) do Fabricate(:upload, url: '/uploads/default/original/1X/1/1234567890123456.mp4' ) end let(:image_upload) do Fabricate(:upload, url: '/uploads/default/original/1X/1/1234567890123456.jpg' ) end let(:audio_upload) do Fabricate(:upload, url: '/uploads/default/original/1X/1/1234567890123456.ogg' ) end let(:attachment_upload) do Fabricate(:upload, url: '/uploads/default/original/1X/1/1234567890123456.csv' ) end let(:raw) do <<~RAW Link