2015-03-03 03:25:25 +08:00
|
|
|
|
# encoding: utf-8
|
2019-04-30 08:27:42 +08:00
|
|
|
|
# frozen_string_literal: true
|
2013-02-07 04:47:36 +08:00
|
|
|
|
|
2022-07-28 10:27:38 +08:00
|
|
|
|
RSpec.describe Topic do
|
2014-03-07 18:38:24 +08:00
|
|
|
|
let(:now) { Time.zone.local(2013, 11, 20, 8, 0) }
|
2024-01-25 14:28:26 +08:00
|
|
|
|
fab!(:user) { Fabricate(:user, refresh_auto_groups: true) }
|
|
|
|
|
fab!(:user1) { Fabricate(:user, refresh_auto_groups: true) }
|
2022-06-30 08:18:12 +08:00
|
|
|
|
fab!(:whisperers_group) { Fabricate(:group) }
|
|
|
|
|
fab!(:user2) { Fabricate(:user, groups: [whisperers_group]) }
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:moderator)
|
|
|
|
|
fab!(:coding_horror)
|
|
|
|
|
fab!(:evil_trout)
|
|
|
|
|
fab!(:admin)
|
|
|
|
|
fab!(:group)
|
2024-01-29 17:52:02 +08:00
|
|
|
|
fab!(:trust_level_2)
|
2014-03-07 18:38:24 +08:00
|
|
|
|
|
2023-06-05 23:38:50 +08:00
|
|
|
|
it_behaves_like "it has custom fields"
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "Validations" do
|
2017-01-09 16:48:10 +08:00
|
|
|
|
let(:topic) { Fabricate.build(:topic) }
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#featured_link" do
|
2017-12-11 16:27:33 +08:00
|
|
|
|
describe "when featured_link contains more than a URL" do
|
|
|
|
|
it "should not be valid" do
|
|
|
|
|
topic.featured_link = "http://meta.discourse.org TEST"
|
|
|
|
|
expect(topic).to_not be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when featured_link is a valid URL" do
|
|
|
|
|
it "should be valid" do
|
|
|
|
|
topic.featured_link = "http://meta.discourse.org"
|
|
|
|
|
expect(topic).to be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#external_id" do
|
2022-02-09 11:55:32 +08:00
|
|
|
|
describe "when external_id is too long" do
|
|
|
|
|
it "should not be valid" do
|
|
|
|
|
topic.external_id = "a" * (Topic::EXTERNAL_ID_MAX_LENGTH + 1)
|
|
|
|
|
expect(topic).to_not be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when external_id has invalid characters" do
|
|
|
|
|
it "should not be valid" do
|
|
|
|
|
topic.external_id = "a*&^!@()#"
|
|
|
|
|
expect(topic).to_not be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when external_id is an empty string" do
|
|
|
|
|
it "should not be valid" do
|
|
|
|
|
topic.external_id = ""
|
|
|
|
|
expect(topic).to_not be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when external_id has already been used" do
|
|
|
|
|
it "should not be valid" do
|
|
|
|
|
topic2 = Fabricate(:topic, external_id: "asdf")
|
|
|
|
|
topic.external_id = "asdf"
|
|
|
|
|
expect(topic).to_not be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when external_id is nil" do
|
|
|
|
|
it "should be valid" do
|
|
|
|
|
topic.external_id = nil
|
|
|
|
|
expect(topic).to be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when external_id is valid" do
|
|
|
|
|
it "should be valid" do
|
|
|
|
|
topic.external_id = "abc_123-ZXY"
|
|
|
|
|
expect(topic).to be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#title" do
|
2017-01-09 16:48:10 +08:00
|
|
|
|
it { is_expected.to validate_presence_of :title }
|
|
|
|
|
|
2017-01-16 13:24:47 +08:00
|
|
|
|
describe "censored words" do
|
2020-05-23 12:56:13 +08:00
|
|
|
|
after { Discourse.redis.flushdb }
|
2017-06-29 04:56:44 +08:00
|
|
|
|
|
2017-01-16 13:24:47 +08:00
|
|
|
|
describe "when title contains censored words" do
|
2020-12-15 06:48:27 +08:00
|
|
|
|
after { WordWatcher.clear_cache! }
|
|
|
|
|
|
2017-01-09 16:48:10 +08:00
|
|
|
|
it "should not be valid" do
|
2017-06-29 04:56:44 +08:00
|
|
|
|
%w[pineapple pen].each do |w|
|
|
|
|
|
Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor])
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2017-01-28 10:55:49 +08:00
|
|
|
|
|
2017-06-29 01:13:40 +08:00
|
|
|
|
topic.title = "pen PinEapple apple pen is a complete sentence"
|
2017-01-09 16:48:10 +08:00
|
|
|
|
|
|
|
|
|
expect(topic).to_not be_valid
|
|
|
|
|
|
|
|
|
|
expect(topic.errors.full_messages.first).to include(
|
2017-01-16 13:24:47 +08:00
|
|
|
|
I18n.t("errors.messages.contains_censored_words", censored_words: "pen, pineapple"),
|
2017-01-09 16:48:10 +08:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2017-06-29 01:13:40 +08:00
|
|
|
|
describe "titles with censored words not on boundaries" do
|
|
|
|
|
it "should be valid" do
|
2017-06-29 04:56:44 +08:00
|
|
|
|
Fabricate(:watched_word, word: "apple", action: WatchedWord.actions[:censor])
|
2017-06-29 01:13:40 +08:00
|
|
|
|
topic.title = "Pineapples are great fruit! Applebee's is a great restaurant"
|
|
|
|
|
expect(topic).to be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2017-01-09 16:48:10 +08:00
|
|
|
|
describe "when title does not contain censored words" do
|
|
|
|
|
it "should be valid" do
|
|
|
|
|
topic.title = "The cake is a lie"
|
|
|
|
|
|
|
|
|
|
expect(topic).to be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
2017-01-16 13:24:47 +08:00
|
|
|
|
|
|
|
|
|
describe "escape special characters in censored words" do
|
|
|
|
|
before do
|
2017-06-29 04:56:44 +08:00
|
|
|
|
%w[co(onut coconut a**le].each do |w|
|
|
|
|
|
Fabricate(:watched_word, word: w, action: WatchedWord.actions[:censor])
|
|
|
|
|
end
|
2017-01-16 13:24:47 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-06-29 04:56:44 +08:00
|
|
|
|
it "should not be valid" do
|
2017-01-16 13:24:47 +08:00
|
|
|
|
topic.title = "I have a co(onut a**le"
|
|
|
|
|
|
|
|
|
|
expect(topic.valid?).to eq(false)
|
|
|
|
|
|
|
|
|
|
expect(topic.errors.full_messages.first).to include(
|
|
|
|
|
I18n.t("errors.messages.contains_censored_words", censored_words: "co(onut, a**le"),
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
2017-01-09 16:48:10 +08:00
|
|
|
|
end
|
2019-10-02 08:38:34 +08:00
|
|
|
|
|
|
|
|
|
describe "blocked words" do
|
|
|
|
|
describe "when title contains watched words" do
|
2020-12-15 06:48:27 +08:00
|
|
|
|
after { WordWatcher.clear_cache! }
|
|
|
|
|
|
2019-10-02 08:38:34 +08:00
|
|
|
|
it "should not be valid" do
|
|
|
|
|
Fabricate(:watched_word, word: "pineapple", action: WatchedWord.actions[:block])
|
|
|
|
|
|
|
|
|
|
topic.title = "pen PinEapple apple pen is a complete sentence"
|
|
|
|
|
|
|
|
|
|
expect(topic).to_not be_valid
|
|
|
|
|
|
|
|
|
|
expect(topic.errors.full_messages.first).to include(
|
|
|
|
|
I18n.t("contains_blocked_word", word: "PinEapple"),
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2017-01-09 16:48:10 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
2015-01-06 00:04:23 +08:00
|
|
|
|
it { is_expected.to rate_limit }
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#visible_post_types" do
|
2015-09-11 04:01:23 +08:00
|
|
|
|
let(:types) { Post.types }
|
|
|
|
|
|
2022-06-30 08:18:12 +08:00
|
|
|
|
before do
|
2022-12-17 00:42:51 +08:00
|
|
|
|
SiteSetting.whispers_allowed_groups = "#{Group::AUTO_GROUPS[:staff]}|#{whisperers_group.id}"
|
2022-06-30 08:18:12 +08:00
|
|
|
|
end
|
|
|
|
|
|
2015-09-11 04:01:23 +08:00
|
|
|
|
it "returns the appropriate types for anonymous users" do
|
2015-09-22 06:50:52 +08:00
|
|
|
|
post_types = Topic.visible_post_types
|
2015-09-11 04:01:23 +08:00
|
|
|
|
|
|
|
|
|
expect(post_types).to include(types[:regular])
|
|
|
|
|
expect(post_types).to include(types[:moderator_action])
|
|
|
|
|
expect(post_types).to include(types[:small_action])
|
|
|
|
|
expect(post_types).to_not include(types[:whisper])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the appropriate types for regular users" do
|
2015-09-22 06:50:52 +08:00
|
|
|
|
post_types = Topic.visible_post_types(Fabricate.build(:user))
|
2015-09-11 04:01:23 +08:00
|
|
|
|
|
|
|
|
|
expect(post_types).to include(types[:regular])
|
|
|
|
|
expect(post_types).to include(types[:moderator_action])
|
|
|
|
|
expect(post_types).to include(types[:small_action])
|
|
|
|
|
expect(post_types).to_not include(types[:whisper])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the appropriate types for staff users" do
|
2022-06-30 08:18:12 +08:00
|
|
|
|
post_types = Topic.visible_post_types(moderator)
|
|
|
|
|
|
|
|
|
|
expect(post_types).to include(types[:regular])
|
|
|
|
|
expect(post_types).to include(types[:moderator_action])
|
|
|
|
|
expect(post_types).to include(types[:small_action])
|
|
|
|
|
expect(post_types).to include(types[:whisper])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns the appropriate types for whisperer users" do
|
|
|
|
|
post_types = Topic.visible_post_types(user2)
|
2015-09-11 04:01:23 +08:00
|
|
|
|
|
|
|
|
|
expect(post_types).to include(types[:regular])
|
|
|
|
|
expect(post_types).to include(types[:moderator_action])
|
|
|
|
|
expect(post_types).to include(types[:small_action])
|
|
|
|
|
expect(post_types).to include(types[:whisper])
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "slug" do
|
|
|
|
|
context "with encoded generator" do
|
2015-04-13 22:50:41 +08:00
|
|
|
|
before { SiteSetting.slug_generation_method = "encoded" }
|
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
context "with ascii letters" do
|
|
|
|
|
let!(:title) { "hello world topic" }
|
|
|
|
|
let!(:slug) { "hello-world-topic" }
|
|
|
|
|
let!(:topic) { Fabricate.build(:topic, title: title) }
|
|
|
|
|
|
|
|
|
|
it "returns a Slug for a title" do
|
|
|
|
|
expect(topic.title).to eq(title)
|
|
|
|
|
expect(topic.slug).to eq(slug)
|
|
|
|
|
end
|
2015-04-13 22:50:41 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "for cjk characters" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
let!(:title) { "熱帶風暴畫眉" }
|
|
|
|
|
let!(:topic) { Fabricate.build(:topic, title: title) }
|
2017-10-27 11:02:12 +08:00
|
|
|
|
|
2015-04-13 22:50:41 +08:00
|
|
|
|
it "returns encoded Slug for a title" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic.title).to eq(title)
|
2019-10-11 23:38:16 +08:00
|
|
|
|
expect(topic.slug).to eq("%E7%86%B1%E5%B8%B6%E9%A2%A8%E6%9A%B4%E7%95%AB%E7%9C%89")
|
2015-04-13 22:50:41 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "for numbers" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
let!(:title) { "123456789" }
|
|
|
|
|
let!(:slug) { "topic" }
|
|
|
|
|
let!(:topic) { Fabricate.build(:topic, title: title) }
|
|
|
|
|
|
2015-04-13 22:50:41 +08:00
|
|
|
|
it "generates default slug" do
|
2015-05-04 19:48:37 +08:00
|
|
|
|
Slug.expects(:for).with(title).returns("topic")
|
2015-04-13 22:50:41 +08:00
|
|
|
|
expect(Fabricate.build(:topic, title: title).slug).to eq("topic")
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-15 06:13:03 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with none generator" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
let!(:title) { "熱帶風暴畫眉" }
|
|
|
|
|
let!(:slug) { "topic" }
|
|
|
|
|
let!(:topic) { Fabricate.build(:topic, title: title) }
|
2017-10-27 11:02:12 +08:00
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
before { SiteSetting.slug_generation_method = "none" }
|
2014-09-16 19:15:05 +08:00
|
|
|
|
|
2015-04-13 22:50:41 +08:00
|
|
|
|
it "returns a Slug for a title" do
|
2015-05-04 19:48:37 +08:00
|
|
|
|
Slug.expects(:for).with(title).returns("topic")
|
2015-04-13 22:50:41 +08:00
|
|
|
|
expect(Fabricate.build(:topic, title: title).slug).to eq(slug)
|
|
|
|
|
end
|
2014-09-16 19:15:05 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe "#ascii_generator" do
|
2015-04-13 22:50:41 +08:00
|
|
|
|
before { SiteSetting.slug_generation_method = "ascii" }
|
2017-10-27 11:02:12 +08:00
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
context "with ascii letters" do
|
|
|
|
|
let!(:title) { "hello world topic" }
|
|
|
|
|
let!(:slug) { "hello-world-topic" }
|
|
|
|
|
let!(:topic) { Fabricate.build(:topic, title: title) }
|
|
|
|
|
|
|
|
|
|
it "returns a Slug for a title" do
|
|
|
|
|
Slug.expects(:for).with(title).returns(slug)
|
|
|
|
|
expect(Fabricate.build(:topic, title: title).slug).to eq(slug)
|
|
|
|
|
end
|
2015-04-13 22:50:41 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "for cjk characters" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
let!(:title) { "熱帶風暴畫眉" }
|
|
|
|
|
let!(:slug) { "topic" }
|
|
|
|
|
let!(:topic) { Fabricate.build(:topic, title: title) }
|
2017-10-27 11:02:12 +08:00
|
|
|
|
|
2015-04-13 22:50:41 +08:00
|
|
|
|
it "returns 'topic' when the slug is empty (say, non-latin characters)" do
|
2015-05-04 19:48:37 +08:00
|
|
|
|
Slug.expects(:for).with(title).returns("topic")
|
2015-04-13 22:50:41 +08:00
|
|
|
|
expect(Fabricate.build(:topic, title: title).slug).to eq("topic")
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-15 06:13:03 +08:00
|
|
|
|
end
|
2022-05-26 06:05:06 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "slug computed hooks" do
|
2022-05-26 06:05:06 +08:00
|
|
|
|
before do
|
|
|
|
|
invert_slug = ->(topic, slug, title) { slug.reverse }
|
|
|
|
|
Topic.slug_computed_callbacks << invert_slug
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let!(:title) { "hello test topic" }
|
|
|
|
|
let!(:slug) { "hello-test-topic".reverse }
|
|
|
|
|
let!(:other_title) { "other title" }
|
|
|
|
|
let!(:other_slug) { "other-title".reverse }
|
|
|
|
|
let!(:topic) { Fabricate.build(:topic, title: title) }
|
|
|
|
|
|
|
|
|
|
it "returns a reversed slug for a title" do
|
|
|
|
|
expect(topic.title).to eq(title)
|
|
|
|
|
expect(topic.slug).to eq(slug)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns a reversed slug after the title is changed" do
|
|
|
|
|
expect(topic.title).to eq(title)
|
|
|
|
|
expect(topic.slug).to eq(slug)
|
|
|
|
|
|
|
|
|
|
topic.title = other_title
|
|
|
|
|
expect(topic.title).to eq(other_title)
|
|
|
|
|
expect(topic.slug).to eq(other_slug)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
after { Topic.slug_computed_callbacks.clear }
|
|
|
|
|
end
|
2013-02-15 06:13:03 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
2024-02-13 13:20:24 +08:00
|
|
|
|
describe "slugless_url" do
|
|
|
|
|
fab!(:topic)
|
|
|
|
|
|
|
|
|
|
it "returns the correct url" do
|
|
|
|
|
expect(topic.slugless_url).to eq("/t/#{topic.id}")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "works with post id" do
|
|
|
|
|
expect(topic.slugless_url(123)).to eq("/t/#{topic.id}/123")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "works with subfolder install" do
|
|
|
|
|
set_subfolder "/forum"
|
|
|
|
|
|
|
|
|
|
expect(topic.slugless_url).to eq("/forum/t/#{topic.id}")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "updating a title to be shorter" do
|
2013-06-01 03:22:34 +08:00
|
|
|
|
let!(:topic) { Fabricate(:topic) }
|
|
|
|
|
|
|
|
|
|
it "doesn't update it to be shorter due to cleaning using TextCleaner" do
|
|
|
|
|
topic.title = "unread glitch"
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.save).to eq(false)
|
2013-06-01 03:22:34 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "private message title" do
|
2013-06-05 05:58:25 +08:00
|
|
|
|
before do
|
2017-07-07 14:09:14 +08:00
|
|
|
|
SiteSetting.min_topic_title_length = 15
|
2018-01-31 13:56:00 +08:00
|
|
|
|
SiteSetting.min_personal_message_title_length = 3
|
2013-06-05 05:58:25 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "allows shorter titles" do
|
2018-01-31 13:56:00 +08:00
|
|
|
|
pm =
|
|
|
|
|
Fabricate.build(
|
|
|
|
|
:private_message_topic,
|
|
|
|
|
title: "a" * SiteSetting.min_personal_message_title_length,
|
|
|
|
|
)
|
2013-06-05 05:58:25 +08:00
|
|
|
|
expect(pm).to be_valid
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "but not too short" do
|
|
|
|
|
pm = Fabricate.build(:private_message_topic, title: "a")
|
|
|
|
|
expect(pm).to_not be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "admin topic title" do
|
2013-06-30 05:57:10 +08:00
|
|
|
|
it "allows really short titles" do
|
|
|
|
|
pm = Fabricate.build(:private_message_topic, user: admin, title: "a")
|
|
|
|
|
expect(pm).to be_valid
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "but not blank" do
|
|
|
|
|
pm = Fabricate.build(:private_message_topic, title: "")
|
|
|
|
|
expect(pm).to_not be_valid
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "topic title uniqueness" do
|
2021-12-16 01:41:14 +08:00
|
|
|
|
fab!(:category1) { Fabricate(:category) }
|
|
|
|
|
fab!(:category2) { Fabricate(:category) }
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
2021-12-21 02:59:10 +08:00
|
|
|
|
fab!(:topic) { Fabricate(:topic, category: category1) }
|
2020-06-18 23:19:47 +08:00
|
|
|
|
let(:new_topic) { Fabricate.build(:topic, title: topic.title, category: category1) }
|
|
|
|
|
let(:new_topic_different_cat) do
|
|
|
|
|
Fabricate.build(:topic, title: topic.title, category: category2)
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
|
|
context "when duplicates aren't allowed" do
|
|
|
|
|
before do
|
2020-06-18 23:19:47 +08:00
|
|
|
|
SiteSetting.allow_duplicate_topic_titles = false
|
|
|
|
|
SiteSetting.allow_duplicate_topic_titles_category = false
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "won't allow another topic to be created with the same name" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(new_topic).not_to be_valid
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2020-06-18 23:19:47 +08:00
|
|
|
|
it "won't even allow another topic to be created with the same name but different category" do
|
|
|
|
|
expect(new_topic_different_cat).not_to be_valid
|
|
|
|
|
end
|
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
|
it "won't allow another topic with an upper case title to be created" do
|
|
|
|
|
new_topic.title = new_topic.title.upcase
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(new_topic).not_to be_valid
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "allows it when the topic is deleted" do
|
|
|
|
|
topic.destroy
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(new_topic).to be_valid
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "allows a private message to be created with the same topic" do
|
|
|
|
|
new_topic.archetype = Archetype.private_message
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(new_topic).to be_valid
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when duplicates are allowed" do
|
|
|
|
|
before do
|
2020-06-18 23:19:47 +08:00
|
|
|
|
SiteSetting.allow_duplicate_topic_titles = true
|
|
|
|
|
SiteSetting.allow_duplicate_topic_titles_category = false
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-05-23 12:52:12 +08:00
|
|
|
|
it "will allow another topic to be created with the same name" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(new_topic).to be_valid
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
2020-06-18 23:19:47 +08:00
|
|
|
|
context "when duplicates are allowed if the category is different" do
|
|
|
|
|
before do
|
|
|
|
|
SiteSetting.allow_duplicate_topic_titles = false
|
|
|
|
|
SiteSetting.allow_duplicate_topic_titles_category = true
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "will allow another topic to be created with the same name but different category" do
|
|
|
|
|
expect(new_topic_different_cat).to be_valid
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "won't allow another topic to be created with the same name in same category" do
|
|
|
|
|
expect(new_topic).not_to be_valid
|
|
|
|
|
end
|
2021-04-21 17:36:32 +08:00
|
|
|
|
|
|
|
|
|
it "other errors will not be cleared" do
|
|
|
|
|
SiteSetting.min_topic_title_length = 5
|
|
|
|
|
topic.update!(title: "more than 5 characters but less than 134")
|
|
|
|
|
SiteSetting.min_topic_title_length = 134
|
|
|
|
|
new_topic_different_cat.title = "more than 5 characters but less than 134"
|
|
|
|
|
expect(new_topic_different_cat).not_to be_valid
|
|
|
|
|
expect(new_topic_different_cat.errors[:title]).to include(
|
|
|
|
|
I18n.t("errors.messages.too_short", count: 134),
|
|
|
|
|
)
|
|
|
|
|
end
|
2020-06-18 23:19:47 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "html in title" do
|
2013-04-22 11:48:05 +08:00
|
|
|
|
def build_topic_with_title(title)
|
2013-05-23 12:52:12 +08:00
|
|
|
|
build(:topic, title: title).tap { |t| t.valid? }
|
2013-04-22 11:48:05 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let(:topic_bold) { build_topic_with_title("Topic with <b>bold</b> text in its title") }
|
|
|
|
|
let(:topic_image) do
|
|
|
|
|
build_topic_with_title("Topic with <img src='something'> image in its title")
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2013-04-22 11:48:05 +08:00
|
|
|
|
let(:topic_script) do
|
|
|
|
|
build_topic_with_title("Topic with <script>alert('title')</script> script in its title")
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2017-05-15 22:27:54 +08:00
|
|
|
|
let(:topic_emoji) { build_topic_with_title("I 💖 candy alot") }
|
2019-03-21 16:11:33 +08:00
|
|
|
|
let(:topic_modifier_emoji) { build_topic_with_title("I 👨🌾 candy alot") }
|
2019-05-21 22:56:51 +08:00
|
|
|
|
let(:topic_shortcut_emoji) { build_topic_with_title("I love candy :)") }
|
2019-12-04 00:32:33 +08:00
|
|
|
|
let(:topic_inline_emoji) { build_topic_with_title("Hello😊World") }
|
2013-02-20 05:08:23 +08:00
|
|
|
|
|
2014-07-14 15:18:02 +08:00
|
|
|
|
it "escapes script contents" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic_script.fancy_title).to eq(
|
|
|
|
|
"Topic with <script>alert(‘title’)</script> script in its title",
|
|
|
|
|
)
|
2013-03-07 00:36:42 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-05-15 22:27:54 +08:00
|
|
|
|
it "expands emojis" do
|
|
|
|
|
expect(topic_emoji.fancy_title).to eq("I :sparkling_heart: candy alot")
|
|
|
|
|
end
|
|
|
|
|
|
2019-03-21 16:11:33 +08:00
|
|
|
|
it "keeps combined emojis" do
|
|
|
|
|
expect(topic_modifier_emoji.fancy_title).to eq("I :man_farmer: candy alot")
|
|
|
|
|
end
|
|
|
|
|
|
2014-07-14 15:18:02 +08:00
|
|
|
|
it "escapes bold contents" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic_bold.fancy_title).to eq("Topic with <b>bold</b> text in its title")
|
2014-07-14 15:18:02 +08:00
|
|
|
|
end
|
2014-04-19 03:01:21 +08:00
|
|
|
|
|
2014-07-14 15:18:02 +08:00
|
|
|
|
it "escapes image contents" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic_image.fancy_title).to eq(
|
|
|
|
|
"Topic with <img src=‘something’> image in its title",
|
|
|
|
|
)
|
2013-02-20 05:08:23 +08:00
|
|
|
|
end
|
|
|
|
|
|
2018-09-18 06:54:44 +08:00
|
|
|
|
it "always escapes title" do
|
|
|
|
|
topic_script.title = topic_script.title + "x" * Topic.max_fancy_title_length
|
|
|
|
|
expect(topic_script.fancy_title).to eq(ERB::Util.html_escape(topic_script.title))
|
|
|
|
|
# not really needed, but just in case
|
|
|
|
|
expect(topic_script.fancy_title).not_to include("<script>")
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with emoji shortcuts enabled" do
|
2019-05-21 22:56:51 +08:00
|
|
|
|
before { SiteSetting.enable_emoji_shortcuts = true }
|
|
|
|
|
|
|
|
|
|
it "converts emoji shortcuts into emoji" do
|
|
|
|
|
expect(topic_shortcut_emoji.fancy_title).to eq("I love candy :slight_smile:")
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with emojis disabled" do
|
2019-05-21 22:56:51 +08:00
|
|
|
|
before { SiteSetting.enable_emoji = false }
|
|
|
|
|
|
|
|
|
|
it "does not convert emoji shortcuts" do
|
|
|
|
|
expect(topic_shortcut_emoji.fancy_title).to eq("I love candy :)")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with emoji shortcuts disabled" do
|
2019-05-21 22:56:51 +08:00
|
|
|
|
before { SiteSetting.enable_emoji_shortcuts = false }
|
|
|
|
|
|
|
|
|
|
it "does not convert emoji shortcuts" do
|
|
|
|
|
expect(topic_shortcut_emoji.fancy_title).to eq("I love candy :)")
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-12-04 00:32:33 +08:00
|
|
|
|
|
|
|
|
|
it "keeps inline emojis if inline emoji setting disabled" do
|
|
|
|
|
SiteSetting.enable_inline_emoji_translation = false
|
|
|
|
|
expect(topic_inline_emoji.fancy_title).to eq("Hello😊World")
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "expands inline emojis if inline emoji setting enabled" do
|
|
|
|
|
SiteSetting.enable_inline_emoji_translation = true
|
|
|
|
|
expect(topic_inline_emoji.fancy_title).to eq("Hello:blush:World")
|
|
|
|
|
end
|
2013-02-20 05:08:23 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "fancy title" do
|
2018-09-18 06:54:44 +08:00
|
|
|
|
let(:topic) { Fabricate.build(:topic, title: %{"this topic" -- has ``fancy stuff''}) }
|
2013-02-20 05:08:23 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with title_fancy_entities disabled" do
|
2015-09-24 11:37:53 +08:00
|
|
|
|
before { SiteSetting.title_fancy_entities = false }
|
2013-02-20 05:08:23 +08:00
|
|
|
|
|
2014-04-18 12:48:38 +08:00
|
|
|
|
it "doesn't add entities to the title" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.fancy_title).to eq(""this topic" -- has ``fancy stuff''")
|
2013-02-20 05:08:23 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with title_fancy_entities enabled" do
|
2015-09-24 11:37:53 +08:00
|
|
|
|
before { SiteSetting.title_fancy_entities = true }
|
2013-02-20 05:08:23 +08:00
|
|
|
|
|
2015-09-24 11:37:53 +08:00
|
|
|
|
it "converts the title to have fancy entities and updates" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.fancy_title).to eq(
|
|
|
|
|
"“this topic” – has “fancy stuff”",
|
|
|
|
|
)
|
2015-09-24 11:37:53 +08:00
|
|
|
|
topic.title = "this is my test hello world... yay"
|
|
|
|
|
topic.save!
|
|
|
|
|
topic.reload
|
|
|
|
|
expect(topic.fancy_title).to eq("This is my test hello world… yay")
|
|
|
|
|
|
|
|
|
|
topic.title = "I made a change to the title"
|
|
|
|
|
topic.save!
|
|
|
|
|
|
|
|
|
|
topic.reload
|
|
|
|
|
expect(topic.fancy_title).to eq("I made a change to the title")
|
|
|
|
|
|
|
|
|
|
# another edge case
|
|
|
|
|
topic.title = "this is another edge case"
|
|
|
|
|
expect(topic.fancy_title).to eq("this is another edge case")
|
2013-02-20 05:08:23 +08:00
|
|
|
|
end
|
2017-10-19 15:41:03 +08:00
|
|
|
|
|
2017-10-18 04:37:13 +08:00
|
|
|
|
it "works with long title that results in lots of entities" do
|
2018-09-18 06:54:44 +08:00
|
|
|
|
long_title = "NEW STOCK PICK: PRCT - LAST PICK UP 233%, NNCO#{"." * 150} ofoum"
|
2017-10-18 04:37:13 +08:00
|
|
|
|
topic.title = long_title
|
|
|
|
|
|
|
|
|
|
expect { topic.save! }.to_not raise_error
|
|
|
|
|
expect(topic.fancy_title).to eq(long_title)
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when in readonly mode" do
|
2017-10-19 15:41:03 +08:00
|
|
|
|
before { Discourse.enable_readonly_mode }
|
|
|
|
|
|
|
|
|
|
after { Discourse.disable_readonly_mode }
|
|
|
|
|
|
|
|
|
|
it "should not attempt to update `fancy_title`" do
|
|
|
|
|
topic.save!
|
|
|
|
|
expect(topic.fancy_title).to eq(
|
|
|
|
|
"“this topic” – has “fancy stuff”",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
topic.title = "This is a test testing testing"
|
|
|
|
|
expect(topic.fancy_title).to eq("This is a test testing testing")
|
|
|
|
|
|
|
|
|
|
expect(topic.reload.read_attribute(:fancy_title)).to eq(
|
|
|
|
|
"“this topic” – has “fancy stuff”",
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-20 05:08:23 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "category validation" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
fab!(:category) { Fabricate(:category_with_definition) }
|
2019-05-07 23:05:53 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when allow_uncategorized_topics is false" do
|
2017-07-07 14:09:14 +08:00
|
|
|
|
before { SiteSetting.allow_uncategorized_topics = false }
|
2013-10-09 02:40:31 +08:00
|
|
|
|
|
|
|
|
|
it "does not allow nil category" do
|
2013-10-24 07:05:51 +08:00
|
|
|
|
topic = Fabricate.build(:topic, category: nil)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic).not_to be_valid
|
|
|
|
|
expect(topic.errors[:category_id]).to be_present
|
2013-10-09 02:40:31 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-10-25 06:33:18 +08:00
|
|
|
|
it "allows PMs" do
|
|
|
|
|
topic = Fabricate.build(:topic, category: nil, archetype: Archetype.private_message)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic).to be_valid
|
2013-10-25 06:33:18 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-10-09 02:40:31 +08:00
|
|
|
|
it "passes for topics with a category" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(Fabricate.build(:topic, category: category)).to be_valid
|
2013-10-09 02:40:31 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when allow_uncategorized_topics is true" do
|
2017-07-07 14:09:14 +08:00
|
|
|
|
before { SiteSetting.allow_uncategorized_topics = true }
|
2013-10-09 02:40:31 +08:00
|
|
|
|
|
|
|
|
|
it "passes for topics with nil category" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(Fabricate.build(:topic, category: nil)).to be_valid
|
2013-10-09 02:40:31 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "passes for topics with a category" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(Fabricate.build(:topic, category: category)).to be_valid
|
2013-10-09 02:40:31 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-27 18:21:10 +08:00
|
|
|
|
describe ".similar_to" do
|
2020-07-28 11:53:25 +08:00
|
|
|
|
fab!(:category) { Fabricate(:category_with_definition) }
|
2013-03-15 02:45:29 +08:00
|
|
|
|
|
2020-07-28 11:53:25 +08:00
|
|
|
|
it "returns an empty array with nil params" do
|
|
|
|
|
expect(Topic.similar_to(nil, nil)).to eq([])
|
2013-03-15 02:45:29 +08:00
|
|
|
|
end
|
|
|
|
|
|
2014-04-15 03:20:41 +08:00
|
|
|
|
context "with a category definition" do
|
|
|
|
|
it "excludes the category definition topic from similar_to" do
|
2020-07-28 11:53:25 +08:00
|
|
|
|
expect(Topic.similar_to("category definition for", "no body")).to eq([])
|
2014-04-15 03:20:41 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-03-26 01:10:05 +08:00
|
|
|
|
it "does not result in a syntax error when removing accents" do
|
|
|
|
|
SiteSetting.search_ignore_accents = true
|
|
|
|
|
expect(Topic.similar_to("something", "it's")).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2020-08-21 10:51:37 +08:00
|
|
|
|
it "does not result in a syntax error when raw is blank after cooking" do
|
|
|
|
|
expect(Topic.similar_to("some title", "#")).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2020-09-22 05:53:12 +08:00
|
|
|
|
it "does not result in invalid statement when prepared data is blank" do
|
|
|
|
|
expect(Topic.similar_to("some title", "https://discourse.org/#INCORRECT#URI")).to be_empty
|
|
|
|
|
end
|
|
|
|
|
|
2021-10-04 11:40:22 +08:00
|
|
|
|
it "does not result in invalid statement when title is all stopwords for zh_CN" do
|
|
|
|
|
SiteSetting.default_locale = "zh_CN"
|
|
|
|
|
|
|
|
|
|
expect(Topic.similar_to("怎么上自己的", "")).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2023-01-10 01:19:51 +08:00
|
|
|
|
it "does not result in invalid statement when title contains unicode characters" do
|
|
|
|
|
SiteSetting.search_ignore_accents = true
|
|
|
|
|
|
|
|
|
|
expect(Topic.similar_to("'bad quotes'", "'bad quotes'")).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2013-03-15 02:45:29 +08:00
|
|
|
|
context "with a similar topic" do
|
2020-07-28 11:53:25 +08:00
|
|
|
|
fab!(:post) do
|
2023-05-04 09:20:52 +08:00
|
|
|
|
with_search_indexer_enabled do
|
|
|
|
|
create_post(title: "Evil trout is the dude who posted this topic")
|
|
|
|
|
end
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2013-03-15 02:45:29 +08:00
|
|
|
|
|
2020-07-28 11:53:25 +08:00
|
|
|
|
let(:topic) { post.topic }
|
|
|
|
|
|
|
|
|
|
before { SearchIndexer.enable }
|
|
|
|
|
|
2013-03-15 02:45:29 +08:00
|
|
|
|
it "returns the similar topic if the title is similar" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(
|
|
|
|
|
Topic.similar_to(
|
|
|
|
|
"has evil trout made any topics?",
|
|
|
|
|
"i am wondering has evil trout made any topics?",
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2015-01-06 00:04:23 +08:00
|
|
|
|
).to eq([topic])
|
2013-03-15 02:45:29 +08:00
|
|
|
|
end
|
|
|
|
|
|
2020-07-28 13:23:53 +08:00
|
|
|
|
it "returns the similar topic even if raw is blank" do
|
|
|
|
|
expect(Topic.similar_to("has evil trout made any topics?", "")).to eq([topic])
|
|
|
|
|
end
|
|
|
|
|
|
2020-07-28 11:53:25 +08:00
|
|
|
|
it "matches title against title and raw against raw when searching for topics" do
|
|
|
|
|
topic.update!(title: "1 2 3 numbered titles")
|
|
|
|
|
post.update!(raw: "random toy poodle")
|
2013-06-13 01:43:59 +08:00
|
|
|
|
|
2020-07-28 11:53:25 +08:00
|
|
|
|
expect(Topic.similar_to("unrelated term", "1 2 3 poddle")).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2020-07-28 15:20:18 +08:00
|
|
|
|
it "doesnt match numbered lists against numbers in Post#raw" do
|
|
|
|
|
post.update!(raw: <<~RAW)
|
|
|
|
|
Internet Explorer 11+ Oct 2013 Google Chrome 32+ Jan 2014 Firefox 27+ Feb 2014 Safari 6.1+ Jul 2012 Safari, iOS 8+ Oct 2014
|
|
|
|
|
RAW
|
|
|
|
|
|
|
|
|
|
post.topic.update!(title: "Where are we with browser support in 2019?")
|
|
|
|
|
|
|
|
|
|
topics = Topic.similar_to("Videos broken in composer", <<~RAW)
|
|
|
|
|
1. Do something
|
|
|
|
|
2. Do something else
|
|
|
|
|
3. Do more things
|
|
|
|
|
RAW
|
|
|
|
|
|
|
|
|
|
expect(topics).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2022-12-07 11:33:01 +08:00
|
|
|
|
it "does not return topics from categories with search priority set to ignore" do
|
|
|
|
|
expect(Topic.similar_to("has evil trout made any topics?", "")).to eq([topic])
|
|
|
|
|
|
|
|
|
|
topic.category.update!(search_priority: Searchable::PRIORITIES[:ignore])
|
|
|
|
|
|
|
|
|
|
expect(Topic.similar_to("has evil trout made any topics?", "")).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not return topics from categories which the user has muted" do
|
|
|
|
|
expect(Topic.similar_to("has evil trout made any topics?", "", user)).to eq([topic])
|
|
|
|
|
|
|
|
|
|
CategoryUser.create!(
|
|
|
|
|
category: topic.category,
|
|
|
|
|
user: user,
|
|
|
|
|
notification_level: CategoryUser.notification_levels[:muted],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(Topic.similar_to("has evil trout made any topics?", "", user)).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2022-12-12 06:31:46 +08:00
|
|
|
|
it "does not return topics from child categories where the user has muted the parent category" do
|
|
|
|
|
expect(Topic.similar_to("has evil trout made any topics?", "", user)).to eq([topic])
|
|
|
|
|
|
|
|
|
|
parent_category = topic.category
|
|
|
|
|
child_category = Fabricate(:category, parent_category: parent_category)
|
|
|
|
|
topic.update!(category: child_category)
|
|
|
|
|
CategoryUser.create!(
|
|
|
|
|
category: parent_category,
|
|
|
|
|
user: user,
|
|
|
|
|
notification_level: CategoryUser.notification_levels[:muted],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(Topic.similar_to("has evil trout made any topics?", "", user)).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with secure categories" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:group)
|
2022-12-07 11:33:01 +08:00
|
|
|
|
fab!(:private_category) { Fabricate(:private_category, group: group) }
|
|
|
|
|
|
|
|
|
|
before { topic.update!(category: private_category) }
|
2013-06-13 01:43:59 +08:00
|
|
|
|
|
|
|
|
|
it "doesn't return topics from private categories" do
|
|
|
|
|
expect(
|
|
|
|
|
Topic.similar_to(
|
|
|
|
|
"has evil trout made any topics?",
|
|
|
|
|
"i am wondering has evil trout made any topics?",
|
|
|
|
|
user,
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2013-06-13 01:43:59 +08:00
|
|
|
|
).to be_blank
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should return the cat since the user can see it" do
|
2022-12-07 11:33:01 +08:00
|
|
|
|
group.add(user)
|
|
|
|
|
|
2013-06-13 01:43:59 +08:00
|
|
|
|
expect(
|
|
|
|
|
Topic.similar_to(
|
|
|
|
|
"has evil trout made any topics?",
|
|
|
|
|
"i am wondering has evil trout made any topics?",
|
|
|
|
|
user,
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2013-06-13 01:43:59 +08:00
|
|
|
|
).to include(topic)
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-03-15 02:45:29 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "post_numbers" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
let!(:topic) { Fabricate(:topic) }
|
|
|
|
|
let!(:p1) { Fabricate(:post, topic: topic, user: topic.user) }
|
|
|
|
|
let!(:p2) { Fabricate(:post, topic: topic, user: topic.user) }
|
|
|
|
|
let!(:p3) { Fabricate(:post, topic: topic, user: topic.user) }
|
|
|
|
|
|
|
|
|
|
it "returns the post numbers of the topic" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.post_numbers).to eq([1, 2, 3])
|
2013-02-06 03:16:51 +08:00
|
|
|
|
p2.destroy
|
2013-04-22 11:48:05 +08:00
|
|
|
|
topic.reload
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.post_numbers).to eq([1, 3])
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2018-02-26 15:18:34 +08:00
|
|
|
|
describe "#invite" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
2018-02-26 15:18:34 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with rate limits" do
|
2024-01-25 14:28:26 +08:00
|
|
|
|
before { RateLimiter.enable }
|
2018-03-01 12:41:36 +08:00
|
|
|
|
|
2023-06-16 10:44:35 +08:00
|
|
|
|
use_redis_snapshotting
|
2018-03-01 12:41:36 +08:00
|
|
|
|
|
2022-10-11 00:21:51 +08:00
|
|
|
|
context "when per day" do
|
|
|
|
|
before { SiteSetting.max_topic_invitations_per_day = 1 }
|
|
|
|
|
|
|
|
|
|
it "rate limits topic invitations" do
|
|
|
|
|
start = Time.now.tomorrow.beginning_of_day
|
|
|
|
|
freeze_time(start)
|
2018-03-01 12:41:36 +08:00
|
|
|
|
|
2022-10-11 00:21:51 +08:00
|
|
|
|
topic = Fabricate(:topic, user: trust_level_2)
|
2018-03-01 12:41:36 +08:00
|
|
|
|
|
2022-10-11 00:21:51 +08:00
|
|
|
|
topic.invite(topic.user, user.username)
|
|
|
|
|
|
|
|
|
|
expect { topic.invite(topic.user, user1.username) }.to raise_error(
|
|
|
|
|
RateLimiter::LimitExceeded,
|
|
|
|
|
)
|
|
|
|
|
end
|
2018-03-01 12:41:36 +08:00
|
|
|
|
|
2022-10-11 00:21:51 +08:00
|
|
|
|
it "rate limits PM invitations" do
|
|
|
|
|
start = Time.now.tomorrow.beginning_of_day
|
|
|
|
|
freeze_time(start)
|
|
|
|
|
|
|
|
|
|
topic = Fabricate(:private_message_topic, user: trust_level_2)
|
|
|
|
|
|
|
|
|
|
topic.invite(topic.user, user.username)
|
|
|
|
|
|
|
|
|
|
expect { topic.invite(topic.user, user1.username) }.to raise_error(
|
|
|
|
|
RateLimiter::LimitExceeded,
|
|
|
|
|
)
|
|
|
|
|
end
|
2018-03-13 01:46:32 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-10-11 00:21:51 +08:00
|
|
|
|
context "when per minute" do
|
|
|
|
|
before { SiteSetting.max_topic_invitations_per_minute = 1 }
|
|
|
|
|
|
|
|
|
|
it "rate limits topic invitations" do
|
|
|
|
|
start = Time.now.tomorrow.beginning_of_minute
|
|
|
|
|
freeze_time(start)
|
2018-03-13 01:46:32 +08:00
|
|
|
|
|
2022-10-11 00:21:51 +08:00
|
|
|
|
topic = Fabricate(:topic, user: trust_level_2)
|
2018-03-13 01:46:32 +08:00
|
|
|
|
|
2022-10-11 00:21:51 +08:00
|
|
|
|
topic.invite(topic.user, user.username)
|
|
|
|
|
|
|
|
|
|
expect { topic.invite(topic.user, user1.username) }.to raise_error(
|
|
|
|
|
RateLimiter::LimitExceeded,
|
|
|
|
|
)
|
|
|
|
|
end
|
2018-03-13 01:46:32 +08:00
|
|
|
|
|
2022-10-11 00:21:51 +08:00
|
|
|
|
it "rate limits PM invitations" do
|
|
|
|
|
start = Time.now.tomorrow.beginning_of_minute
|
|
|
|
|
freeze_time(start)
|
|
|
|
|
|
|
|
|
|
topic = Fabricate(:private_message_topic, user: trust_level_2)
|
|
|
|
|
|
|
|
|
|
topic.invite(topic.user, user.username)
|
|
|
|
|
|
|
|
|
|
expect { topic.invite(topic.user, user1.username) }.to raise_error(
|
|
|
|
|
RateLimiter::LimitExceeded,
|
|
|
|
|
)
|
|
|
|
|
end
|
2018-03-01 13:12:13 +08:00
|
|
|
|
end
|
2018-03-01 12:41:36 +08:00
|
|
|
|
end
|
|
|
|
|
|
2018-02-26 15:18:34 +08:00
|
|
|
|
describe "when username_or_email is not valid" do
|
|
|
|
|
it "should return the right value" do
|
|
|
|
|
expect do expect(topic.invite(user, "somerandomstring")).to eq(nil) end.to_not change {
|
|
|
|
|
topic.allowed_users
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when user is already allowed" do
|
|
|
|
|
it "should raise the right error" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
topic.allowed_users << user1
|
2018-02-26 15:18:34 +08:00
|
|
|
|
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect { topic.invite(user, user1.username) }.to raise_error(Topic::UserExists)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "private message" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
fab!(:user) { trust_level_2 }
|
|
|
|
|
fab!(:topic) { Fabricate(:private_message_topic, user: trust_level_2) }
|
2018-02-26 15:18:34 +08:00
|
|
|
|
|
|
|
|
|
describe "by username" do
|
|
|
|
|
it "should be able to invite a user" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect(topic.invite(user, user1.username)).to eq(true)
|
|
|
|
|
expect(topic.allowed_users).to include(user1)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
expect(Post.last.action_code).to eq("invited_user")
|
|
|
|
|
|
|
|
|
|
notification = Notification.last
|
|
|
|
|
|
|
|
|
|
expect(notification.notification_type).to eq(
|
|
|
|
|
Notification.types[:invited_to_private_message],
|
|
|
|
|
)
|
|
|
|
|
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect(topic.remove_allowed_user(user, user1.username)).to eq(true)
|
|
|
|
|
expect(topic.reload.allowed_users).to_not include(user1)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
expect(Post.last.action_code).to eq("removed_user")
|
|
|
|
|
end
|
2018-03-01 03:15:01 +08:00
|
|
|
|
|
2020-02-27 20:45:20 +08:00
|
|
|
|
it "should not create a small action if user is already invited through a group" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
group = Fabricate(:group, users: [user, user1])
|
2020-02-27 20:45:20 +08:00
|
|
|
|
expect(topic.invite_group(user, group)).to eq(true)
|
|
|
|
|
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect { topic.invite(user, user1.username) }.to change { Notification.count }.by(
|
2023-01-09 19:18:21 +08:00
|
|
|
|
1,
|
2022-07-19 22:03:03 +08:00
|
|
|
|
).and not_change { Post.where(post_type: Post.types[:small_action]).count }
|
2020-02-27 20:45:20 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when from a muted user" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
before { Fabricate(:muted_user, user: user1, muted_user: user) }
|
2018-03-01 03:15:01 +08:00
|
|
|
|
|
2021-10-28 23:33:00 +08:00
|
|
|
|
it "fails with an error" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect { topic.invite(user, user1.username) }.to raise_error(Topic::NotAllowed)
|
|
|
|
|
expect(topic.allowed_users).to_not include(user1)
|
2021-10-28 23:33:00 +08:00
|
|
|
|
expect(Post.last).to be_blank
|
|
|
|
|
expect(Notification.last).to be_blank
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when from a ignored user" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
before { Fabricate(:ignored_user, user: user1, ignored_user: user) }
|
2021-10-28 23:33:00 +08:00
|
|
|
|
|
|
|
|
|
it "fails with an error" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect { topic.invite(user, user1.username) }.to raise_error(Topic::NotAllowed)
|
|
|
|
|
expect(topic.allowed_users).to_not include(user1)
|
2018-03-01 03:15:01 +08:00
|
|
|
|
expect(Post.last).to be_blank
|
|
|
|
|
expect(Notification.last).to be_blank
|
|
|
|
|
end
|
|
|
|
|
end
|
2018-03-08 04:04:17 +08:00
|
|
|
|
|
|
|
|
|
context "when PMs are enabled for TL3 or higher only" do
|
|
|
|
|
before do
|
2022-09-26 11:58:40 +08:00
|
|
|
|
SiteSetting.personal_message_enabled_groups = Group::AUTO_GROUPS[:trust_level_4]
|
2018-03-08 04:04:17 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should raise error" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect { topic.invite(user, user1.username) }.to raise_error(Topic::UserExists)
|
2018-03-08 04:04:17 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2020-07-31 23:52:19 +08:00
|
|
|
|
|
|
|
|
|
context "when invited_user has enabled allow_list" do
|
|
|
|
|
fab!(:pm) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:private_message_topic,
|
|
|
|
|
user: user,
|
|
|
|
|
topic_allowed_users: [
|
|
|
|
|
Fabricate.build(:topic_allowed_user, user: user),
|
|
|
|
|
Fabricate.build(:topic_allowed_user, user: user2),
|
|
|
|
|
],
|
2021-12-21 02:59:10 +08:00
|
|
|
|
)
|
2021-02-26 01:26:49 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-12-21 02:59:10 +08:00
|
|
|
|
before { user1.user_option.update!(enable_allowed_pm_users: true) }
|
2023-01-09 19:18:21 +08:00
|
|
|
|
|
2020-07-31 23:52:19 +08:00
|
|
|
|
it "succeeds when inviter is in allowed list" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
AllowedPmUser.create!(user: user1, allowed_pm_user: user)
|
|
|
|
|
expect(topic.invite(user, user1.username)).to eq(true)
|
2020-07-31 23:52:19 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should raise error when inviter not in allowed list" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
AllowedPmUser.create!(user: user1, allowed_pm_user: user2)
|
|
|
|
|
expect { topic.invite(user, user1.username) }.to raise_error(
|
2020-07-31 23:52:19 +08:00
|
|
|
|
Topic::NotAllowed,
|
2020-11-06 22:58:10 +08:00
|
|
|
|
).with_message(I18n.t("topic_invite.receiver_does_not_allow_pm"))
|
2020-07-31 23:52:19 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should succeed for staff even when not allowed" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
AllowedPmUser.create!(user: user1, allowed_pm_user: user2)
|
|
|
|
|
expect(topic.invite(user1, admin.username)).to eq(true)
|
2020-07-31 23:52:19 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should raise error when target_user is not in inviters allowed list" do
|
|
|
|
|
user.user_option.update!(enable_allowed_pm_users: true)
|
2021-12-21 02:59:10 +08:00
|
|
|
|
AllowedPmUser.create!(user: user1, allowed_pm_user: user)
|
|
|
|
|
expect { topic.invite(user, user1.username) }.to raise_error(
|
2020-07-31 23:52:19 +08:00
|
|
|
|
Topic::NotAllowed,
|
2020-11-06 22:58:10 +08:00
|
|
|
|
).with_message(I18n.t("topic_invite.sender_does_not_allow_pm"))
|
2020-07-31 23:52:19 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-02-26 01:26:49 +08:00
|
|
|
|
it "succeeds when inviter is in allowed list even though other participants are not in allowed list" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
AllowedPmUser.create!(user: user1, allowed_pm_user: user)
|
|
|
|
|
expect(pm.invite(user, user1.username)).to eq(true)
|
2020-07-31 23:52:19 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2018-02-26 15:18:34 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "by email" do
|
|
|
|
|
it "should be able to invite a user" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect(topic.invite(user, user1.email)).to eq(true)
|
|
|
|
|
expect(topic.allowed_users).to include(user1)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
|
|
|
|
|
expect(Notification.last.notification_type).to eq(
|
|
|
|
|
Notification.types[:invited_to_private_message],
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when user is not found" do
|
|
|
|
|
it "should create the right invite" do
|
|
|
|
|
expect(topic.invite(user, "test@email.com")).to eq(true)
|
|
|
|
|
|
|
|
|
|
invite = Invite.last
|
|
|
|
|
|
|
|
|
|
expect(invite.email).to eq("test@email.com")
|
|
|
|
|
expect(invite.invited_by).to eq(user)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when user does not have sufficient trust level" do
|
2023-12-18 12:07:36 +08:00
|
|
|
|
before { user.change_trust_level!(TrustLevel[1]) }
|
2018-02-26 15:18:34 +08:00
|
|
|
|
|
|
|
|
|
it "should not create an invite" do
|
|
|
|
|
expect do expect(topic.invite(user, "test@email.com")).to eq(nil) end.to_not change {
|
|
|
|
|
Invite.count
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "public topic" do
|
2018-12-05 23:43:07 +08:00
|
|
|
|
def expect_the_right_notification_to_be_created(inviter, invitee)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
notification = Notification.last
|
|
|
|
|
|
|
|
|
|
expect(notification.notification_type).to eq(Notification.types[:invited_to_topic])
|
|
|
|
|
|
2018-12-05 23:43:07 +08:00
|
|
|
|
expect(notification.user).to eq(invitee)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
expect(notification.topic).to eq(topic)
|
|
|
|
|
|
|
|
|
|
notification_data = JSON.parse(notification.data)
|
|
|
|
|
|
|
|
|
|
expect(notification_data["topic_title"]).to eq(topic.title)
|
2018-12-05 23:43:07 +08:00
|
|
|
|
expect(notification_data["display_username"]).to eq(inviter.username)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "by username" do
|
|
|
|
|
it "should invite user into a topic" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
topic.invite(user, user1.username)
|
|
|
|
|
expect_the_right_notification_to_be_created(user, user1)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "by email" do
|
|
|
|
|
it "should be able to invite a user" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect(topic.invite(user, user1.email)).to eq(true)
|
|
|
|
|
expect_the_right_notification_to_be_created(user, user1)
|
2018-12-05 23:43:07 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when topic belongs to a private category" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
fab!(:category) do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
Fabricate(:category_with_definition, groups: [group]).tap do |category|
|
2018-12-05 23:43:07 +08:00
|
|
|
|
category.set_permissions(group => :full)
|
|
|
|
|
category.save!
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
2021-12-21 02:59:10 +08:00
|
|
|
|
fab!(:inviter) { Fabricate(:user).tap { |user| group.add_owner(user) } }
|
2019-05-07 23:05:53 +08:00
|
|
|
|
fab!(:invitee) { Fabricate(:user) }
|
2018-12-05 23:43:07 +08:00
|
|
|
|
|
|
|
|
|
describe "as a group owner" do
|
|
|
|
|
it "should be able to invite a user" do
|
|
|
|
|
expect do
|
|
|
|
|
expect(topic.invite(inviter, invitee.email, [group.id])).to eq(true)
|
|
|
|
|
end.to change { Notification.count } & change { GroupHistory.count }
|
|
|
|
|
|
|
|
|
|
expect_the_right_notification_to_be_created(inviter, invitee)
|
|
|
|
|
|
|
|
|
|
group_history = GroupHistory.last
|
|
|
|
|
|
|
|
|
|
expect(group_history.acting_user).to eq(inviter)
|
|
|
|
|
expect(group_history.target_user).to eq(invitee)
|
|
|
|
|
|
|
|
|
|
expect(group_history.action).to eq(GroupHistory.actions[:add_user_to_group])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when group ids are not given" do
|
|
|
|
|
it "should not invite the user" do
|
|
|
|
|
expect do
|
|
|
|
|
expect(topic.invite(inviter, invitee.email)).to eq(false)
|
|
|
|
|
end.to_not change { Notification.count }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "as a normal user" do
|
|
|
|
|
it "should not be able to invite a user" do
|
|
|
|
|
expect do
|
|
|
|
|
expect(topic.invite(Fabricate(:user), invitee.email, [group.id])).to eq(false)
|
|
|
|
|
end.to_not change { Notification.count }
|
|
|
|
|
end
|
|
|
|
|
end
|
2018-02-26 15:18:34 +08:00
|
|
|
|
end
|
|
|
|
|
|
2018-03-01 03:15:01 +08:00
|
|
|
|
context "for a muted topic" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
before do
|
|
|
|
|
TopicUser.change(
|
|
|
|
|
user1.id,
|
|
|
|
|
topic.id,
|
|
|
|
|
notification_level: TopicUser.notification_levels[:muted],
|
|
|
|
|
)
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2018-03-01 03:15:01 +08:00
|
|
|
|
|
2020-07-31 23:52:19 +08:00
|
|
|
|
it "fails with an error message" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect { topic.invite(user, user1.username) }.to raise_error(Topic::NotAllowed)
|
|
|
|
|
expect(topic.allowed_users).to_not include(user1)
|
2018-03-01 03:15:01 +08:00
|
|
|
|
expect(Post.last).to be_blank
|
|
|
|
|
expect(Notification.last).to be_blank
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2018-02-26 15:18:34 +08:00
|
|
|
|
describe "when user can invite via email" do
|
2023-12-18 12:07:36 +08:00
|
|
|
|
before { user.change_trust_level!(TrustLevel[2]) }
|
2018-02-26 15:18:34 +08:00
|
|
|
|
|
|
|
|
|
it "should create an invite" do
|
2021-03-16 23:08:54 +08:00
|
|
|
|
Jobs.run_immediately!
|
2018-02-26 15:18:34 +08:00
|
|
|
|
expect(topic.invite(user, "test@email.com")).to eq(true)
|
|
|
|
|
|
|
|
|
|
invite = Invite.last
|
|
|
|
|
|
|
|
|
|
expect(invite.email).to eq("test@email.com")
|
|
|
|
|
expect(invite.invited_by).to eq(user)
|
2021-03-16 23:08:54 +08:00
|
|
|
|
expect(ActionMailer::Base.deliveries.last.body).to include(topic.title)
|
2018-02-26 15:18:34 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "private message" do
|
2024-01-25 14:28:26 +08:00
|
|
|
|
fab!(:pm_user) { Fabricate(:user, refresh_auto_groups: true) }
|
2021-12-21 02:59:10 +08:00
|
|
|
|
fab!(:topic) do
|
2021-03-02 22:46:50 +08:00
|
|
|
|
PostCreator
|
|
|
|
|
.new(
|
2022-10-05 08:50:20 +08:00
|
|
|
|
pm_user,
|
2021-03-02 22:46:50 +08:00
|
|
|
|
title: "This is a private message",
|
|
|
|
|
raw: "This is my message to you-ou-ou",
|
|
|
|
|
archetype: Archetype.private_message,
|
|
|
|
|
target_usernames: coding_horror.username,
|
|
|
|
|
)
|
|
|
|
|
.create!
|
|
|
|
|
.topic
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
2013-04-22 11:48:05 +08:00
|
|
|
|
it "should integrate correctly" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(Guardian.new(topic.user).can_see?(topic)).to eq(true)
|
|
|
|
|
expect(Guardian.new.can_see?(topic)).to eq(false)
|
|
|
|
|
expect(Guardian.new(evil_trout).can_see?(topic)).to eq(false)
|
|
|
|
|
expect(Guardian.new(coding_horror).can_see?(topic)).to eq(true)
|
|
|
|
|
expect(TopicQuery.new(evil_trout).list_latest.topics).not_to include(topic)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with invite" do
|
|
|
|
|
context "with existing user" do
|
|
|
|
|
context "when using group name" do
|
2016-06-20 14:29:11 +08:00
|
|
|
|
it "can add admin to allowed groups" do
|
|
|
|
|
admins = Group[:admins]
|
2017-08-29 00:32:08 +08:00
|
|
|
|
admins.update!(messageable_level: Group::ALIAS_LEVELS[:everyone])
|
2016-06-20 14:29:11 +08:00
|
|
|
|
|
|
|
|
|
expect(topic.invite_group(topic.user, admins)).to eq(true)
|
|
|
|
|
expect(topic.allowed_groups.include?(admins)).to eq(true)
|
|
|
|
|
expect(topic.remove_allowed_group(topic.user, "admins")).to eq(true)
|
|
|
|
|
expect(topic.allowed_groups.include?(admins)).to eq(false)
|
|
|
|
|
end
|
|
|
|
|
|
2019-08-16 15:22:18 +08:00
|
|
|
|
def set_state!(group, user, state)
|
|
|
|
|
group
|
|
|
|
|
.group_users
|
|
|
|
|
.find_by(user_id: user.id)
|
|
|
|
|
.update!(notification_level: NotificationLevels.all[state])
|
|
|
|
|
end
|
|
|
|
|
|
2017-06-14 13:53:49 +08:00
|
|
|
|
it "creates a notification for each user in the group" do
|
2019-08-16 15:22:18 +08:00
|
|
|
|
# trigger notification
|
|
|
|
|
user_watching_first = Fabricate(:user)
|
|
|
|
|
user_watching = Fabricate(:user)
|
|
|
|
|
|
|
|
|
|
# trigger rollup
|
|
|
|
|
user_tracking = Fabricate(:user)
|
|
|
|
|
|
|
|
|
|
# trigger nothing
|
|
|
|
|
user_normal = Fabricate(:user)
|
|
|
|
|
user_muted = Fabricate(:user)
|
|
|
|
|
|
2017-06-14 13:53:49 +08:00
|
|
|
|
Fabricate(:post, topic: topic)
|
|
|
|
|
|
2019-08-16 15:22:18 +08:00
|
|
|
|
group.add(topic.user) # no notification even though watching
|
|
|
|
|
group.add(user_watching_first)
|
|
|
|
|
group.add(user_watching)
|
|
|
|
|
group.add(user_normal)
|
|
|
|
|
group.add(user_muted)
|
|
|
|
|
group.add(user_tracking)
|
2017-06-14 13:53:49 +08:00
|
|
|
|
|
2019-08-16 15:22:18 +08:00
|
|
|
|
set_state!(group, topic.user, :watching)
|
|
|
|
|
set_state!(group, user_watching, :watching)
|
|
|
|
|
set_state!(group, user_watching_first, :watching_first_post)
|
|
|
|
|
set_state!(group, user_tracking, :tracking)
|
|
|
|
|
set_state!(group, user_normal, :regular)
|
|
|
|
|
set_state!(group, user_muted, :muted)
|
|
|
|
|
|
|
|
|
|
Notification.delete_all
|
2021-04-15 00:30:51 +08:00
|
|
|
|
Jobs.run_immediately!
|
2019-08-16 15:22:18 +08:00
|
|
|
|
topic.invite_group(topic.user, group)
|
2017-06-14 13:53:49 +08:00
|
|
|
|
|
2019-08-16 15:22:18 +08:00
|
|
|
|
expect(Notification.count).to eq(3)
|
2017-06-14 13:53:49 +08:00
|
|
|
|
|
2019-08-16 15:22:18 +08:00
|
|
|
|
[user_watching, user_watching_first].each do |u|
|
|
|
|
|
notifications = Notification.where(user_id: u.id).to_a
|
|
|
|
|
expect(notifications.length).to eq(1)
|
2017-06-14 13:53:49 +08:00
|
|
|
|
|
2019-08-16 15:22:18 +08:00
|
|
|
|
notification = notifications.first
|
|
|
|
|
|
|
|
|
|
expect(notification.topic).to eq(topic)
|
|
|
|
|
expect(notification.notification_type).to eq(
|
|
|
|
|
Notification.types[:invited_to_private_message],
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
notifications = Notification.where(user_id: user_tracking.id).to_a
|
|
|
|
|
expect(notifications.length).to eq(1)
|
|
|
|
|
notification = notifications.first
|
2017-06-21 14:31:15 +08:00
|
|
|
|
|
2019-08-16 15:22:18 +08:00
|
|
|
|
expect(notification.notification_type).to eq(Notification.types[:group_message_summary])
|
2017-06-14 13:53:49 +08:00
|
|
|
|
end
|
2021-10-22 06:57:51 +08:00
|
|
|
|
|
|
|
|
|
it "removes users in topic_allowed_users who are part of the added group" do
|
|
|
|
|
admins = Group[:admins]
|
|
|
|
|
admins.update!(messageable_level: Group::ALIAS_LEVELS[:everyone])
|
|
|
|
|
|
|
|
|
|
# clear up the state so we can be more explicit with the test
|
|
|
|
|
TopicAllowedUser.where(topic: topic).delete_all
|
|
|
|
|
user0 = topic.user
|
|
|
|
|
user3 = Fabricate(:user)
|
|
|
|
|
Fabricate(:topic_allowed_user, topic: topic, user: user0)
|
|
|
|
|
Fabricate(:topic_allowed_user, topic: topic, user: user1)
|
|
|
|
|
Fabricate(:topic_allowed_user, topic: topic, user: user2)
|
|
|
|
|
Fabricate(:topic_allowed_user, topic: topic, user: user3)
|
|
|
|
|
|
|
|
|
|
admins.add(user1)
|
|
|
|
|
admins.add(user2)
|
|
|
|
|
|
|
|
|
|
other_topic = Fabricate(:topic)
|
|
|
|
|
Fabricate(:topic_allowed_user, user: user1, topic: other_topic)
|
|
|
|
|
|
|
|
|
|
expect(topic.invite_group(topic.user, admins)).to eq(true)
|
|
|
|
|
expect(topic.posts.last.action_code).to eq("removed_user")
|
2022-08-29 18:01:16 +08:00
|
|
|
|
expect(topic.allowed_users).to match_array([user0, user3])
|
2021-10-22 06:57:51 +08:00
|
|
|
|
expect(other_topic.allowed_users).to match_array([user1])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not remove the OP from topic_allowed_users if they are part of an added group" do
|
|
|
|
|
admins = Group[:admins]
|
|
|
|
|
admins.update!(messageable_level: Group::ALIAS_LEVELS[:everyone])
|
|
|
|
|
|
|
|
|
|
# clear up the state so we can be more explicit with the test
|
|
|
|
|
TopicAllowedUser.where(topic: topic).delete_all
|
|
|
|
|
user0 = topic.user
|
|
|
|
|
Fabricate(:topic_allowed_user, topic: topic, user: user0)
|
|
|
|
|
Fabricate(:topic_allowed_user, topic: topic, user: user1)
|
|
|
|
|
|
|
|
|
|
admins.add(topic.user)
|
|
|
|
|
admins.add(user1)
|
|
|
|
|
|
|
|
|
|
expect(topic.invite_group(topic.user, admins)).to eq(true)
|
2022-08-29 18:01:16 +08:00
|
|
|
|
expect(topic.allowed_users).to match_array([topic.user])
|
2021-10-22 06:57:51 +08:00
|
|
|
|
end
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with user actions" do
|
2013-04-22 11:48:05 +08:00
|
|
|
|
it "should set up actions correctly" do
|
2019-01-04 01:03:01 +08:00
|
|
|
|
UserActionManager.enable
|
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
post = create_post(archetype: "private_message", target_usernames: [user.username])
|
2019-01-04 01:03:01 +08:00
|
|
|
|
actions = post.user.user_actions
|
2013-05-14 09:59:55 +08:00
|
|
|
|
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(actions.map { |a| a.action_type }).not_to include(UserAction::NEW_TOPIC)
|
|
|
|
|
expect(actions.map { |a| a.action_type }).to include(UserAction::NEW_PRIVATE_MESSAGE)
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(user.user_actions.map { |a| a.action_type }).to include(
|
|
|
|
|
UserAction::GOT_PRIVATE_MESSAGE,
|
|
|
|
|
)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "bumping topics" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
let!(:topic) { Fabricate(:topic, bumped_at: 1.year.ago) }
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
|
|
it "updates the bumped_at field when a new post is made" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic.bumped_at).to be_present
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect {
|
2019-05-07 23:05:53 +08:00
|
|
|
|
create_post(topic: topic, user: topic.user)
|
|
|
|
|
topic.reload
|
|
|
|
|
}.to change(topic, :bumped_at)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when editing posts" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
@earlier_post = Fabricate(:post, topic: topic, user: topic.user)
|
|
|
|
|
@last_post = Fabricate(:post, topic: topic, user: topic.user)
|
|
|
|
|
topic.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "doesn't bump the topic on an edit to the last post that doesn't result in a new version" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect {
|
2018-03-07 13:44:21 +08:00
|
|
|
|
SiteSetting.editing_grace_period = 5.minutes
|
|
|
|
|
@last_post.revise(
|
|
|
|
|
@last_post.user,
|
|
|
|
|
{ raw: @last_post.raw + "a" },
|
|
|
|
|
revised_at: @last_post.created_at + 10.seconds,
|
|
|
|
|
)
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.reload
|
|
|
|
|
}.not_to change(topic, :bumped_at)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "bumps the topic when a new version is made of the last post" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect {
|
2021-12-16 01:41:14 +08:00
|
|
|
|
@last_post.revise(moderator, raw: "updated contents")
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.reload
|
|
|
|
|
}.to change(topic, :bumped_at)
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
|
|
it "doesn't bump the topic when a post that isn't the last post receives a new version" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect {
|
2021-12-16 01:41:14 +08:00
|
|
|
|
@earlier_post.revise(moderator, raw: "updated contents")
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.reload
|
|
|
|
|
}.not_to change(topic, :bumped_at)
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2016-04-25 16:33:38 +08:00
|
|
|
|
|
|
|
|
|
it "doesn't bump the topic when a post have invalid topic title while edit" do
|
|
|
|
|
expect {
|
2021-12-16 01:41:14 +08:00
|
|
|
|
@last_post.revise(moderator, title: "invalid title")
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.reload
|
|
|
|
|
}.not_to change(topic, :bumped_at)
|
2016-04-25 16:33:38 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "moderator posts" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
|
|
it "creates a moderator post" do
|
2016-06-20 15:47:29 +08:00
|
|
|
|
mod_post =
|
|
|
|
|
topic.add_moderator_post(
|
|
|
|
|
moderator,
|
|
|
|
|
"Moderator did something. http://discourse.org",
|
|
|
|
|
post_number: 999,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(mod_post).to be_present
|
|
|
|
|
expect(mod_post.post_type).to eq(Post.types[:moderator_action])
|
|
|
|
|
expect(mod_post.post_number).to eq(999)
|
|
|
|
|
expect(mod_post.sort_order).to eq(999)
|
|
|
|
|
expect(topic.topic_links.count).to eq(1)
|
|
|
|
|
expect(topic.reload.moderator_posts_count).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when moderator post fails to be created" do
|
2017-11-14 02:41:36 +08:00
|
|
|
|
before { user.update_column(:silenced_till, 1.year.from_now) }
|
2016-06-20 15:47:29 +08:00
|
|
|
|
|
|
|
|
|
it "should not increment moderator_posts_count" do
|
|
|
|
|
expect(topic.moderator_posts_count).to eq(0)
|
|
|
|
|
|
|
|
|
|
topic.add_moderator_post(user, "winter is never coming")
|
|
|
|
|
|
|
|
|
|
expect(topic.moderator_posts_count).to eq(0)
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "update_status" do
|
2022-02-11 09:00:58 +08:00
|
|
|
|
fab!(:post) { Fabricate(:post).tap { |p| p.topic.update!(bumped_at: 1.hour.ago) } }
|
|
|
|
|
|
|
|
|
|
fab!(:topic) { post.topic }
|
2019-05-07 23:05:53 +08:00
|
|
|
|
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2020-03-11 05:13:17 +08:00
|
|
|
|
@original_bumped_at = topic.bumped_at
|
2019-05-07 23:05:53 +08:00
|
|
|
|
@user = topic.user
|
2013-03-29 14:38:54 +08:00
|
|
|
|
@user.admin = true
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with visibility" do
|
2021-02-16 23:45:12 +08:00
|
|
|
|
let(:category) { Fabricate(:category_with_definition) }
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when disabled" do
|
2020-01-31 00:00:49 +08:00
|
|
|
|
it "should not be visible and have correct counts" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.update_status("visible", false, @user)
|
|
|
|
|
topic.reload
|
|
|
|
|
expect(topic).not_to be_visible
|
|
|
|
|
expect(topic.moderator_posts_count).to eq(1)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.bumped_at).to eq_time(@original_bumped_at)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
2020-01-31 00:00:49 +08:00
|
|
|
|
|
2021-02-16 23:45:12 +08:00
|
|
|
|
it "decreases topic_count of topic category" do
|
|
|
|
|
topic.update!(category: category)
|
|
|
|
|
Category.update_stats
|
|
|
|
|
|
2022-02-11 09:00:58 +08:00
|
|
|
|
expect do 2.times { topic.update_status("visible", false, @user) } end.to change {
|
|
|
|
|
category.reload.topic_count
|
|
|
|
|
}.by(-1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "decreases topic_count of user stat" do
|
|
|
|
|
expect do 2.times { topic.update_status("visible", false, @user) } end.to change {
|
|
|
|
|
post.user.user_stat.reload.topic_count
|
|
|
|
|
}.from(1).to(0)
|
2021-02-16 23:45:12 +08:00
|
|
|
|
end
|
|
|
|
|
|
2020-01-31 00:00:49 +08:00
|
|
|
|
it "removes itself as featured topic on user profiles" do
|
|
|
|
|
user.user_profile.update(featured_topic_id: topic.id)
|
|
|
|
|
expect(user.user_profile.featured_topic).to eq(topic)
|
|
|
|
|
|
|
|
|
|
topic.update_status("visible", false, @user)
|
|
|
|
|
expect(user.user_profile.reload.featured_topic).to eq(nil)
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when enabled" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2022-02-11 09:00:58 +08:00
|
|
|
|
topic.update_status("visible", false, @user)
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-04-22 11:48:05 +08:00
|
|
|
|
it "should be visible with correct counts" do
|
2022-02-11 09:00:58 +08:00
|
|
|
|
topic.update_status("visible", true, @user)
|
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic).to be_visible
|
2022-02-11 09:00:58 +08:00
|
|
|
|
expect(topic.moderator_posts_count).to eq(2)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.bumped_at).to eq_time(@original_bumped_at)
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2021-02-16 23:45:12 +08:00
|
|
|
|
|
|
|
|
|
it "increases topic_count of topic category" do
|
2022-02-11 09:00:58 +08:00
|
|
|
|
topic.update!(category: category)
|
|
|
|
|
|
|
|
|
|
expect do 2.times { topic.update_status("visible", true, @user) } end.to change {
|
|
|
|
|
category.reload.topic_count
|
|
|
|
|
}.by(1)
|
|
|
|
|
end
|
2021-02-16 23:45:12 +08:00
|
|
|
|
|
2022-02-11 09:00:58 +08:00
|
|
|
|
it "increases topic_count of user stat" do
|
|
|
|
|
expect do 2.times { topic.update_status("visible", true, @user) } end.to change {
|
|
|
|
|
post.user.user_stat.reload.topic_count
|
|
|
|
|
}.from(0).to(1)
|
2021-02-16 23:45:12 +08:00
|
|
|
|
end
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with pinned" do
|
|
|
|
|
context "when disabled" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.update_status("pinned", false, @user)
|
|
|
|
|
topic.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-04-22 11:48:05 +08:00
|
|
|
|
it "doesn't have a pinned_at but has correct dates" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic.pinned_at).to be_blank
|
|
|
|
|
expect(topic.moderator_posts_count).to eq(1)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.bumped_at).to eq_time(@original_bumped_at)
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when enabled" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.update_attribute :pinned_at, nil
|
|
|
|
|
topic.update_status("pinned", true, @user)
|
|
|
|
|
topic.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-04-22 11:48:05 +08:00
|
|
|
|
it "should enable correctly" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic.pinned_at).to be_present
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.bumped_at).to eq_time(@original_bumped_at)
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic.moderator_posts_count).to eq(1)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with archived" do
|
2020-07-22 02:29:02 +08:00
|
|
|
|
it "should create a staff action log entry" do
|
|
|
|
|
expect { topic.update_status("archived", true, @user) }.to change {
|
|
|
|
|
UserHistory.where(action: UserHistory.actions[:topic_archived]).count
|
|
|
|
|
}.by(1)
|
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when disabled" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2017-04-08 05:10:01 +08:00
|
|
|
|
@archived_topic = Fabricate(:topic, archived: true, bumped_at: 1.hour.ago)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
@original_bumped_at = @archived_topic.bumped_at
|
2017-04-08 05:10:01 +08:00
|
|
|
|
@archived_topic.update_status("archived", false, @user)
|
|
|
|
|
@archived_topic.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-04-22 11:48:05 +08:00
|
|
|
|
it "should archive correctly" do
|
2017-04-08 05:10:01 +08:00
|
|
|
|
expect(@archived_topic).not_to be_archived
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(@archived_topic.bumped_at).to eq_time(@original_bumped_at)
|
2017-04-08 05:10:01 +08:00
|
|
|
|
expect(@archived_topic.moderator_posts_count).to eq(1)
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when enabled" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.update_attribute :archived, false
|
|
|
|
|
topic.update_status("archived", true, @user)
|
|
|
|
|
topic.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should be archived" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic).to be_archived
|
|
|
|
|
expect(topic.moderator_posts_count).to eq(1)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.bumped_at).to eq_time(@original_bumped_at)
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-05-08 02:25:41 +08:00
|
|
|
|
shared_examples_for "a status that closes a topic" do
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when disabled" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2017-04-08 05:10:01 +08:00
|
|
|
|
@closed_topic = Fabricate(:topic, closed: true, bumped_at: 1.hour.ago)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
@original_bumped_at = @closed_topic.bumped_at
|
2017-04-08 05:10:01 +08:00
|
|
|
|
@closed_topic.update_status(status, false, @user)
|
|
|
|
|
@closed_topic.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should not be pinned" do
|
2017-04-08 05:10:01 +08:00
|
|
|
|
expect(@closed_topic).not_to be_closed
|
|
|
|
|
expect(@closed_topic.moderator_posts_count).to eq(1)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(@closed_topic.bumped_at).not_to eq_time(@original_bumped_at)
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when enabled" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.update_attribute :closed, false
|
|
|
|
|
topic.update_status(status, true, @user)
|
|
|
|
|
topic.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should be closed" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic).to be_closed
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.bumped_at).to eq_time(@original_bumped_at)
|
2019-05-07 23:05:53 +08:00
|
|
|
|
expect(topic.moderator_posts_count).to eq(1)
|
|
|
|
|
expect(topic.topic_timers.first).to eq(nil)
|
2013-02-26 00:42:20 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when closed" do
|
2013-05-08 02:25:41 +08:00
|
|
|
|
let(:status) { "closed" }
|
2020-03-28 00:35:40 +08:00
|
|
|
|
it_behaves_like "a status that closes a topic"
|
2020-02-27 13:39:37 +08:00
|
|
|
|
|
|
|
|
|
it "should archive group message" do
|
|
|
|
|
group.add(@user)
|
|
|
|
|
topic = Fabricate(:private_message_topic, allowed_groups: [group])
|
|
|
|
|
|
|
|
|
|
expect { topic.update_status(status, true, @user) }.to change(
|
|
|
|
|
topic.group_archived_messages,
|
|
|
|
|
:count,
|
|
|
|
|
).by(1)
|
|
|
|
|
end
|
2020-07-22 02:29:02 +08:00
|
|
|
|
|
|
|
|
|
it "should create a staff action log entry" do
|
|
|
|
|
expect { topic.update_status(status, true, @user) }.to change {
|
|
|
|
|
UserHistory.where(action: UserHistory.actions[:topic_closed]).count
|
|
|
|
|
}.by(1)
|
|
|
|
|
end
|
2013-05-08 02:25:41 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when autoclosed" do
|
2013-05-08 02:25:41 +08:00
|
|
|
|
let(:status) { "autoclosed" }
|
2020-03-28 00:35:40 +08:00
|
|
|
|
it_behaves_like "a status that closes a topic"
|
2013-05-28 08:59:43 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when topic was set to close when it was created" do
|
2017-11-14 02:41:36 +08:00
|
|
|
|
it "includes the autoclose duration in the moderator post" do
|
2017-02-15 23:58:18 +08:00
|
|
|
|
freeze_time(Time.new(2000, 1, 1))
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.created_at = 3.days.ago
|
|
|
|
|
topic.update_status(status, true, @user)
|
|
|
|
|
expect(topic.posts.last.raw).to include "closed after 3 days"
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-05-28 08:59:43 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "when topic was set to close after it was created" do
|
2017-11-14 02:41:36 +08:00
|
|
|
|
it "includes the autoclose duration in the moderator post" do
|
2017-02-15 23:58:18 +08:00
|
|
|
|
freeze_time(Time.new(2000, 1, 1))
|
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.created_at = 7.days.ago
|
2017-02-15 23:58:18 +08:00
|
|
|
|
|
|
|
|
|
freeze_time(2.days.ago)
|
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], 48)
|
|
|
|
|
topic.save!
|
2017-02-15 23:58:18 +08:00
|
|
|
|
|
|
|
|
|
freeze_time(2.days.from_now)
|
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
topic.update_status(status, true, @user)
|
|
|
|
|
expect(topic.posts.last.raw).to include "closed after 2 days"
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
2013-05-28 08:59:43 +08:00
|
|
|
|
end
|
2013-05-08 02:25:41 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2014-06-19 02:04:10 +08:00
|
|
|
|
describe "banner" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2019-05-07 23:05:53 +08:00
|
|
|
|
fab!(:user) { topic.user }
|
2014-06-19 02:04:10 +08:00
|
|
|
|
let(:banner) { { html: "<p>BANNER</p>", url: topic.url, key: topic.id } }
|
2014-06-17 01:21:21 +08:00
|
|
|
|
|
2014-06-19 02:04:10 +08:00
|
|
|
|
before { topic.stubs(:banner).returns(banner) }
|
2014-06-17 01:21:21 +08:00
|
|
|
|
|
2014-06-19 02:04:10 +08:00
|
|
|
|
describe "make_banner!" do
|
|
|
|
|
it "changes the topic archetype to 'banner'" do
|
2015-05-04 10:21:00 +08:00
|
|
|
|
messages =
|
|
|
|
|
MessageBus.track_publish do
|
2014-11-14 15:10:52 +08:00
|
|
|
|
topic.make_banner!(user)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.archetype).to eq(Archetype.banner)
|
2014-11-14 15:10:52 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
channels = messages.map(&:channel)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(channels).to include("/site/banner")
|
|
|
|
|
expect(channels).to include("/distributed_hash")
|
2014-06-19 02:04:10 +08:00
|
|
|
|
end
|
2014-06-17 01:21:21 +08:00
|
|
|
|
|
2014-06-19 02:04:10 +08:00
|
|
|
|
it "ensures only one banner topic at all time" do
|
2014-11-14 15:10:52 +08:00
|
|
|
|
_banner_topic = Fabricate(:banner_topic)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(Topic.where(archetype: Archetype.banner).count).to eq(1)
|
2014-06-17 01:21:21 +08:00
|
|
|
|
|
2014-06-19 02:04:10 +08:00
|
|
|
|
topic.make_banner!(user)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(Topic.where(archetype: Archetype.banner).count).to eq(1)
|
2014-06-19 02:04:10 +08:00
|
|
|
|
end
|
2014-06-17 01:21:21 +08:00
|
|
|
|
|
2017-02-04 04:07:38 +08:00
|
|
|
|
it "removes any dismissed banner keys" do
|
|
|
|
|
user.user_profile.update_column(:dismissed_banner_key, topic.id)
|
|
|
|
|
|
|
|
|
|
topic.make_banner!(user)
|
|
|
|
|
user.user_profile.reload
|
|
|
|
|
expect(user.user_profile.dismissed_banner_key).to be_nil
|
|
|
|
|
end
|
2014-06-17 01:21:21 +08:00
|
|
|
|
end
|
|
|
|
|
|
2014-06-19 02:04:10 +08:00
|
|
|
|
describe "remove_banner!" do
|
|
|
|
|
it "resets the topic archetype" do
|
|
|
|
|
topic.expects(:add_moderator_post)
|
2018-10-15 11:15:31 +08:00
|
|
|
|
|
|
|
|
|
message = MessageBus.track_publish { topic.remove_banner!(user) }.first
|
|
|
|
|
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.archetype).to eq(Archetype.default)
|
2018-10-15 11:15:31 +08:00
|
|
|
|
expect(message.channel).to eq("/site/banner")
|
|
|
|
|
expect(message.data).to eq(nil)
|
2014-06-19 02:04:10 +08:00
|
|
|
|
end
|
2014-06-17 01:21:21 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with bannered_until date" do
|
2021-06-24 17:35:36 +08:00
|
|
|
|
it "sets bannered_until to be caught by ensure_consistency" do
|
|
|
|
|
bannered_until = 5.days.from_now
|
|
|
|
|
topic.make_banner!(user, bannered_until.to_s)
|
|
|
|
|
|
|
|
|
|
freeze_time 6.days.from_now do
|
|
|
|
|
expect(topic.archetype).to eq(Archetype.banner)
|
|
|
|
|
|
|
|
|
|
Topic.ensure_consistency!
|
|
|
|
|
topic.reload
|
|
|
|
|
|
|
|
|
|
expect(topic.archetype).to eq(Archetype.default)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2014-06-17 01:21:21 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with last_poster info" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
before do
|
2013-07-22 13:06:53 +08:00
|
|
|
|
@post = create_post
|
|
|
|
|
@user = @post.user
|
2013-02-06 03:16:51 +08:00
|
|
|
|
@topic = @post.topic
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "initially has the last_post_user_id of the OP" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(@topic.last_post_user_id).to eq(@user.id)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "after a second post" do
|
|
|
|
|
before do
|
2021-12-16 01:41:14 +08:00
|
|
|
|
@second_user = coding_horror
|
2013-07-22 13:06:53 +08:00
|
|
|
|
@new_post = create_post(topic: @topic, user: @second_user)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
@topic.reload
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "updates the last_post_user_id to the second_user" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(@topic.last_post_user_id).to eq(@second_user.id)
|
|
|
|
|
expect(@topic.last_posted_at.to_i).to eq(@new_post.created_at.to_i)
|
2014-05-06 21:41:59 +08:00
|
|
|
|
topic_user = @second_user.topic_users.find_by(topic_id: @topic.id)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic_user.posted?).to eq(true)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
2013-04-22 11:48:05 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "with category" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
before { @category = Fabricate(:category_with_definition) }
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
|
|
it "should not increase the topic_count with no category" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect {
|
|
|
|
|
Fabricate(:topic, user: @category.user)
|
|
|
|
|
@category.reload
|
|
|
|
|
}.not_to change(@category, :topic_count)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should increase the category's topic_count" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect {
|
|
|
|
|
Fabricate(:topic, user: @category.user, category_id: @category.id)
|
|
|
|
|
@category.reload
|
|
|
|
|
}.to change(@category, :topic_count).by(1)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "after create" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
|
|
it "is a regular topic by default" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.archetype).to eq(Archetype.default)
|
|
|
|
|
expect(topic.has_summary).to eq(false)
|
|
|
|
|
expect(topic).to be_visible
|
|
|
|
|
expect(topic.pinned_at).to be_blank
|
|
|
|
|
expect(topic).not_to be_closed
|
|
|
|
|
expect(topic).not_to be_archived
|
|
|
|
|
expect(topic.moderator_posts_count).to eq(0)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with post" do
|
2013-02-06 03:16:51 +08:00
|
|
|
|
let(:post) { Fabricate(:post, topic: topic, user: topic.user) }
|
|
|
|
|
|
|
|
|
|
it "has the same archetype as the topic" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(post.archetype).to eq(topic.archetype)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-26 00:42:20 +08:00
|
|
|
|
|
2017-05-31 16:40:21 +08:00
|
|
|
|
describe "#change_category_to_id" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2019-05-07 23:05:53 +08:00
|
|
|
|
fab!(:user) { topic.user }
|
2019-08-06 18:26:54 +08:00
|
|
|
|
fab!(:category) { Fabricate(:category_with_definition, user: user) }
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
|
|
|
|
describe "without a previous category" do
|
2017-05-31 16:40:21 +08:00
|
|
|
|
it "changes the category" do
|
|
|
|
|
topic.change_category_to_id(category.id)
|
|
|
|
|
category.reload
|
|
|
|
|
expect(topic.category).to eq(category)
|
|
|
|
|
expect(category.topic_count).to eq(1)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-05-31 16:40:21 +08:00
|
|
|
|
it "should not change the topic_count when not changed" do
|
|
|
|
|
expect {
|
|
|
|
|
topic.change_category_to_id(topic.category.id)
|
|
|
|
|
category.reload
|
|
|
|
|
}.not_to change(category, :topic_count)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "doesn't change the category when it can't be found" do
|
2017-05-31 16:40:21 +08:00
|
|
|
|
topic.change_category_to_id(12_312_312)
|
|
|
|
|
expect(topic.category_id).to eq(SiteSetting.uncategorized_category_id)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
2018-08-14 22:06:52 +08:00
|
|
|
|
|
|
|
|
|
it "changes the category even when the topic title is invalid" do
|
|
|
|
|
SiteSetting.min_topic_title_length = 5
|
|
|
|
|
topic.update_column(:title, "xyz")
|
|
|
|
|
expect { topic.change_category_to_id(category.id) }.to change { topic.category_id }.to(
|
|
|
|
|
category.id,
|
|
|
|
|
)
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "with a previous category" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
before_all do
|
2017-05-31 16:40:21 +08:00
|
|
|
|
topic.change_category_to_id(category.id)
|
|
|
|
|
topic.reload
|
|
|
|
|
category.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "doesn't change the topic_count when the value doesn't change" do
|
2018-05-07 21:29:06 +08:00
|
|
|
|
expect(category.topic_count).to eq(1)
|
2017-05-31 16:40:21 +08:00
|
|
|
|
expect {
|
|
|
|
|
topic.change_category_to_id(category.id)
|
|
|
|
|
category.reload
|
|
|
|
|
}.not_to change(category, :topic_count)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-05-31 16:40:21 +08:00
|
|
|
|
it "doesn't reset the category when an id that doesn't exist" do
|
|
|
|
|
topic.change_category_to_id(55_556)
|
|
|
|
|
expect(topic.category_id).to eq(category.id)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "to a different category" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
fab!(:new_category) do
|
|
|
|
|
Fabricate(:category_with_definition, user: user, name: "2nd category")
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
|
2017-05-31 16:40:21 +08:00
|
|
|
|
it "should work" do
|
|
|
|
|
topic.change_category_to_id(new_category.id)
|
|
|
|
|
|
|
|
|
|
expect(topic.reload.category).to eq(new_category)
|
|
|
|
|
expect(new_category.reload.topic_count).to eq(1)
|
|
|
|
|
expect(category.reload.topic_count).to eq(0)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2018-05-17 16:09:21 +08:00
|
|
|
|
describe "user that is watching the new category" do
|
2019-04-05 21:06:38 +08:00
|
|
|
|
before do
|
2019-03-14 22:47:38 +08:00
|
|
|
|
Jobs.run_immediately!
|
2018-05-31 15:53:49 +08:00
|
|
|
|
|
2018-05-07 21:29:06 +08:00
|
|
|
|
topic.posts << Fabricate(:post)
|
|
|
|
|
|
|
|
|
|
CategoryUser.set_notification_level_for_category(
|
|
|
|
|
user,
|
|
|
|
|
CategoryUser.notification_levels[:watching],
|
|
|
|
|
new_category.id,
|
|
|
|
|
)
|
|
|
|
|
|
2018-05-17 16:09:21 +08:00
|
|
|
|
CategoryUser.set_notification_level_for_category(
|
2021-12-21 02:59:10 +08:00
|
|
|
|
user1,
|
2018-05-17 16:09:21 +08:00
|
|
|
|
CategoryUser.notification_levels[:watching_first_post],
|
|
|
|
|
new_category.id,
|
|
|
|
|
)
|
2019-04-05 21:06:38 +08:00
|
|
|
|
end
|
2018-05-07 21:29:06 +08:00
|
|
|
|
|
2019-04-05 21:06:38 +08:00
|
|
|
|
it "should generate the notification for the topic" do
|
2018-05-17 16:09:21 +08:00
|
|
|
|
expect do topic.change_category_to_id(new_category.id) end.to change {
|
|
|
|
|
Notification.count
|
|
|
|
|
}.by(2)
|
2023-01-09 19:18:21 +08:00
|
|
|
|
|
2020-01-29 08:03:47 +08:00
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
|
|
|
|
user_id: user.id,
|
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
post_number: 1,
|
|
|
|
|
notification_type: Notification.types[:posted],
|
|
|
|
|
).exists?,
|
|
|
|
|
).to eq(true)
|
2023-01-09 19:18:21 +08:00
|
|
|
|
|
2020-01-29 08:03:47 +08:00
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
2021-12-21 02:59:10 +08:00
|
|
|
|
user_id: user1.id,
|
2020-01-29 08:03:47 +08:00
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
post_number: 1,
|
|
|
|
|
notification_type: Notification.types[:watching_first_post],
|
|
|
|
|
).exists?,
|
|
|
|
|
).to eq(true)
|
|
|
|
|
end
|
|
|
|
|
|
2021-10-18 21:04:01 +08:00
|
|
|
|
it "should not generate a notification if SiteSetting.disable_category_edit_notifications is enabled" do
|
|
|
|
|
SiteSetting.disable_category_edit_notifications = true
|
|
|
|
|
|
|
|
|
|
expect do topic.change_category_to_id(new_category.id) end.not_to change {
|
2022-07-19 22:03:03 +08:00
|
|
|
|
Notification.count
|
|
|
|
|
}
|
2021-10-18 21:04:01 +08:00
|
|
|
|
|
|
|
|
|
expect(topic.category_id).to eq(new_category.id)
|
|
|
|
|
end
|
|
|
|
|
|
2020-01-29 08:03:47 +08:00
|
|
|
|
it "should generate the modified notification for the topic if already seen" do
|
2021-07-05 14:17:31 +08:00
|
|
|
|
TopicUser.create!(
|
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
last_read_post_number: topic.posts.first.post_number,
|
2018-05-17 16:09:21 +08:00
|
|
|
|
user_id: user.id,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect do topic.change_category_to_id(new_category.id) end.to change {
|
2022-07-19 22:03:03 +08:00
|
|
|
|
Notification.count
|
2023-01-09 19:18:21 +08:00
|
|
|
|
}.by(2)
|
|
|
|
|
|
2018-05-17 16:09:21 +08:00
|
|
|
|
expect(
|
|
|
|
|
Notification.where(
|
2021-12-21 02:59:10 +08:00
|
|
|
|
user_id: user.id,
|
2018-05-17 16:09:21 +08:00
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
post_number: 1,
|
2020-01-21 05:41:13 +08:00
|
|
|
|
notification_type: Notification.types[:edited],
|
2023-01-09 19:18:21 +08:00
|
|
|
|
).exists?,
|
2017-08-22 11:54:11 +08:00
|
|
|
|
).to eq(true)
|
2023-01-09 19:18:21 +08:00
|
|
|
|
|
|
|
|
|
expect(
|
2018-05-17 16:09:21 +08:00
|
|
|
|
Notification.where(
|
|
|
|
|
user_id: user1.id,
|
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
post_number: 1,
|
|
|
|
|
notification_type: Notification.types[:watching_first_post],
|
|
|
|
|
).exists?,
|
|
|
|
|
).to eq(true)
|
2018-05-07 21:29:06 +08:00
|
|
|
|
end
|
2019-04-05 21:06:38 +08:00
|
|
|
|
|
|
|
|
|
it "should not generate a notification for unlisted topic" do
|
|
|
|
|
topic.update_column(:visible, false)
|
|
|
|
|
|
|
|
|
|
expect do topic.change_category_to_id(new_category.id) end.not_to change {
|
2022-07-19 22:03:03 +08:00
|
|
|
|
Notification.count
|
|
|
|
|
}
|
2019-04-05 21:06:38 +08:00
|
|
|
|
end
|
2018-05-07 21:29:06 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-05-31 16:40:21 +08:00
|
|
|
|
describe "when new category is set to auto close by default" do
|
|
|
|
|
before do
|
2021-02-17 17:04:25 +08:00
|
|
|
|
freeze_time
|
2017-05-31 16:40:21 +08:00
|
|
|
|
new_category.update!(auto_close_hours: 5)
|
2020-08-26 12:08:46 +08:00
|
|
|
|
topic.user.update!(admin: true)
|
2017-05-31 16:40:21 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should set a topic timer" do
|
2021-02-17 13:00:06 +08:00
|
|
|
|
now = Time.zone.now
|
2020-03-11 05:13:17 +08:00
|
|
|
|
|
2017-05-31 16:40:21 +08:00
|
|
|
|
expect { topic.change_category_to_id(new_category.id) }.to change {
|
|
|
|
|
TopicTimer.count
|
|
|
|
|
}.by(1)
|
|
|
|
|
|
|
|
|
|
expect(topic.reload.category).to eq(new_category)
|
|
|
|
|
|
|
|
|
|
topic_timer = TopicTimer.last
|
|
|
|
|
|
2020-08-26 12:08:46 +08:00
|
|
|
|
expect(topic_timer.user).to eq(Discourse.system_user)
|
2017-05-31 16:40:21 +08:00
|
|
|
|
expect(topic_timer.topic).to eq(topic)
|
2021-02-17 17:52:49 +08:00
|
|
|
|
expect(topic_timer.execute_at).to be_within_one_minute_of(now + 5.hours)
|
2017-05-31 16:40:21 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-08-22 11:54:11 +08:00
|
|
|
|
describe "when topic is already closed" do
|
|
|
|
|
before { topic.update_status("closed", true, Discourse.system_user) }
|
|
|
|
|
|
|
|
|
|
it "should not set a topic timer" do
|
|
|
|
|
expect { topic.change_category_to_id(new_category.id) }.not_to change {
|
2022-07-19 22:03:03 +08:00
|
|
|
|
TopicTimer.with_deleted.count
|
|
|
|
|
}
|
2017-08-22 11:54:11 +08:00
|
|
|
|
|
|
|
|
|
expect(topic.closed).to eq(true)
|
|
|
|
|
expect(topic.reload.category).to eq(new_category)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2017-05-31 16:40:21 +08:00
|
|
|
|
describe "when topic has an existing topic timer" do
|
|
|
|
|
let(:topic_timer) { Fabricate(:topic_timer, topic: topic) }
|
|
|
|
|
|
|
|
|
|
it "should not inherit category's auto close hours" do
|
|
|
|
|
topic_timer
|
|
|
|
|
topic.change_category_to_id(new_category.id)
|
|
|
|
|
|
|
|
|
|
expect(topic.reload.category).to eq(new_category)
|
|
|
|
|
expect(topic.public_topic_timer).to eq(topic_timer)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.public_topic_timer.execute_at).to eq_time(topic_timer.execute_at)
|
2017-05-31 16:40:21 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
2023-07-12 05:24:13 +08:00
|
|
|
|
|
|
|
|
|
describe "when the topic title is not valid" do
|
|
|
|
|
fab!(:topic_title) { topic.title }
|
|
|
|
|
fab!(:topic_slug) { topic.slug }
|
|
|
|
|
fab!(:topic_2) { Fabricate(:topic) }
|
|
|
|
|
|
|
|
|
|
it "does not save title or slug when title repeats letters" do
|
|
|
|
|
topic.title = "a" * 50
|
|
|
|
|
topic.change_category_to_id(new_category.id)
|
|
|
|
|
|
|
|
|
|
expect(topic.reload.title).to eq(topic_title)
|
|
|
|
|
expect(topic.reload.slug).to eq(topic_slug)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not save title or slug when title is too long" do
|
|
|
|
|
SiteSetting.max_topic_title_length = 200
|
|
|
|
|
|
|
|
|
|
topic.title = "Neque porro quisquam est qui dolorem ipsum quia dolor amet" * 100
|
|
|
|
|
topic.change_category_to_id(new_category.id)
|
|
|
|
|
|
|
|
|
|
expect(topic.reload.title).to eq(topic_title)
|
|
|
|
|
expect(topic.reload.slug).to eq(topic_slug)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not save title when it is too short" do
|
|
|
|
|
SiteSetting.min_topic_title_length = 15
|
|
|
|
|
topic.title = "Hello world"
|
|
|
|
|
expect { topic.change_category_to_id(new_category.id) }.not_to change {
|
|
|
|
|
topic.reload.title
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not save title when it is a duplicate" do
|
|
|
|
|
topic_2.title = topic_title
|
|
|
|
|
expect { topic_2.change_category_to_id(new_category.id) }.not_to change {
|
|
|
|
|
topic_2.reload.title
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not save title when it is blank" do
|
|
|
|
|
topic.title = ""
|
|
|
|
|
expect { topic.change_category_to_id(new_category.id) }.not_to change {
|
|
|
|
|
topic.reload.title
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not save title when there are too many emojis" do
|
|
|
|
|
SiteSetting.max_emojis_in_title = 2
|
|
|
|
|
|
|
|
|
|
topic.title = "Dummy topic title " + "😀" * 5
|
|
|
|
|
expect { topic.change_category_to_id(new_category.id) }.not_to change {
|
|
|
|
|
topic.reload.title
|
|
|
|
|
}
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-10-09 02:40:31 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when allow_uncategorized_topics is false" do
|
2017-07-07 14:09:14 +08:00
|
|
|
|
before { SiteSetting.allow_uncategorized_topics = false }
|
2013-02-26 00:42:20 +08:00
|
|
|
|
|
2019-08-06 18:26:54 +08:00
|
|
|
|
let!(:topic) { Fabricate(:topic, category: Fabricate(:category_with_definition)) }
|
2013-10-09 02:40:31 +08:00
|
|
|
|
|
|
|
|
|
it "returns false" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.change_category_to_id(nil)).to eq(false) # don't use "== false" here because it would also match nil
|
2013-10-09 02:40:31 +08:00
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when the category exists" do
|
|
|
|
|
before do
|
2017-05-31 16:40:21 +08:00
|
|
|
|
topic.change_category_to_id(nil)
|
|
|
|
|
category.reload
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-02-26 00:42:20 +08:00
|
|
|
|
it "resets the category" do
|
2017-05-31 16:40:21 +08:00
|
|
|
|
expect(topic.category_id).to eq(SiteSetting.uncategorized_category_id)
|
|
|
|
|
expect(category.topic_count).to eq(0)
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2013-02-28 11:36:12 +08:00
|
|
|
|
describe "scopes" do
|
|
|
|
|
describe "#by_most_recently_created" do
|
|
|
|
|
it "returns topics ordered by created_at desc, id desc" do
|
|
|
|
|
now = Time.now
|
2019-05-07 23:05:53 +08:00
|
|
|
|
a = Fabricate(:topic, user: user, created_at: now - 2.minutes)
|
|
|
|
|
b = Fabricate(:topic, user: user, created_at: now)
|
|
|
|
|
c = Fabricate(:topic, user: user, created_at: now)
|
|
|
|
|
d = Fabricate(:topic, user: user, created_at: now - 2.minutes)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(Topic.by_newest).to eq([c, b, d, a])
|
2013-02-28 11:36:12 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-05-30 19:23:40 +08:00
|
|
|
|
|
|
|
|
|
describe "#created_since" do
|
|
|
|
|
it "returns topics created after some date" do
|
|
|
|
|
now = Time.now
|
2019-05-07 23:05:53 +08:00
|
|
|
|
a = Fabricate(:topic, user: user, created_at: now - 2.minutes)
|
|
|
|
|
b = Fabricate(:topic, user: user, created_at: now - 1.minute)
|
|
|
|
|
c = Fabricate(:topic, user: user, created_at: now)
|
|
|
|
|
d = Fabricate(:topic, user: user, created_at: now + 1.minute)
|
|
|
|
|
e = Fabricate(:topic, user: user, created_at: now + 2.minutes)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(Topic.created_since(now)).not_to include a
|
|
|
|
|
expect(Topic.created_since(now)).not_to include b
|
|
|
|
|
expect(Topic.created_since(now)).not_to include c
|
|
|
|
|
expect(Topic.created_since(now)).to include d
|
|
|
|
|
expect(Topic.created_since(now)).to include e
|
2013-05-30 19:23:40 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "#visible" do
|
|
|
|
|
it "returns topics set as visible" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
a = Fabricate(:topic, user: user, visible: false)
|
|
|
|
|
b = Fabricate(:topic, user: user, visible: true)
|
|
|
|
|
c = Fabricate(:topic, user: user, visible: true)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(Topic.visible).not_to include a
|
|
|
|
|
expect(Topic.visible).to include b
|
|
|
|
|
expect(Topic.visible).to include c
|
2013-05-30 19:23:40 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2018-08-10 08:50:05 +08:00
|
|
|
|
|
|
|
|
|
describe "#in_category_and_subcategories" do
|
|
|
|
|
it "returns topics in a category and its subcategories" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
c1 = Fabricate(:category_with_definition)
|
|
|
|
|
c2 = Fabricate(:category_with_definition, parent_category_id: c1.id)
|
|
|
|
|
c3 = Fabricate(:category_with_definition)
|
2018-08-10 08:50:05 +08:00
|
|
|
|
|
2019-05-07 23:05:53 +08:00
|
|
|
|
t1 = Fabricate(:topic, user: user, category_id: c1.id)
|
|
|
|
|
t2 = Fabricate(:topic, user: user, category_id: c2.id)
|
|
|
|
|
t3 = Fabricate(:topic, user: user, category_id: c3.id)
|
2018-08-10 08:50:05 +08:00
|
|
|
|
|
|
|
|
|
expect(Topic.in_category_and_subcategories(c1.id)).not_to include(t3)
|
|
|
|
|
expect(Topic.in_category_and_subcategories(c1.id)).to include(t2)
|
|
|
|
|
expect(Topic.in_category_and_subcategories(c1.id)).to include(t1)
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-28 11:36:12 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-05-12 06:23:18 +08:00
|
|
|
|
describe "#set_or_create_timer" do
|
2017-03-22 11:12:02 +08:00
|
|
|
|
let(:topic) { Fabricate.build(:topic) }
|
2013-11-11 07:52:44 +08:00
|
|
|
|
|
2017-07-24 14:56:08 +08:00
|
|
|
|
let(:closing_topic) { Fabricate(:topic_timer, execute_at: 5.hours.from_now).topic }
|
2013-05-08 02:25:41 +08:00
|
|
|
|
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:trust_level_4)
|
2013-06-07 05:04:10 +08:00
|
|
|
|
|
2013-11-27 08:06:20 +08:00
|
|
|
|
it "can take a number of hours as an integer" do
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time now
|
|
|
|
|
|
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], 72, by_user: admin)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.topic_timers.first.execute_at).to eq_time(3.days.from_now)
|
2015-05-28 00:22:34 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-11-27 08:06:20 +08:00
|
|
|
|
it "can take a number of hours as a string" do
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time now
|
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], "18", by_user: admin)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.topic_timers.first.execute_at).to eq_time(18.hours.from_now)
|
2015-05-28 00:22:34 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-04-04 01:03:11 +08:00
|
|
|
|
it "can take a number of hours as a string and can handle based on last post" do
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time now
|
2021-02-05 08:12:56 +08:00
|
|
|
|
topic.set_or_create_timer(
|
|
|
|
|
TopicTimer.types[:close],
|
|
|
|
|
nil,
|
|
|
|
|
by_user: admin,
|
|
|
|
|
based_on_last_post: true,
|
|
|
|
|
duration_minutes: "1080",
|
|
|
|
|
)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.topic_timers.first.execute_at).to eq_time(18.hours.from_now)
|
2017-04-04 01:03:11 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-11-27 08:06:20 +08:00
|
|
|
|
it "can take a timestamp for a future time" do
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time now
|
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], "2013-11-22 5:00", by_user: admin)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.topic_timers.first.execute_at).to eq_time(Time.zone.local(2013, 11, 22, 5, 0))
|
2013-11-27 08:06:20 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "sets a validation error when given a timestamp in the past" do
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time now
|
2017-03-22 11:12:02 +08:00
|
|
|
|
|
2020-12-09 01:13:45 +08:00
|
|
|
|
expect do
|
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], "2013-11-19 5:00", by_user: admin)
|
|
|
|
|
end.to raise_error(Discourse::InvalidParameters)
|
2013-11-27 08:06:20 +08:00
|
|
|
|
end
|
|
|
|
|
|
2018-03-26 11:32:52 +08:00
|
|
|
|
it "sets a validation error when give a timestamp of an invalid format" do
|
|
|
|
|
freeze_time now
|
|
|
|
|
|
|
|
|
|
expect do
|
|
|
|
|
topic.set_or_create_timer(
|
|
|
|
|
TopicTimer.types[:close],
|
|
|
|
|
"۲۰۱۸-۰۳-۲۶ ۱۸:۰۰+۰۸:۰۰",
|
|
|
|
|
by_user: admin,
|
|
|
|
|
)
|
|
|
|
|
end.to raise_error(Discourse::InvalidParameters)
|
|
|
|
|
end
|
|
|
|
|
|
2013-11-27 08:06:20 +08:00
|
|
|
|
it "can take a timestamp with timezone" do
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time now
|
|
|
|
|
topic.set_or_create_timer(
|
|
|
|
|
TopicTimer.types[:close],
|
|
|
|
|
"2013-11-25T01:35:00-08:00",
|
|
|
|
|
by_user: admin,
|
|
|
|
|
)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.topic_timers.first.execute_at).to eq_time(Time.utc(2013, 11, 25, 9, 35))
|
2013-11-27 08:06:20 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "sets topic status update user to given user if it is a staff or TL4 user" do
|
2017-05-12 06:23:18 +08:00
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], 3, by_user: admin)
|
|
|
|
|
expect(topic.topic_timers.first.user).to eq(admin)
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "sets topic status update user to given user if it is a TL4 user" do
|
2017-05-12 06:23:18 +08:00
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], 3, by_user: trust_level_4)
|
|
|
|
|
expect(topic.topic_timers.first.user).to eq(trust_level_4)
|
2014-11-27 02:51:07 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "sets topic status update user to system user if given user is not staff or a TL4 user" do
|
2017-05-12 06:23:18 +08:00
|
|
|
|
topic.set_or_create_timer(
|
|
|
|
|
TopicTimer.types[:close],
|
|
|
|
|
3,
|
|
|
|
|
by_user: Fabricate.build(:user, id: 444),
|
|
|
|
|
)
|
2017-08-22 14:22:48 +08:00
|
|
|
|
expect(topic.topic_timers.first.user).to eq(Discourse.system_user)
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "sets topic status update user to system user if user is not given and topic creator is not staff nor TL4 user" do
|
2017-05-12 06:23:18 +08:00
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], 3)
|
2017-08-22 14:22:48 +08:00
|
|
|
|
expect(topic.topic_timers.first.user).to eq(Discourse.system_user)
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "sets topic status update user to topic creator if it is a staff user" do
|
2013-06-07 05:04:10 +08:00
|
|
|
|
staff_topic = Fabricate.build(:topic, user: Fabricate.build(:admin, id: 999))
|
2017-05-12 06:23:18 +08:00
|
|
|
|
staff_topic.set_or_create_timer(TopicTimer.types[:close], 3)
|
|
|
|
|
expect(staff_topic.topic_timers.first.user_id).to eq(999)
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "sets topic status update user to topic creator if it is a TL4 user" do
|
2014-11-27 02:51:07 +08:00
|
|
|
|
tl4_topic = Fabricate.build(:topic, user: Fabricate.build(:trust_level_4, id: 998))
|
2017-05-12 06:23:18 +08:00
|
|
|
|
tl4_topic.set_or_create_timer(TopicTimer.types[:close], 3)
|
|
|
|
|
expect(tl4_topic.topic_timers.first.user_id).to eq(998)
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "removes close topic status update if arg is nil" do
|
2017-05-12 06:23:18 +08:00
|
|
|
|
closing_topic.set_or_create_timer(TopicTimer.types[:close], nil)
|
2017-03-22 11:12:02 +08:00
|
|
|
|
closing_topic.reload
|
2017-05-12 06:23:18 +08:00
|
|
|
|
expect(closing_topic.topic_timers.first).to be_nil
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "updates topic status update execute_at if it was already set to close" do
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time now
|
|
|
|
|
closing_topic.set_or_create_timer(TopicTimer.types[:close], 48)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(closing_topic.reload.public_topic_timer.execute_at).to eq_time(2.days.from_now)
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-08-22 14:22:48 +08:00
|
|
|
|
it "should not delete topic_timer of another status_type" do
|
|
|
|
|
freeze_time
|
|
|
|
|
closing_topic.set_or_create_timer(TopicTimer.types[:open], nil)
|
|
|
|
|
topic_timer = closing_topic.public_topic_timer
|
|
|
|
|
|
2019-03-28 14:28:01 +08:00
|
|
|
|
expect(topic_timer.execute_at).to eq_time(5.hours.from_now)
|
2017-08-22 14:22:48 +08:00
|
|
|
|
expect(topic_timer.status_type).to eq(TopicTimer.types[:close])
|
|
|
|
|
end
|
|
|
|
|
|
2017-06-21 14:31:15 +08:00
|
|
|
|
it "should allow status_type to be updated" do
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time
|
2017-06-21 14:31:15 +08:00
|
|
|
|
|
2017-07-24 21:17:42 +08:00
|
|
|
|
topic_timer =
|
|
|
|
|
closing_topic.set_or_create_timer(
|
|
|
|
|
TopicTimer.types[:publish_to_category],
|
|
|
|
|
72,
|
|
|
|
|
by_user: admin,
|
|
|
|
|
)
|
|
|
|
|
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic_timer.execute_at).to eq_time(3.days.from_now)
|
2017-06-21 14:31:15 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-03-22 11:12:02 +08:00
|
|
|
|
it "does not update topic's topic status created_at it was already set to close" do
|
2017-05-12 06:23:18 +08:00
|
|
|
|
expect { closing_topic.set_or_create_timer(TopicTimer.types[:close], 14) }.to_not change {
|
|
|
|
|
closing_topic.topic_timers.first.created_at
|
|
|
|
|
}
|
2017-03-22 11:12:02 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when category's default auto close is set" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
let(:category) { Fabricate(:category_with_definition, auto_close_hours: 4) }
|
2017-03-22 11:12:02 +08:00
|
|
|
|
let(:topic) { Fabricate(:topic, category: category) }
|
|
|
|
|
|
|
|
|
|
it "should be able to override category's default auto close" do
|
2020-03-28 03:39:25 +08:00
|
|
|
|
freeze_time
|
2019-03-14 22:47:38 +08:00
|
|
|
|
Jobs.run_immediately!
|
2018-05-31 15:53:49 +08:00
|
|
|
|
|
2021-02-17 05:51:39 +08:00
|
|
|
|
expect(topic.topic_timers.first.execute_at).to be_within_one_second_of(
|
|
|
|
|
topic.created_at + 4.hours,
|
|
|
|
|
)
|
2017-03-22 11:12:02 +08:00
|
|
|
|
|
2017-05-12 06:23:18 +08:00
|
|
|
|
topic.set_or_create_timer(TopicTimer.types[:close], 2, by_user: admin)
|
2017-03-22 11:12:02 +08:00
|
|
|
|
|
|
|
|
|
expect(topic.reload.closed).to eq(false)
|
|
|
|
|
|
2017-07-24 21:17:42 +08:00
|
|
|
|
freeze_time 3.hours.from_now
|
|
|
|
|
|
2021-01-19 11:30:58 +08:00
|
|
|
|
Jobs::TopicTimerEnqueuer.new.execute
|
2017-07-24 21:17:42 +08:00
|
|
|
|
expect(topic.reload.closed).to eq(true)
|
2017-03-22 11:12:02 +08:00
|
|
|
|
end
|
2013-06-07 05:04:10 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2019-04-05 09:00:48 +08:00
|
|
|
|
describe ".for_digest" do
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with no edit grace period" do
|
2017-08-15 00:47:33 +08:00
|
|
|
|
before { SiteSetting.editing_grace_period = 0 }
|
2013-11-07 04:05:06 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "returns none when there are no topics" do
|
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
|
|
|
|
end
|
2013-11-07 04:05:06 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "doesn't return category topics" do
|
2020-03-11 05:13:17 +08:00
|
|
|
|
Fabricate(:category_with_definition, created_at: 1.minute.ago)
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
|
|
|
|
end
|
2013-11-07 04:05:06 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "returns regular topics" do
|
2020-03-11 05:13:17 +08:00
|
|
|
|
topic = Fabricate(:topic, created_at: 1.minute.ago)
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to eq([topic])
|
|
|
|
|
end
|
2014-11-03 23:57:50 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "doesn't return topics from muted categories" do
|
2020-03-11 05:13:17 +08:00
|
|
|
|
category = Fabricate(:category_with_definition, created_at: 2.minutes.ago)
|
|
|
|
|
Fabricate(:topic, category: category, created_at: 1.minute.ago)
|
2014-11-03 23:57:50 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
CategoryUser.set_notification_level_for_category(
|
|
|
|
|
user,
|
|
|
|
|
CategoryUser.notification_levels[:muted],
|
|
|
|
|
category.id,
|
|
|
|
|
)
|
2014-11-03 23:57:50 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
|
|
|
|
end
|
2016-03-26 03:12:00 +08:00
|
|
|
|
|
2019-04-05 09:00:48 +08:00
|
|
|
|
it "doesn't return topics that a user has muted" do
|
2020-03-11 05:13:17 +08:00
|
|
|
|
topic = Fabricate(:topic, created_at: 1.minute.ago)
|
2019-04-05 09:00:48 +08:00
|
|
|
|
|
|
|
|
|
Fabricate(
|
|
|
|
|
:topic_user,
|
|
|
|
|
user: user,
|
|
|
|
|
topic: topic,
|
|
|
|
|
notification_level: TopicUser.notification_levels[:muted],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago)).to eq([])
|
|
|
|
|
end
|
|
|
|
|
|
2021-04-07 05:01:15 +08:00
|
|
|
|
it "does return watched topics from muted categories" do
|
|
|
|
|
category = Fabricate(:category_with_definition, created_at: 2.minutes.ago)
|
|
|
|
|
topic = Fabricate(:topic, category: category, created_at: 1.minute.ago)
|
|
|
|
|
|
|
|
|
|
CategoryUser.set_notification_level_for_category(
|
|
|
|
|
user,
|
|
|
|
|
CategoryUser.notification_levels[:muted],
|
|
|
|
|
category.id,
|
|
|
|
|
)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:topic_user,
|
|
|
|
|
user: user,
|
|
|
|
|
topic: topic,
|
|
|
|
|
notification_level: TopicUser.notification_levels[:regular],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to eq([topic])
|
|
|
|
|
end
|
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "doesn't return topics from suppressed categories" do
|
2020-03-11 05:13:17 +08:00
|
|
|
|
category = Fabricate(:category_with_definition, created_at: 2.minutes.ago)
|
2021-11-03 15:17:09 +08:00
|
|
|
|
topic = Fabricate(:topic, category: category, created_at: 1.minute.ago)
|
2016-03-26 03:12:00 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
SiteSetting.digest_suppress_categories = "#{category.id}"
|
2016-03-26 03:12:00 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
2021-11-03 15:17:09 +08:00
|
|
|
|
|
|
|
|
|
Fabricate(
|
|
|
|
|
:topic_user,
|
|
|
|
|
user: user,
|
|
|
|
|
topic: topic,
|
|
|
|
|
notification_level: TopicUser.notification_levels[:regular],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
2017-08-15 00:47:33 +08:00
|
|
|
|
end
|
2015-01-29 23:40:26 +08:00
|
|
|
|
|
2023-08-19 03:28:20 +08:00
|
|
|
|
it "doesn't return topics from suppressed tags" do
|
|
|
|
|
category = Fabricate(:category_with_definition, created_at: 2.minutes.ago)
|
|
|
|
|
topic = Fabricate(:topic, category: category, created_at: 1.minute.ago)
|
|
|
|
|
topic2 = Fabricate(:topic, category: category, created_at: 1.minute.ago)
|
|
|
|
|
tag = Fabricate(:tag)
|
2023-09-18 15:45:43 +08:00
|
|
|
|
tag2 = Fabricate(:tag)
|
2023-08-19 03:28:20 +08:00
|
|
|
|
Fabricate(:topic_tag, topic: topic, tag: tag)
|
|
|
|
|
|
2023-09-18 15:45:43 +08:00
|
|
|
|
SiteSetting.digest_suppress_tags = "#{tag.name}|#{tag2.name}"
|
2023-08-19 03:28:20 +08:00
|
|
|
|
topics = Topic.for_digest(user, 1.year.ago, top_order: true)
|
|
|
|
|
expect(topics).to eq([topic2])
|
|
|
|
|
|
|
|
|
|
Fabricate(
|
|
|
|
|
:topic_user,
|
|
|
|
|
user: user,
|
|
|
|
|
topic: topic,
|
|
|
|
|
notification_level: TopicUser.notification_levels[:regular],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to eq([topic2])
|
|
|
|
|
end
|
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "doesn't return topics from TL0 users" do
|
|
|
|
|
new_user = Fabricate(:user, trust_level: 0)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
Fabricate(:topic, user: new_user, created_at: 1.minute.ago)
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
|
|
|
|
end
|
2016-12-20 03:53:53 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "returns topics from TL0 users if given include_tl0" do
|
|
|
|
|
new_user = Fabricate(:user, trust_level: 0)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
topic = Fabricate(:topic, user_id: new_user.id, created_at: 1.minute.ago)
|
2016-12-20 03:53:53 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true, include_tl0: true)).to eq(
|
|
|
|
|
[topic],
|
|
|
|
|
)
|
|
|
|
|
end
|
2016-03-18 05:35:23 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "returns topics from TL0 users if enabled in preferences" do
|
|
|
|
|
new_user = Fabricate(:user, trust_level: 0)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
topic = Fabricate(:topic, user: new_user, created_at: 1.minute.ago)
|
2016-03-18 05:35:23 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
u = Fabricate(:user)
|
|
|
|
|
u.user_option.include_tl0_in_digests = true
|
2016-03-18 05:35:23 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(u, 1.year.ago, top_order: true)).to eq([topic])
|
|
|
|
|
end
|
2016-08-09 03:14:18 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "doesn't return topics with only muted tags" do
|
|
|
|
|
tag = Fabricate(:tag)
|
|
|
|
|
TagUser.change(user.id, tag.id, TagUser.notification_levels[:muted])
|
2020-03-11 05:13:17 +08:00
|
|
|
|
Fabricate(:topic, tags: [tag], created_at: 1.minute.ago)
|
2016-08-09 03:14:18 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to be_blank
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns topics with both muted and not muted tags" do
|
|
|
|
|
muted_tag, other_tag = Fabricate(:tag), Fabricate(:tag)
|
|
|
|
|
TagUser.change(user.id, muted_tag.id, TagUser.notification_levels[:muted])
|
2020-03-11 05:13:17 +08:00
|
|
|
|
topic = Fabricate(:topic, tags: [muted_tag, other_tag], created_at: 1.minute.ago)
|
2017-08-15 00:47:33 +08:00
|
|
|
|
|
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to eq([topic])
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns topics with no tags too" do
|
|
|
|
|
muted_tag = Fabricate(:tag)
|
|
|
|
|
TagUser.change(user.id, muted_tag.id, TagUser.notification_levels[:muted])
|
2020-03-11 05:13:17 +08:00
|
|
|
|
_topic1 = Fabricate(:topic, tags: [muted_tag], created_at: 1.minute.ago)
|
|
|
|
|
topic2 =
|
|
|
|
|
Fabricate(:topic, tags: [Fabricate(:tag), Fabricate(:tag)], created_at: 1.minute.ago)
|
|
|
|
|
topic3 = Fabricate(:topic, created_at: 1.minute.ago)
|
2017-08-15 00:47:33 +08:00
|
|
|
|
topics = Topic.for_digest(user, 1.year.ago, top_order: true)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(topics.size).to eq(2)
|
|
|
|
|
expect(topics).to contain_exactly(topic2, topic3)
|
|
|
|
|
end
|
2016-08-09 03:14:18 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
it "sorts by category notification levels" do
|
2020-03-11 05:13:17 +08:00
|
|
|
|
category1, category2 =
|
|
|
|
|
Fabricate(:category_with_definition),
|
|
|
|
|
Fabricate(:category_with_definition, created_at: 2.minutes.ago)
|
|
|
|
|
2.times { |i| Fabricate(:topic, category: category1, created_at: 1.minute.ago) }
|
|
|
|
|
topic1 = Fabricate(:topic, category: category2, created_at: 1.minute.ago)
|
|
|
|
|
2.times { |i| Fabricate(:topic, category: category1, created_at: 1.minute.ago) }
|
2017-08-15 00:47:33 +08:00
|
|
|
|
CategoryUser.create(
|
|
|
|
|
user: user,
|
|
|
|
|
category: category2,
|
|
|
|
|
notification_level: CategoryUser.notification_levels[:watching],
|
|
|
|
|
)
|
|
|
|
|
for_digest = Topic.for_digest(user, 1.year.ago, top_order: true)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(for_digest.first).to eq(topic1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "sorts by topic notification levels" do
|
|
|
|
|
topics = []
|
2020-03-11 05:13:17 +08:00
|
|
|
|
3.times { |i| topics << Fabricate(:topic, created_at: 1.minute.ago) }
|
2017-08-15 00:47:33 +08:00
|
|
|
|
TopicUser.create(
|
|
|
|
|
user_id: user.id,
|
|
|
|
|
topic_id: topics[0].id,
|
|
|
|
|
notification_level: TopicUser.notification_levels[:tracking],
|
|
|
|
|
)
|
|
|
|
|
TopicUser.create(
|
|
|
|
|
user_id: user.id,
|
|
|
|
|
topic_id: topics[2].id,
|
|
|
|
|
notification_level: TopicUser.notification_levels[:watching],
|
|
|
|
|
)
|
|
|
|
|
for_digest = Topic.for_digest(user, 1.year.ago, top_order: true).pluck(:id)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(for_digest).to eq([topics[2].id, topics[0].id, topics[1].id])
|
|
|
|
|
end
|
2016-08-09 03:14:18 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-08-15 00:47:33 +08:00
|
|
|
|
context "with editing_grace_period" do
|
|
|
|
|
before { SiteSetting.editing_grace_period = 5.minutes }
|
|
|
|
|
|
|
|
|
|
it "excludes topics that are within the grace period" do
|
|
|
|
|
topic1 = Fabricate(:topic, created_at: 6.minutes.ago)
|
2018-06-05 15:29:17 +08:00
|
|
|
|
_topic2 = Fabricate(:topic, created_at: 4.minutes.ago)
|
2017-08-15 00:47:33 +08:00
|
|
|
|
expect(Topic.for_digest(user, 1.year.ago, top_order: true)).to eq([topic1])
|
|
|
|
|
end
|
2016-08-19 05:16:52 +08:00
|
|
|
|
end
|
2013-11-07 04:05:06 +08:00
|
|
|
|
end
|
|
|
|
|
|
2019-04-05 09:00:48 +08:00
|
|
|
|
describe ".secured" do
|
|
|
|
|
it "should return the right topics" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
category = Fabricate(:category_with_definition, read_restricted: true)
|
2019-04-05 09:00:48 +08:00
|
|
|
|
topic = Fabricate(:topic, category: category, created_at: 1.day.ago)
|
|
|
|
|
group.add(user)
|
2019-08-06 18:26:54 +08:00
|
|
|
|
private_category = Fabricate(:private_category_with_definition, group: group)
|
2013-06-08 21:52:06 +08:00
|
|
|
|
|
2019-04-05 09:00:48 +08:00
|
|
|
|
expect(Topic.secured(Guardian.new(nil))).to eq([])
|
2013-06-08 21:52:06 +08:00
|
|
|
|
|
2019-04-05 09:00:48 +08:00
|
|
|
|
expect(Topic.secured(Guardian.new(user))).to contain_exactly(private_category.topic)
|
2013-06-08 21:52:06 +08:00
|
|
|
|
|
2019-04-05 09:00:48 +08:00
|
|
|
|
expect(Topic.secured(Guardian.new(Fabricate(:admin)))).to contain_exactly(
|
|
|
|
|
category.topic,
|
|
|
|
|
private_category.topic,
|
|
|
|
|
topic,
|
|
|
|
|
)
|
2013-06-08 21:52:06 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-30 01:39:30 +08:00
|
|
|
|
describe "all_allowed_users" do
|
2019-05-07 23:05:53 +08:00
|
|
|
|
fab!(:topic) { Fabricate(:topic, allowed_groups: [group]) }
|
|
|
|
|
fab!(:allowed_user) { Fabricate(:user) }
|
|
|
|
|
fab!(:allowed_group_user) { Fabricate(:user) }
|
|
|
|
|
fab!(:moderator) { Fabricate(:user, moderator: true) }
|
|
|
|
|
fab!(:rando) { Fabricate(:user) }
|
2015-10-30 01:39:30 +08:00
|
|
|
|
|
|
|
|
|
before do
|
|
|
|
|
topic.allowed_users << allowed_user
|
|
|
|
|
group.users << allowed_group_user
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "includes allowed_users" do
|
|
|
|
|
expect(topic.all_allowed_users).to include allowed_user
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "includes allowed_group_users" do
|
|
|
|
|
expect(topic.all_allowed_users).to include allowed_group_user
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "includes moderators if flagged and a pm" do
|
|
|
|
|
topic.stubs(:has_flags?).returns(true)
|
|
|
|
|
topic.stubs(:private_message?).returns(true)
|
|
|
|
|
expect(topic.all_allowed_users).to include moderator
|
|
|
|
|
end
|
|
|
|
|
|
2021-05-21 09:43:47 +08:00
|
|
|
|
it "includes moderators if official warning" do
|
2016-04-11 20:37:28 +08:00
|
|
|
|
topic.stubs(:subtype).returns(TopicSubtype.moderator_warning)
|
|
|
|
|
topic.stubs(:private_message?).returns(true)
|
|
|
|
|
expect(topic.all_allowed_users).to include moderator
|
|
|
|
|
end
|
|
|
|
|
|
2015-10-30 01:39:30 +08:00
|
|
|
|
it "does not include moderators if pm without flags" do
|
|
|
|
|
topic.stubs(:private_message?).returns(true)
|
|
|
|
|
expect(topic.all_allowed_users).not_to include moderator
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not include moderators for regular topic" do
|
|
|
|
|
expect(topic.all_allowed_users).not_to include moderator
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not include randos" do
|
|
|
|
|
expect(topic.all_allowed_users).not_to include rando
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2014-12-30 22:06:15 +08:00
|
|
|
|
describe "#listable_count_per_day" do
|
|
|
|
|
before(:each) do
|
2024-03-01 08:07:35 +08:00
|
|
|
|
freeze_time_safe
|
2017-07-24 21:17:42 +08:00
|
|
|
|
|
2014-12-30 22:06:15 +08:00
|
|
|
|
Fabricate(:topic)
|
|
|
|
|
Fabricate(:topic, created_at: 1.day.ago)
|
|
|
|
|
Fabricate(:topic, created_at: 1.day.ago)
|
|
|
|
|
Fabricate(:topic, created_at: 2.days.ago)
|
|
|
|
|
Fabricate(:topic, created_at: 4.days.ago)
|
|
|
|
|
end
|
2017-07-24 21:17:42 +08:00
|
|
|
|
|
2014-12-30 22:06:15 +08:00
|
|
|
|
let(:listable_topics_count_per_day) do
|
|
|
|
|
{ 1.day.ago.to_date => 2, 2.days.ago.to_date => 1, Time.now.utc.to_date => 1 }
|
2023-01-09 19:18:21 +08:00
|
|
|
|
end
|
2014-12-30 22:06:15 +08:00
|
|
|
|
|
|
|
|
|
it "collect closed interval listable topics count" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(Topic.listable_count_per_day(2.days.ago, Time.now)).to include(
|
|
|
|
|
listable_topics_count_per_day,
|
|
|
|
|
)
|
|
|
|
|
expect(Topic.listable_count_per_day(2.days.ago, Time.now)).not_to include(
|
|
|
|
|
4.days.ago.to_date => 1,
|
|
|
|
|
)
|
2014-12-30 22:06:15 +08:00
|
|
|
|
end
|
2023-09-05 13:47:18 +08:00
|
|
|
|
|
|
|
|
|
it "returns the correct count with group filter" do
|
|
|
|
|
group = Fabricate(:group)
|
|
|
|
|
group.add(user)
|
|
|
|
|
topic = Fabricate(:topic, user: user)
|
|
|
|
|
|
|
|
|
|
expect(Topic.listable_count_per_day(2.days.ago, Time.now, nil, false, [group.id])).to include(
|
|
|
|
|
Time.now.utc.to_date => 1,
|
|
|
|
|
)
|
|
|
|
|
end
|
2014-12-30 22:06:15 +08:00
|
|
|
|
end
|
|
|
|
|
|
2013-05-20 14:04:53 +08:00
|
|
|
|
describe "#secure_category?" do
|
|
|
|
|
let(:category) { Category.new }
|
|
|
|
|
|
|
|
|
|
it "is true if the category is secure" do
|
2013-07-14 09:24:16 +08:00
|
|
|
|
category.stubs(:read_restricted).returns(true)
|
2022-07-28 10:27:38 +08:00
|
|
|
|
expect(Topic.new(category: category).read_restricted_category?).to eq(true)
|
2013-05-20 14:04:53 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "is false if the category is not secure" do
|
2013-07-14 09:24:16 +08:00
|
|
|
|
category.stubs(:read_restricted).returns(false)
|
2022-07-28 10:27:38 +08:00
|
|
|
|
expect(Topic.new(category: category).read_restricted_category?).to eq(false)
|
2013-05-20 14:04:53 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 10:27:38 +08:00
|
|
|
|
it "is falsey if there is no category" do
|
|
|
|
|
expect(Topic.new(category: nil).read_restricted_category?).to eq(nil)
|
2013-05-20 14:04:53 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2013-07-09 03:23:20 +08:00
|
|
|
|
|
|
|
|
|
describe "trash!" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2022-12-06 05:56:16 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with category's topic count" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
fab!(:category) { Fabricate(:category_with_definition) }
|
2013-07-09 03:23:20 +08:00
|
|
|
|
|
|
|
|
|
it "subtracts 1 if topic is being deleted" do
|
|
|
|
|
topic = Fabricate(:topic, category: category)
|
2013-07-10 03:20:18 +08:00
|
|
|
|
expect { topic.trash!(moderator) }.to change { category.reload.topic_count }.by(-1)
|
2013-07-09 03:23:20 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "doesn't subtract 1 if topic is already deleted" do
|
|
|
|
|
topic = Fabricate(:topic, category: category, deleted_at: 1.day.ago)
|
2013-07-10 03:20:18 +08:00
|
|
|
|
expect { topic.trash!(moderator) }.to_not change { category.reload.topic_count }
|
2013-07-09 03:23:20 +08:00
|
|
|
|
end
|
2021-02-16 23:45:12 +08:00
|
|
|
|
|
|
|
|
|
it "doesn't subtract 1 if topic is unlisted" do
|
|
|
|
|
topic = Fabricate(:topic, category: category, visible: false)
|
|
|
|
|
expect { topic.trash!(moderator) }.to_not change { category.reload.topic_count }
|
|
|
|
|
end
|
2013-07-09 03:23:20 +08:00
|
|
|
|
end
|
2017-04-25 02:29:04 +08:00
|
|
|
|
|
|
|
|
|
it "trashes topic embed record" do
|
|
|
|
|
post = Fabricate(:post, topic: topic, post_number: 1)
|
|
|
|
|
topic_embed =
|
|
|
|
|
TopicEmbed.create!(
|
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
embed_url: "https://blog.codinghorror.com/password-rules-are-bullshit",
|
|
|
|
|
post_id: post.id,
|
|
|
|
|
)
|
|
|
|
|
topic.trash!
|
|
|
|
|
topic_embed.reload
|
|
|
|
|
expect(topic_embed.deleted_at).not_to eq(nil)
|
|
|
|
|
end
|
2022-12-06 05:56:16 +08:00
|
|
|
|
|
|
|
|
|
it "triggers the topic trashed event" do
|
|
|
|
|
events = DiscourseEvent.track_events(:topic_trashed) { topic.trash! }
|
|
|
|
|
|
|
|
|
|
expect(events.size).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not trigger the topic trashed event when topic is already trashed" do
|
|
|
|
|
topic.trash!
|
|
|
|
|
|
|
|
|
|
events = DiscourseEvent.track_events(:topic_trashed) { topic.trash! }
|
|
|
|
|
|
|
|
|
|
expect(events.size).to eq(0)
|
|
|
|
|
end
|
2013-07-09 03:23:20 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "recover!" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2022-12-06 05:56:16 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
context "with category's topic count" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
fab!(:category) { Fabricate(:category_with_definition) }
|
2013-07-09 03:23:20 +08:00
|
|
|
|
|
|
|
|
|
it "adds 1 if topic is deleted" do
|
|
|
|
|
topic = Fabricate(:topic, category: category, deleted_at: 1.day.ago)
|
|
|
|
|
expect { topic.recover! }.to change { category.reload.topic_count }.by(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "doesn't add 1 if topic is not deleted" do
|
|
|
|
|
topic = Fabricate(:topic, category: category)
|
|
|
|
|
expect { topic.recover! }.to_not change { category.reload.topic_count }
|
|
|
|
|
end
|
2021-02-16 23:45:12 +08:00
|
|
|
|
|
|
|
|
|
it "doesn't add 1 if topic is not visible" do
|
|
|
|
|
topic = Fabricate(:topic, category: category, visible: false)
|
|
|
|
|
expect { topic.recover! }.to_not change { category.reload.topic_count }
|
|
|
|
|
end
|
2013-07-09 03:23:20 +08:00
|
|
|
|
end
|
2017-04-25 02:29:04 +08:00
|
|
|
|
|
|
|
|
|
it "recovers topic embed record" do
|
|
|
|
|
topic = Fabricate(:topic, deleted_at: 1.day.ago)
|
|
|
|
|
post = Fabricate(:post, topic: topic, post_number: 1)
|
|
|
|
|
topic_embed =
|
|
|
|
|
TopicEmbed.create!(
|
|
|
|
|
topic_id: topic.id,
|
|
|
|
|
embed_url: "https://blog.codinghorror.com/password-rules-are-bullshit",
|
|
|
|
|
post_id: post.id,
|
|
|
|
|
deleted_at: 1.day.ago,
|
|
|
|
|
)
|
|
|
|
|
topic.recover!
|
|
|
|
|
topic_embed.reload
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic_embed.deleted_at).to be_nil
|
2017-04-25 02:29:04 +08:00
|
|
|
|
end
|
2022-12-06 05:56:16 +08:00
|
|
|
|
|
|
|
|
|
it "triggers the topic recovered event" do
|
|
|
|
|
topic.trash!
|
|
|
|
|
|
|
|
|
|
events = DiscourseEvent.track_events(:topic_recovered) { topic.recover! }
|
|
|
|
|
|
|
|
|
|
expect(events.size).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "does not trigger the topic recovered event when topic is already recovered" do
|
|
|
|
|
topic.trash!
|
|
|
|
|
topic.recover!
|
|
|
|
|
|
|
|
|
|
events = DiscourseEvent.track_events(:topic_recovered) { topic.recover! }
|
|
|
|
|
|
|
|
|
|
expect(events.size).to eq(0)
|
|
|
|
|
end
|
2013-07-09 03:23:20 +08:00
|
|
|
|
end
|
2013-10-10 07:32:03 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "new user limits" do
|
2016-06-21 04:38:15 +08:00
|
|
|
|
before do
|
|
|
|
|
SiteSetting.max_topics_in_first_day = 1
|
|
|
|
|
SiteSetting.max_replies_in_first_day = 1
|
|
|
|
|
SiteSetting.stubs(:client_settings_json).returns(SiteSetting.client_settings_json_uncached)
|
|
|
|
|
RateLimiter.stubs(:rate_limit_create_topic).returns(100)
|
2017-12-04 18:23:11 +08:00
|
|
|
|
RateLimiter.enable
|
2016-06-21 04:38:15 +08:00
|
|
|
|
end
|
2013-10-10 07:32:03 +08:00
|
|
|
|
|
2023-06-16 10:44:35 +08:00
|
|
|
|
use_redis_snapshotting
|
|
|
|
|
|
2016-06-21 04:38:15 +08:00
|
|
|
|
it "limits new users to max_topics_in_first_day and max_posts_in_first_day" do
|
|
|
|
|
start = Time.now.tomorrow.beginning_of_day
|
2013-10-10 07:32:03 +08:00
|
|
|
|
|
2016-06-21 04:38:15 +08:00
|
|
|
|
freeze_time(start)
|
2013-10-10 07:32:03 +08:00
|
|
|
|
|
2023-12-13 11:50:13 +08:00
|
|
|
|
user = Fabricate(:user, refresh_auto_groups: true)
|
2016-06-21 04:38:15 +08:00
|
|
|
|
topic_id = create_post(user: user).topic_id
|
2013-10-10 07:32:03 +08:00
|
|
|
|
|
2016-06-21 04:38:15 +08:00
|
|
|
|
freeze_time(start + 10.minutes)
|
|
|
|
|
expect { create_post(user: user) }.to raise_error(RateLimiter::LimitExceeded)
|
2013-10-10 07:32:03 +08:00
|
|
|
|
|
2016-06-21 04:38:15 +08:00
|
|
|
|
freeze_time(start + 20.minutes)
|
|
|
|
|
create_post(user: user, topic_id: topic_id)
|
2013-10-10 07:32:03 +08:00
|
|
|
|
|
2016-06-21 04:38:15 +08:00
|
|
|
|
freeze_time(start + 30.minutes)
|
|
|
|
|
expect { create_post(user: user, topic_id: topic_id) }.to raise_error(
|
|
|
|
|
RateLimiter::LimitExceeded,
|
|
|
|
|
)
|
|
|
|
|
end
|
2013-10-10 07:32:03 +08:00
|
|
|
|
|
2016-06-21 04:38:15 +08:00
|
|
|
|
it "starts counting when they make their first post/topic" do
|
|
|
|
|
start = Time.now.tomorrow.beginning_of_day
|
|
|
|
|
|
|
|
|
|
freeze_time(start)
|
|
|
|
|
|
2023-12-13 11:50:13 +08:00
|
|
|
|
user = Fabricate(:user, refresh_auto_groups: true)
|
2016-06-21 04:38:15 +08:00
|
|
|
|
|
|
|
|
|
freeze_time(start + 25.hours)
|
|
|
|
|
topic_id = create_post(user: user).topic_id
|
|
|
|
|
|
|
|
|
|
freeze_time(start + 26.hours)
|
|
|
|
|
expect { create_post(user: user) }.to raise_error(RateLimiter::LimitExceeded)
|
|
|
|
|
|
|
|
|
|
freeze_time(start + 27.hours)
|
2013-10-10 07:32:03 +08:00
|
|
|
|
create_post(user: user, topic_id: topic_id)
|
2016-06-21 04:38:15 +08:00
|
|
|
|
|
|
|
|
|
freeze_time(start + 28.hours)
|
|
|
|
|
expect { create_post(user: user, topic_id: topic_id) }.to raise_error(
|
|
|
|
|
RateLimiter::LimitExceeded,
|
|
|
|
|
)
|
|
|
|
|
end
|
2013-10-10 07:32:03 +08:00
|
|
|
|
end
|
2013-10-28 14:12:07 +08:00
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "per day personal message limit" do
|
2020-11-05 08:23:49 +08:00
|
|
|
|
before do
|
|
|
|
|
SiteSetting.max_personal_messages_per_day = 1
|
|
|
|
|
SiteSetting.max_topics_per_day = 0
|
|
|
|
|
SiteSetting.max_topics_in_first_day = 0
|
|
|
|
|
RateLimiter.enable
|
|
|
|
|
end
|
|
|
|
|
|
2023-06-16 10:44:35 +08:00
|
|
|
|
use_redis_snapshotting
|
2020-11-05 08:23:49 +08:00
|
|
|
|
|
|
|
|
|
it "limits according to max_personal_messages_per_day" do
|
|
|
|
|
create_post(
|
|
|
|
|
user: user,
|
|
|
|
|
archetype: "private_message",
|
|
|
|
|
target_usernames: [user1.username, user2.username],
|
|
|
|
|
)
|
|
|
|
|
expect {
|
|
|
|
|
create_post(
|
|
|
|
|
user: user,
|
|
|
|
|
archetype: "private_message",
|
|
|
|
|
target_usernames: [user1.username, user2.username],
|
|
|
|
|
)
|
|
|
|
|
}.to raise_error(RateLimiter::LimitExceeded)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-05-21 09:43:47 +08:00
|
|
|
|
describe ".count_exceeds_minimum?" do
|
2017-07-07 14:09:14 +08:00
|
|
|
|
before { SiteSetting.minimum_topics_similar = 20 }
|
2013-10-28 14:12:07 +08:00
|
|
|
|
|
2021-05-21 09:43:47 +08:00
|
|
|
|
context "when Topic count is greater than minimum_topics_similar" do
|
2013-10-28 14:12:07 +08:00
|
|
|
|
it "should be true" do
|
|
|
|
|
Topic.stubs(:count).returns(30)
|
2014-09-25 23:44:48 +08:00
|
|
|
|
expect(Topic.count_exceeds_minimum?).to be_truthy
|
2013-10-28 14:12:07 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when topic's count is less than minimum_topics_similar" do
|
|
|
|
|
it "should be false" do
|
|
|
|
|
Topic.stubs(:count).returns(10)
|
2014-09-25 23:44:48 +08:00
|
|
|
|
expect(Topic.count_exceeds_minimum?).to_not be_truthy
|
2013-10-28 14:12:07 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2014-02-27 08:45:20 +08:00
|
|
|
|
|
2014-04-02 03:29:15 +08:00
|
|
|
|
describe "expandable_first_post?" do
|
2015-08-19 05:15:46 +08:00
|
|
|
|
let(:topic) { Fabricate.build(:topic) }
|
2014-04-02 03:29:15 +08:00
|
|
|
|
|
|
|
|
|
it "is false if embeddable_host is blank" do
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.expandable_first_post?).to eq(false)
|
2014-04-02 03:29:15 +08:00
|
|
|
|
end
|
|
|
|
|
|
2021-05-21 09:43:47 +08:00
|
|
|
|
describe "with an embeddable host" do
|
2015-08-19 05:15:46 +08:00
|
|
|
|
before do
|
|
|
|
|
Fabricate(:embeddable_host)
|
|
|
|
|
SiteSetting.embed_truncate = true
|
|
|
|
|
topic.stubs(:has_topic_embed?).returns(true)
|
|
|
|
|
end
|
2014-04-02 03:29:15 +08:00
|
|
|
|
|
2015-08-19 05:15:46 +08:00
|
|
|
|
it "is true with the correct settings and topic_embed" do
|
|
|
|
|
expect(topic.expandable_first_post?).to eq(true)
|
|
|
|
|
end
|
|
|
|
|
it "is false if embed_truncate? is false" do
|
|
|
|
|
SiteSetting.embed_truncate = false
|
|
|
|
|
expect(topic.expandable_first_post?).to eq(false)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "is false if has_topic_embed? is false" do
|
|
|
|
|
topic.stubs(:has_topic_embed?).returns(false)
|
|
|
|
|
expect(topic.expandable_first_post?).to eq(false)
|
|
|
|
|
end
|
2014-04-02 03:29:15 +08:00
|
|
|
|
end
|
2014-04-26 00:24:22 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "has custom fields" do
|
|
|
|
|
topic = Fabricate(:topic)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.custom_fields["a"]).to eq(nil)
|
2014-04-02 03:29:15 +08:00
|
|
|
|
|
2014-04-26 00:24:22 +08:00
|
|
|
|
topic.custom_fields["bob"] = "marley"
|
|
|
|
|
topic.custom_fields["jack"] = "black"
|
|
|
|
|
topic.save
|
2014-04-02 03:29:15 +08:00
|
|
|
|
|
2014-04-26 00:24:22 +08:00
|
|
|
|
topic = Topic.find(topic.id)
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.custom_fields).to eq("bob" => "marley", "jack" => "black")
|
2014-04-02 03:29:15 +08:00
|
|
|
|
end
|
2014-08-02 05:28:00 +08:00
|
|
|
|
|
|
|
|
|
it "doesn't validate the title again if it isn't changing" do
|
2017-07-07 14:09:14 +08:00
|
|
|
|
SiteSetting.min_topic_title_length = 5
|
2014-08-02 05:28:00 +08:00
|
|
|
|
topic = Fabricate(:topic, title: "Short")
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic).to be_valid
|
2014-08-02 05:28:00 +08:00
|
|
|
|
|
2017-07-07 14:09:14 +08:00
|
|
|
|
SiteSetting.min_topic_title_length = 15
|
2014-08-02 05:28:00 +08:00
|
|
|
|
topic.last_posted_at = 1.minute.ago
|
2015-01-06 00:04:23 +08:00
|
|
|
|
expect(topic.save).to eq(true)
|
2014-08-02 05:28:00 +08:00
|
|
|
|
end
|
2015-03-03 03:25:25 +08:00
|
|
|
|
|
2016-01-19 15:35:46 +08:00
|
|
|
|
it "Correctly sets #message_archived?" do
|
|
|
|
|
topic = Fabricate(:private_message_topic)
|
|
|
|
|
user = topic.user
|
|
|
|
|
|
|
|
|
|
expect(topic.message_archived?(user)).to eq(false)
|
|
|
|
|
|
2018-04-05 15:17:31 +08:00
|
|
|
|
group2 = Fabricate(:group)
|
|
|
|
|
|
2016-01-19 15:35:46 +08:00
|
|
|
|
group.add(user)
|
|
|
|
|
|
|
|
|
|
TopicAllowedGroup.create!(topic_id: topic.id, group_id: group.id)
|
2018-04-05 15:17:31 +08:00
|
|
|
|
TopicAllowedGroup.create!(topic_id: topic.id, group_id: group2.id)
|
2016-01-19 15:35:46 +08:00
|
|
|
|
GroupArchivedMessage.create!(topic_id: topic.id, group_id: group.id)
|
2015-03-03 03:25:25 +08:00
|
|
|
|
|
2016-01-19 15:35:46 +08:00
|
|
|
|
expect(topic.message_archived?(user)).to eq(true)
|
2018-04-05 15:17:31 +08:00
|
|
|
|
|
|
|
|
|
# here is a pickle, we add another group, make the user a
|
|
|
|
|
# member of that new group... now this message is not properly archived
|
|
|
|
|
# for the user any more
|
|
|
|
|
group2.add(user)
|
|
|
|
|
expect(topic.message_archived?(user)).to eq(false)
|
2015-03-03 03:25:25 +08:00
|
|
|
|
end
|
2016-03-05 04:41:59 +08:00
|
|
|
|
|
|
|
|
|
it "will trigger :topic_status_updated" do
|
|
|
|
|
topic = Fabricate(:topic)
|
|
|
|
|
user = topic.user
|
|
|
|
|
user.admin = true
|
|
|
|
|
@topic_status_event_triggered = false
|
|
|
|
|
|
|
|
|
|
blk = Proc.new { @topic_status_event_triggered = true }
|
|
|
|
|
|
2020-11-12 03:46:13 +08:00
|
|
|
|
DiscourseEvent.on(:topic_status_updated, &blk)
|
|
|
|
|
|
2016-03-05 04:41:59 +08:00
|
|
|
|
topic.update_status("closed", true, user)
|
|
|
|
|
topic.reload
|
2016-03-26 03:12:00 +08:00
|
|
|
|
|
2016-03-05 04:41:59 +08:00
|
|
|
|
expect(@topic_status_event_triggered).to eq(true)
|
2020-11-12 03:46:13 +08:00
|
|
|
|
ensure
|
|
|
|
|
DiscourseEvent.off(:topic_status_updated, &blk)
|
2016-03-05 04:41:59 +08:00
|
|
|
|
end
|
2016-12-02 14:03:31 +08:00
|
|
|
|
|
|
|
|
|
it "allows users to normalize counts" do
|
|
|
|
|
topic = Fabricate(:topic, last_posted_at: 1.year.ago)
|
|
|
|
|
post1 = Fabricate(:post, topic: topic, post_number: 1)
|
|
|
|
|
post2 = Fabricate(:post, topic: topic, post_type: Post.types[:whisper], post_number: 2)
|
|
|
|
|
|
|
|
|
|
Topic.reset_all_highest!
|
|
|
|
|
topic.reload
|
|
|
|
|
|
|
|
|
|
expect(topic.posts_count).to eq(1)
|
|
|
|
|
expect(topic.highest_post_number).to eq(post1.post_number)
|
|
|
|
|
expect(topic.highest_staff_post_number).to eq(post2.post_number)
|
2020-03-11 05:13:17 +08:00
|
|
|
|
expect(topic.last_posted_at).to eq_time(post1.created_at)
|
2016-12-02 14:03:31 +08:00
|
|
|
|
end
|
|
|
|
|
|
2022-07-28 00:14:14 +08:00
|
|
|
|
describe "featured link" do
|
2016-12-05 20:31:43 +08:00
|
|
|
|
before { SiteSetting.topic_featured_link_enabled = true }
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2016-12-05 20:31:43 +08:00
|
|
|
|
|
|
|
|
|
it "can validate featured link" do
|
|
|
|
|
topic.featured_link = " invalid string"
|
|
|
|
|
|
|
|
|
|
expect(topic).not_to be_valid
|
|
|
|
|
expect(topic.errors[:featured_link]).to be_present
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "can properly save the featured link" do
|
|
|
|
|
topic.featured_link = " https://github.com/discourse/discourse"
|
|
|
|
|
|
|
|
|
|
expect(topic.save).to be_truthy
|
2016-12-16 06:46:43 +08:00
|
|
|
|
expect(topic.featured_link).to eq("https://github.com/discourse/discourse")
|
2016-12-05 20:31:43 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when category restricts present" do
|
|
|
|
|
let!(:link_category) { Fabricate(:link_category) }
|
|
|
|
|
let(:link_topic) { Fabricate(:topic, category: link_category) }
|
|
|
|
|
|
|
|
|
|
it "can save the featured link if it belongs to that category" do
|
|
|
|
|
link_topic.featured_link = "https://github.com/discourse/discourse"
|
|
|
|
|
expect(link_topic.save).to be_truthy
|
2016-12-16 06:46:43 +08:00
|
|
|
|
expect(link_topic.featured_link).to eq("https://github.com/discourse/discourse")
|
2016-12-05 20:31:43 +08:00
|
|
|
|
end
|
|
|
|
|
|
2016-12-16 06:46:43 +08:00
|
|
|
|
it "can not save the featured link if category does not allow it" do
|
2019-08-06 18:26:54 +08:00
|
|
|
|
topic.category = Fabricate(:category_with_definition, topic_featured_link_allowed: false)
|
2016-12-05 20:31:43 +08:00
|
|
|
|
topic.featured_link = "https://github.com/discourse/discourse"
|
|
|
|
|
expect(topic.save).to be_falsey
|
|
|
|
|
end
|
2016-12-16 06:46:43 +08:00
|
|
|
|
|
|
|
|
|
it "if category changes to disallow it, topic remains valid" do
|
|
|
|
|
t =
|
|
|
|
|
Fabricate(
|
|
|
|
|
:topic,
|
|
|
|
|
category: link_category,
|
|
|
|
|
featured_link: "https://github.com/discourse/discourse",
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
link_category.topic_featured_link_allowed = false
|
|
|
|
|
link_category.save!
|
|
|
|
|
t.reload
|
|
|
|
|
|
|
|
|
|
expect(t.valid?).to eq(true)
|
|
|
|
|
end
|
2016-12-05 20:31:43 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2017-02-03 06:27:41 +08:00
|
|
|
|
|
|
|
|
|
describe "#time_to_first_response" do
|
|
|
|
|
it "should have no results if no topics in range" do
|
|
|
|
|
expect(Topic.time_to_first_response_per_day(5.days.ago, Time.zone.now).count).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should have no results if there is only a topic with no replies" do
|
|
|
|
|
topic = Fabricate(:topic, created_at: 1.hour.ago)
|
2017-02-04 04:07:38 +08:00
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
expect(Topic.time_to_first_response_per_day(5.days.ago, Time.zone.now).count).to eq(0)
|
|
|
|
|
expect(Topic.time_to_first_response_total).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should have no results if reply is from first poster" do
|
|
|
|
|
topic = Fabricate(:topic, created_at: 1.hour.ago)
|
2017-02-04 04:07:38 +08:00
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1)
|
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 2)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
expect(Topic.time_to_first_response_per_day(5.days.ago, Time.zone.now).count).to eq(0)
|
|
|
|
|
expect(Topic.time_to_first_response_total).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "should have results if there's a topic with replies" do
|
|
|
|
|
topic = Fabricate(:topic, created_at: 3.hours.ago)
|
2017-02-04 04:07:38 +08:00
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 3.hours.ago)
|
|
|
|
|
Fabricate(:post, topic: topic, post_number: 2, created_at: 2.hours.ago)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
r = Topic.time_to_first_response_per_day(5.days.ago, Time.zone.now)
|
|
|
|
|
expect(r.count).to eq(1)
|
|
|
|
|
expect(r[0]["hours"].to_f.round).to eq(1)
|
|
|
|
|
expect(Topic.time_to_first_response_total).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
2020-04-04 18:31:34 +08:00
|
|
|
|
it "should have results if there's a topic with replies" do
|
|
|
|
|
SiteSetting.max_category_nesting = 3
|
|
|
|
|
|
|
|
|
|
category = Fabricate(:category_with_definition)
|
|
|
|
|
subcategory = Fabricate(:category_with_definition, parent_category_id: category.id)
|
|
|
|
|
subsubcategory = Fabricate(:category_with_definition, parent_category_id: subcategory.id)
|
|
|
|
|
|
|
|
|
|
topic = Fabricate(:topic, category: subsubcategory, created_at: 3.hours.ago)
|
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 3.hours.ago)
|
|
|
|
|
Fabricate(:post, topic: topic, post_number: 2, created_at: 2.hours.ago)
|
|
|
|
|
|
2020-04-22 16:52:50 +08:00
|
|
|
|
expect(
|
|
|
|
|
Topic.time_to_first_response_total(category_id: category.id, include_subcategories: true),
|
|
|
|
|
).to eq(1)
|
2020-04-04 18:31:34 +08:00
|
|
|
|
end
|
|
|
|
|
|
2017-02-03 06:27:41 +08:00
|
|
|
|
it "should only count regular posts as the first response" do
|
2017-02-04 04:07:38 +08:00
|
|
|
|
topic = Fabricate(:topic, created_at: 5.hours.ago)
|
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 5.hours.ago)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 2,
|
|
|
|
|
created_at: 4.hours.ago,
|
|
|
|
|
post_type: Post.types[:whisper],
|
|
|
|
|
)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 3,
|
|
|
|
|
created_at: 3.hours.ago,
|
|
|
|
|
post_type: Post.types[:moderator_action],
|
|
|
|
|
)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 4,
|
|
|
|
|
created_at: 2.hours.ago,
|
|
|
|
|
post_type: Post.types[:small_action],
|
|
|
|
|
)
|
|
|
|
|
Fabricate(:post, topic: topic, post_number: 5, created_at: 1.hour.ago)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
r = Topic.time_to_first_response_per_day(5.days.ago, Time.zone.now)
|
|
|
|
|
expect(r.count).to eq(1)
|
|
|
|
|
expect(r[0]["hours"].to_f.round).to eq(4)
|
|
|
|
|
expect(Topic.time_to_first_response_total).to eq(4)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "#with_no_response" do
|
|
|
|
|
it "returns nothing with no topics" do
|
|
|
|
|
expect(Topic.with_no_response_per_day(5.days.ago, Time.zone.now).count).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns 1 with one topic that has no replies" do
|
2017-02-04 04:07:38 +08:00
|
|
|
|
topic = Fabricate(:topic, created_at: 5.hours.ago)
|
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 5.hours.ago)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
expect(Topic.with_no_response_per_day(5.days.ago, Time.zone.now).count).to eq(1)
|
|
|
|
|
expect(Topic.with_no_response_total).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns 1 with one topic that has no replies and author was changed on first post" do
|
2017-02-04 04:07:38 +08:00
|
|
|
|
topic = Fabricate(:topic, created_at: 5.hours.ago)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
user: Fabricate(:user),
|
|
|
|
|
post_number: 1,
|
|
|
|
|
created_at: 5.hours.ago,
|
|
|
|
|
)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
expect(Topic.with_no_response_per_day(5.days.ago, Time.zone.now).count).to eq(1)
|
|
|
|
|
expect(Topic.with_no_response_total).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns 1 with one topic that has a reply by the first poster" do
|
2017-02-04 04:07:38 +08:00
|
|
|
|
topic = Fabricate(:topic, created_at: 5.hours.ago)
|
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 5.hours.ago)
|
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 2, created_at: 2.hours.ago)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
expect(Topic.with_no_response_per_day(5.days.ago, Time.zone.now).count).to eq(1)
|
|
|
|
|
expect(Topic.with_no_response_total).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns 0 with a topic with 1 reply" do
|
2018-06-05 15:29:17 +08:00
|
|
|
|
topic = Fabricate(:topic, created_at: 5.hours.ago)
|
|
|
|
|
_post1 =
|
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 5.hours.ago)
|
|
|
|
|
_post2 = Fabricate(:post, topic: topic, post_number: 2, created_at: 2.hours.ago)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
expect(Topic.with_no_response_per_day(5.days.ago, Time.zone.now).count).to eq(0)
|
|
|
|
|
expect(Topic.with_no_response_total).to eq(0)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns 1 with one topic that doesn't have regular replies" do
|
2017-02-04 04:07:38 +08:00
|
|
|
|
topic = Fabricate(:topic, created_at: 5.hours.ago)
|
|
|
|
|
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 5.hours.ago)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 2,
|
|
|
|
|
created_at: 4.hours.ago,
|
|
|
|
|
post_type: Post.types[:whisper],
|
|
|
|
|
)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 3,
|
|
|
|
|
created_at: 3.hours.ago,
|
|
|
|
|
post_type: Post.types[:moderator_action],
|
|
|
|
|
)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 4,
|
|
|
|
|
created_at: 2.hours.ago,
|
|
|
|
|
post_type: Post.types[:small_action],
|
|
|
|
|
)
|
2017-02-03 06:27:41 +08:00
|
|
|
|
expect(Topic.with_no_response_per_day(5.days.ago, Time.zone.now).count).to eq(1)
|
|
|
|
|
expect(Topic.with_no_response_total).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
end
|
2017-04-27 11:53:53 +08:00
|
|
|
|
|
|
|
|
|
describe "#pm_with_non_human_user?" do
|
2022-10-25 15:29:09 +08:00
|
|
|
|
fab!(:robot) { Fabricate(:bot) }
|
2017-04-27 11:53:53 +08:00
|
|
|
|
|
2021-12-21 02:59:10 +08:00
|
|
|
|
fab!(:topic) do
|
2019-04-16 15:47:16 +08:00
|
|
|
|
topic =
|
|
|
|
|
Fabricate(
|
|
|
|
|
:private_message_topic,
|
|
|
|
|
topic_allowed_users: [
|
|
|
|
|
Fabricate.build(:topic_allowed_user, user: robot),
|
|
|
|
|
Fabricate.build(:topic_allowed_user, user: user),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
Fabricate(:post, topic: topic)
|
|
|
|
|
topic
|
2017-04-27 11:53:53 +08:00
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when PM is between a human and a non human user" do
|
|
|
|
|
it "should return true" do
|
|
|
|
|
expect(topic.pm_with_non_human_user?).to be(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when PM contains 2 human users and a non human user" do
|
|
|
|
|
it "should return false" do
|
|
|
|
|
Fabricate(:topic_allowed_user, topic: topic, user: Fabricate(:user))
|
|
|
|
|
|
|
|
|
|
expect(topic.pm_with_non_human_user?).to be(false)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when PM only contains a user" do
|
|
|
|
|
it "should return true" do
|
|
|
|
|
topic.topic_allowed_users.first.destroy!
|
|
|
|
|
|
|
|
|
|
expect(topic.reload.pm_with_non_human_user?).to be(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when PM contains a group" do
|
|
|
|
|
it "should return false" do
|
|
|
|
|
Fabricate(:topic_allowed_group, topic: topic)
|
|
|
|
|
|
|
|
|
|
expect(topic.pm_with_non_human_user?).to be(false)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
describe "when topic is not a PM" do
|
|
|
|
|
it "should return false" do
|
|
|
|
|
topic.convert_to_public_topic(Fabricate(:admin))
|
|
|
|
|
|
|
|
|
|
expect(topic.pm_with_non_human_user?).to be(false)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2017-10-10 16:26:56 +08:00
|
|
|
|
|
|
|
|
|
describe "#remove_allowed_user" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:topic)
|
2023-03-02 13:47:54 +08:00
|
|
|
|
fab!(:private_topic) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:private_message_topic,
|
|
|
|
|
title: "Private message",
|
|
|
|
|
user: admin,
|
|
|
|
|
topic_allowed_users: [
|
|
|
|
|
Fabricate.build(:topic_allowed_user, user: admin),
|
|
|
|
|
Fabricate.build(:topic_allowed_user, user: user1),
|
|
|
|
|
],
|
|
|
|
|
)
|
|
|
|
|
end
|
2017-10-10 16:26:56 +08:00
|
|
|
|
|
|
|
|
|
describe "removing oneself" do
|
2023-06-14 04:02:21 +08:00
|
|
|
|
it "should remove oneself" do
|
2021-12-21 02:59:10 +08:00
|
|
|
|
topic.allowed_users << user1
|
2017-10-10 16:26:56 +08:00
|
|
|
|
|
2021-12-21 02:59:10 +08:00
|
|
|
|
expect(topic.remove_allowed_user(user1, user1)).to eq(true)
|
|
|
|
|
expect(topic.allowed_users.include?(user1)).to eq(false)
|
2017-10-10 16:26:56 +08:00
|
|
|
|
|
|
|
|
|
post = Post.last
|
|
|
|
|
|
2022-08-29 18:01:16 +08:00
|
|
|
|
expect(post.user).to eq(user1)
|
2017-10-10 16:26:56 +08:00
|
|
|
|
expect(post.post_type).to eq(Post.types[:small_action])
|
|
|
|
|
expect(post.action_code).to eq("user_left")
|
|
|
|
|
end
|
2023-03-02 13:47:54 +08:00
|
|
|
|
|
|
|
|
|
it "should show a small action when user removes themselves from pm" do
|
|
|
|
|
expect do private_topic.remove_allowed_user(user1, user1) end.to change {
|
|
|
|
|
private_topic.posts.where(action_code: "user_left").count
|
|
|
|
|
}.by(1)
|
|
|
|
|
end
|
2017-10-10 16:26:56 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2017-11-29 21:52:41 +08:00
|
|
|
|
|
|
|
|
|
describe "#featured_link_root_domain" do
|
|
|
|
|
let(:topic) { Fabricate.build(:topic) }
|
|
|
|
|
|
2017-12-04 10:00:07 +08:00
|
|
|
|
%w[
|
|
|
|
|
https://meta.discourse.org
|
|
|
|
|
https://meta.discourse.org/
|
|
|
|
|
https://meta.discourse.org/?filter=test
|
2018-01-07 05:01:35 +08:00
|
|
|
|
https://meta.discourse.org/t/中國/1
|
2017-12-04 10:00:07 +08:00
|
|
|
|
].each do |featured_link|
|
|
|
|
|
it "should extract the root domain from #{featured_link} correctly" do
|
2017-11-29 21:52:41 +08:00
|
|
|
|
topic.featured_link = featured_link
|
|
|
|
|
expect(topic.featured_link_root_domain).to eq("discourse.org")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2018-08-10 08:51:03 +08:00
|
|
|
|
|
|
|
|
|
describe "#reset_bumped_at" do
|
2018-11-15 01:56:22 +08:00
|
|
|
|
it "ignores hidden, deleted, moderator and small action posts when resetting the topic's bump date" do
|
|
|
|
|
post1 = create_post(created_at: 10.hours.ago)
|
|
|
|
|
topic = post1.topic
|
2018-08-10 08:51:03 +08:00
|
|
|
|
|
|
|
|
|
expect { topic.reset_bumped_at }.to_not change { topic.bumped_at }
|
|
|
|
|
|
2018-11-15 01:56:22 +08:00
|
|
|
|
post2 = Fabricate(:post, topic: topic, post_number: 2, created_at: 9.hours.ago)
|
2018-08-10 08:51:03 +08:00
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 3,
|
|
|
|
|
created_at: 8.hours.ago,
|
|
|
|
|
deleted_at: 1.hour.ago,
|
|
|
|
|
)
|
|
|
|
|
Fabricate(:post, topic: topic, post_number: 4, created_at: 7.hours.ago, hidden: true)
|
|
|
|
|
Fabricate(:post, topic: topic, post_number: 5, created_at: 6.hours.ago, user_deleted: true)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 6,
|
|
|
|
|
created_at: 5.hours.ago,
|
|
|
|
|
post_type: Post.types[:whisper],
|
|
|
|
|
)
|
|
|
|
|
|
2018-11-15 01:56:22 +08:00
|
|
|
|
expect { topic.reset_bumped_at }.to change { topic.bumped_at }.to(post2.reload.created_at)
|
2018-08-10 08:51:03 +08:00
|
|
|
|
|
2018-11-15 01:56:22 +08:00
|
|
|
|
post3 =
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 7,
|
|
|
|
|
created_at: 4.hours.ago,
|
|
|
|
|
post_type: Post.types[:regular],
|
|
|
|
|
)
|
|
|
|
|
expect { topic.reset_bumped_at }.to change { topic.bumped_at }.to(post3.reload.created_at)
|
2018-08-10 08:51:03 +08:00
|
|
|
|
|
2018-11-15 01:56:22 +08:00
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 8,
|
|
|
|
|
created_at: 3.hours.ago,
|
|
|
|
|
post_type: Post.types[:small_action],
|
|
|
|
|
)
|
|
|
|
|
Fabricate(
|
|
|
|
|
:post,
|
|
|
|
|
topic: topic,
|
|
|
|
|
post_number: 9,
|
|
|
|
|
created_at: 2.hours.ago,
|
|
|
|
|
post_type: Post.types[:moderator_action],
|
|
|
|
|
)
|
|
|
|
|
expect { topic.reset_bumped_at }.not_to change { topic.bumped_at }
|
2018-08-10 08:51:03 +08:00
|
|
|
|
end
|
|
|
|
|
end
|
2019-07-04 16:12:39 +08:00
|
|
|
|
|
|
|
|
|
describe "#access_topic_via_group" do
|
|
|
|
|
let(:open_group) { Fabricate(:group, public_admission: true) }
|
|
|
|
|
let(:request_group) do
|
|
|
|
|
Fabricate(:group).tap do |g|
|
|
|
|
|
g.add_owner(user)
|
|
|
|
|
g.allow_membership_requests = true
|
|
|
|
|
g.save!
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-08-06 18:26:54 +08:00
|
|
|
|
let(:category) { Fabricate(:category_with_definition) }
|
2019-07-04 16:12:39 +08:00
|
|
|
|
let(:topic) { Fabricate(:topic, category: category) }
|
|
|
|
|
|
|
|
|
|
it "returns a group that is open or accepts membership requests and has access to the topic" do
|
|
|
|
|
expect(topic.access_topic_via_group).to eq(nil)
|
|
|
|
|
|
|
|
|
|
category.set_permissions(request_group => :full)
|
|
|
|
|
category.save!
|
|
|
|
|
|
|
|
|
|
expect(topic.access_topic_via_group).to eq(request_group)
|
|
|
|
|
|
|
|
|
|
category.set_permissions(request_group => :full, open_group => :full)
|
|
|
|
|
category.save!
|
|
|
|
|
|
|
|
|
|
expect(topic.access_topic_via_group).to eq(open_group)
|
|
|
|
|
end
|
|
|
|
|
end
|
2019-12-10 03:15:47 +08:00
|
|
|
|
|
|
|
|
|
describe "#after_update" do
|
|
|
|
|
fab!(:topic) { Fabricate(:topic, user: user) }
|
|
|
|
|
fab!(:category) { Fabricate(:category_with_definition, read_restricted: true) }
|
|
|
|
|
|
|
|
|
|
it "removes the topic as featured from user profiles if new category is read_restricted" do
|
|
|
|
|
user.user_profile.update(featured_topic: topic)
|
|
|
|
|
expect(user.user_profile.featured_topic).to eq(topic)
|
|
|
|
|
|
|
|
|
|
topic.update(category: category)
|
|
|
|
|
expect(user.user_profile.reload.featured_topic).to eq(nil)
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-04-08 21:44:31 +08:00
|
|
|
|
|
|
|
|
|
describe "#auto_close_threshold_reached?" do
|
|
|
|
|
before do
|
|
|
|
|
Reviewable.set_priorities(low: 2.0, medium: 6.0, high: 9.0)
|
|
|
|
|
SiteSetting.num_flaggers_to_close_topic = 2
|
|
|
|
|
SiteSetting.reviewable_default_visibility = "medium"
|
2021-12-09 01:12:24 +08:00
|
|
|
|
SiteSetting.auto_close_topic_sensitivity = Reviewable.sensitivities[:high]
|
2020-04-08 21:44:31 +08:00
|
|
|
|
post = Fabricate(:post)
|
|
|
|
|
@topic = post.topic
|
|
|
|
|
@reviewable = Fabricate(:reviewable_flagged_post, target: post, topic: @topic)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "ignores flags with a low score" do
|
|
|
|
|
5.times do
|
|
|
|
|
@reviewable.add_score(
|
|
|
|
|
Fabricate(:user, trust_level: TrustLevel[0]),
|
|
|
|
|
PostActionType.types[:spam],
|
|
|
|
|
created_at: 1.minute.ago,
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
expect(@topic.auto_close_threshold_reached?).to eq(false)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns true when the flags have a high score" do
|
|
|
|
|
5.times do
|
|
|
|
|
@reviewable.add_score(
|
|
|
|
|
Fabricate(:user, admin: true),
|
|
|
|
|
PostActionType.types[:spam],
|
|
|
|
|
created_at: 1.minute.ago,
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
expect(@topic.auto_close_threshold_reached?).to eq(true)
|
|
|
|
|
end
|
|
|
|
|
end
|
2020-07-13 14:30:00 +08:00
|
|
|
|
|
|
|
|
|
describe "#update_action_counts" do
|
|
|
|
|
let(:topic) { Fabricate(:topic) }
|
|
|
|
|
|
|
|
|
|
it "updates like count without including whisper posts" do
|
|
|
|
|
post = Fabricate(:post, topic: topic)
|
|
|
|
|
whisper_post = Fabricate(:post, topic: topic, post_type: Post.types[:whisper])
|
|
|
|
|
|
|
|
|
|
topic.update_action_counts
|
|
|
|
|
expect(topic.like_count).to eq(0)
|
|
|
|
|
|
|
|
|
|
PostAction.create!(post: post, user: user, post_action_type_id: PostActionType.types[:like])
|
|
|
|
|
|
|
|
|
|
topic.update_action_counts
|
|
|
|
|
expect(topic.like_count).to eq(1)
|
|
|
|
|
|
|
|
|
|
PostAction.create!(
|
|
|
|
|
post: whisper_post,
|
|
|
|
|
user: user,
|
|
|
|
|
post_action_type_id: PostActionType.types[:like],
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
topic.update_action_counts
|
|
|
|
|
expect(topic.like_count).to eq(1)
|
|
|
|
|
end
|
|
|
|
|
end
|
2021-01-15 08:54:46 +08:00
|
|
|
|
|
2023-01-27 09:27:15 +08:00
|
|
|
|
describe "#incoming_email_addresses" do
|
|
|
|
|
fab!(:group) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:group,
|
|
|
|
|
smtp_server: "imap.gmail.com",
|
|
|
|
|
smtp_port: 587,
|
|
|
|
|
email_username: "discourse@example.com",
|
|
|
|
|
email_password: "discourse@example.com",
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
fab!(:topic) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:private_message_topic,
|
|
|
|
|
topic_allowed_groups: [Fabricate.build(:topic_allowed_group, group: group)],
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
let!(:incoming1) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:incoming_email,
|
|
|
|
|
to_addresses: "discourse@example.com",
|
|
|
|
|
from_address: "johnsmith@user.com",
|
|
|
|
|
topic: topic,
|
|
|
|
|
post: topic.posts.first,
|
|
|
|
|
created_at: 20.minutes.ago,
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
let!(:incoming2) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:incoming_email,
|
|
|
|
|
from_address: "discourse@example.com",
|
|
|
|
|
to_addresses: "johnsmith@user.com",
|
|
|
|
|
topic: topic,
|
|
|
|
|
post: Fabricate(:post, topic: topic),
|
|
|
|
|
created_at: 10.minutes.ago,
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
let!(:incoming3) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:incoming_email,
|
|
|
|
|
to_addresses: "discourse@example.com",
|
|
|
|
|
from_address: "johnsmith@user.com",
|
|
|
|
|
topic: topic,
|
|
|
|
|
post: topic.posts.first,
|
|
|
|
|
cc_addresses: "otherguy@user.com",
|
|
|
|
|
created_at: 2.minutes.ago,
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
let!(:incoming4) do
|
|
|
|
|
Fabricate(
|
|
|
|
|
:incoming_email,
|
|
|
|
|
to_addresses: "unrelated@test.com",
|
|
|
|
|
from_address: "discourse@example.com",
|
|
|
|
|
topic: topic,
|
|
|
|
|
post: topic.posts.first,
|
|
|
|
|
created_at: 1.minutes.ago,
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns an array of all the incoming email addresses" do
|
|
|
|
|
expect(topic.incoming_email_addresses).to match_array(
|
|
|
|
|
%w[discourse@example.com johnsmith@user.com otherguy@user.com unrelated@test.com],
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns an array of all the incoming email addresses where incoming was received before X" do
|
|
|
|
|
expect(topic.incoming_email_addresses(received_before: 5.minutes.ago)).to match_array(
|
|
|
|
|
%w[discourse@example.com johnsmith@user.com],
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when the group is present" do
|
|
|
|
|
it "excludes incoming emails that are not to or CCd to the group" do
|
|
|
|
|
expect(topic.incoming_email_addresses(group: group)).not_to include("unrelated@test.com")
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
2021-10-26 23:31:15 +08:00
|
|
|
|
describe "#cannot_permanently_delete_reason" do
|
2023-11-10 06:47:59 +08:00
|
|
|
|
fab!(:post)
|
2021-10-26 23:31:15 +08:00
|
|
|
|
let!(:topic) { post.topic }
|
|
|
|
|
|
|
|
|
|
before { freeze_time }
|
|
|
|
|
|
|
|
|
|
it "returns error message if topic has more posts" do
|
2022-02-07 11:23:34 +08:00
|
|
|
|
post_2 = create_post(user: user, topic_id: topic.id, raw: "some post content")
|
2021-10-26 23:31:15 +08:00
|
|
|
|
|
|
|
|
|
PostDestroyer.new(admin, post).destroy
|
|
|
|
|
expect(topic.reload.cannot_permanently_delete_reason(Fabricate(:admin))).to eq(
|
|
|
|
|
I18n.t("post.cannot_permanently_delete.many_posts"),
|
|
|
|
|
)
|
|
|
|
|
|
2022-02-07 11:23:34 +08:00
|
|
|
|
PostDestroyer.new(admin, post_2.reload).destroy
|
2022-08-10 17:11:50 +08:00
|
|
|
|
expect(topic.reload.cannot_permanently_delete_reason(Fabricate(:admin))).to eq(
|
|
|
|
|
I18n.t("post.cannot_permanently_delete.many_posts"),
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
PostDestroyer.new(admin, post_2.reload, force_destroy: true).destroy
|
2021-10-26 23:31:15 +08:00
|
|
|
|
expect(topic.reload.cannot_permanently_delete_reason(Fabricate(:admin))).to eq(nil)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns error message if same admin and time did not pass" do
|
|
|
|
|
PostDestroyer.new(admin, post).destroy
|
|
|
|
|
expect(topic.reload.cannot_permanently_delete_reason(admin)).to eq(
|
|
|
|
|
I18n.t(
|
|
|
|
|
"post.cannot_permanently_delete.wait_or_different_admin",
|
|
|
|
|
time_left: RateLimiter.time_left(Post::PERMANENT_DELETE_TIMER.to_i),
|
2023-01-09 19:18:21 +08:00
|
|
|
|
),
|
2021-10-26 23:31:15 +08:00
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "returns nothing if different admin" do
|
|
|
|
|
PostDestroyer.new(admin, post).destroy
|
|
|
|
|
expect(topic.reload.cannot_permanently_delete_reason(Fabricate(:admin))).to eq(nil)
|
|
|
|
|
end
|
|
|
|
|
end
|
2022-06-28 05:21:05 +08:00
|
|
|
|
|
|
|
|
|
describe "#publish_stats_to_clients!" do
|
|
|
|
|
fab!(:user1) { Fabricate(:user) }
|
|
|
|
|
fab!(:user2) { Fabricate(:user) }
|
|
|
|
|
fab!(:topic) { Fabricate(:topic, user: user1) }
|
|
|
|
|
fab!(:post1) { Fabricate(:post, topic: topic, user: user1) }
|
|
|
|
|
fab!(:post2) { Fabricate(:post, topic: topic, user: user2) }
|
|
|
|
|
fab!(:like1) { Fabricate(:like, post: post1, user: user2) }
|
|
|
|
|
|
|
|
|
|
it "it is triggered when a post publishes a message of type :liked or :unliked" do
|
|
|
|
|
%i[liked unliked].each do |action|
|
|
|
|
|
messages =
|
|
|
|
|
MessageBus.track_publish("/topic/#{topic.id}") do
|
|
|
|
|
post1.publish_change_to_clients!(action)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
stats_message = messages.select { |msg| msg.data[:type] == :stats }.first
|
|
|
|
|
expect(stats_message).to be_present
|
|
|
|
|
expect(stats_message.data[:like_count]).to eq(topic.like_count)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "it is triggered when a post publishes a message of type :created, :destroyed, :deleted, :recovered" do
|
|
|
|
|
freeze_time Date.today
|
|
|
|
|
|
|
|
|
|
%i[created destroyed deleted recovered].each do |action|
|
|
|
|
|
messages =
|
|
|
|
|
MessageBus.track_publish("/topic/#{topic.id}") do
|
|
|
|
|
post1.publish_change_to_clients!(action)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
stats_message = messages.select { |msg| msg.data[:type] == :stats }.first
|
|
|
|
|
expect(stats_message).to be_present
|
|
|
|
|
expect(stats_message.data[:posts_count]).to eq(topic.posts_count)
|
|
|
|
|
expect(stats_message.data[:last_posted_at]).to eq(topic.last_posted_at.as_json)
|
|
|
|
|
expect(stats_message.data[:last_poster]).to eq(
|
|
|
|
|
BasicUserSerializer.new(topic.last_poster, root: false).as_json,
|
|
|
|
|
)
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
it "it is not triggered when a post publishes an unhandled kind of message" do
|
|
|
|
|
%i[unhandled unknown dont_care].each do |action|
|
|
|
|
|
messages =
|
|
|
|
|
MessageBus.track_publish("/topic/#{topic.id}") do
|
|
|
|
|
post1.publish_change_to_clients!(action)
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
stats_message = messages.select { |msg| msg.data[:type] == :stats }.first
|
|
|
|
|
expect(stats_message).to be_blank
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2023-03-08 00:58:14 +08:00
|
|
|
|
|
|
|
|
|
describe "#group_pm?" do
|
|
|
|
|
context "when topic is not a private message" do
|
|
|
|
|
subject(:public_topic) { Fabricate(:topic) }
|
|
|
|
|
|
|
|
|
|
it { is_expected.not_to be_a_group_pm }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when topic is a private message" do
|
|
|
|
|
subject(:pm_topic) { Fabricate(:private_message_topic) }
|
|
|
|
|
|
|
|
|
|
context "when more than two people have access" do
|
|
|
|
|
let(:other_user) { Fabricate(:user) }
|
|
|
|
|
|
|
|
|
|
before { pm_topic.allowed_users << other_user }
|
|
|
|
|
|
|
|
|
|
it { is_expected.to be_a_group_pm }
|
|
|
|
|
end
|
|
|
|
|
|
|
|
|
|
context "when no more than two people have access" do
|
|
|
|
|
it { is_expected.not_to be_a_group_pm }
|
|
|
|
|
end
|
|
|
|
|
end
|
|
|
|
|
end
|
2013-02-06 03:16:51 +08:00
|
|
|
|
end
|