mirror of
https://github.com/discourse/discourse.git
synced 2024-11-23 18:36:35 +08:00
87ec058b8b
Currently the auto-bump cooldown is hard-coded to 24 hours. This change makes the highlighted 24 hours part configurable (defaulting to 24 hours), and the rest of the process remains the same. This uses the new CategorySetting model associated with Category. We decided to add this because we want to move away from custom fields due to the lack of type casting and validations, but we want to keep the loading of these optional as they are not needed for almost all of the flows. Category settings will be back-filled to all categories as part of this change, and creating a new category will now also create a category setting.
1356 lines
44 KiB
Ruby
1356 lines
44 KiB
Ruby
# encoding: utf-8
|
|
# frozen_string_literal: true
|
|
|
|
RSpec.describe Category do
|
|
fab!(:user) { Fabricate(:user) }
|
|
|
|
it { is_expected.to validate_presence_of :user_id }
|
|
it { is_expected.to validate_presence_of :name }
|
|
|
|
it do
|
|
is_expected.to validate_numericality_of(:default_slow_mode_seconds).is_greater_than(
|
|
0,
|
|
).only_integer
|
|
end
|
|
|
|
it "validates uniqueness of name" do
|
|
Fabricate(:category_with_definition)
|
|
is_expected.to validate_uniqueness_of(:name).scoped_to(:parent_category_id).case_insensitive
|
|
end
|
|
|
|
it "validates inclusion of search_priority" do
|
|
category = Fabricate.build(:category, user: user)
|
|
|
|
expect(category.valid?).to eq(true)
|
|
|
|
category.search_priority = Searchable::PRIORITIES.values.last + 1
|
|
|
|
expect(category.valid?).to eq(false)
|
|
expect(category.errors.to_hash.keys).to contain_exactly(:search_priority)
|
|
end
|
|
|
|
it "validates uniqueness in case insensitive way" do
|
|
Fabricate(:category_with_definition, name: "Cats")
|
|
cats = Fabricate.build(:category, name: "cats")
|
|
expect(cats).to_not be_valid
|
|
expect(cats.errors[:name]).to be_present
|
|
end
|
|
|
|
describe "Associations" do
|
|
it { is_expected.to have_one(:category_setting).dependent(:destroy) }
|
|
|
|
it "automatically creates a category setting" do
|
|
expect { Fabricate(:category) }.to change { CategorySetting.count }.by(1)
|
|
end
|
|
|
|
it "should delete associated sidebar_section_links when category is destroyed" do
|
|
category_sidebar_section_link = Fabricate(:category_sidebar_section_link)
|
|
category_sidebar_section_link_2 =
|
|
Fabricate(:category_sidebar_section_link, linkable: category_sidebar_section_link.linkable)
|
|
tag_sidebar_section_link = Fabricate(:tag_sidebar_section_link)
|
|
|
|
expect { category_sidebar_section_link.linkable.destroy! }.to change {
|
|
SidebarSectionLink.count
|
|
}.from(3).to(1)
|
|
expect(SidebarSectionLink.first).to eq(tag_sidebar_section_link)
|
|
end
|
|
end
|
|
|
|
describe "slug" do
|
|
it "converts to lower" do
|
|
category = Category.create!(name: "Hello World", slug: "Hello-World", user: user)
|
|
expect(category.slug).to eq("hello-world")
|
|
end
|
|
end
|
|
|
|
describe "resolve_permissions" do
|
|
it "can determine read_restricted" do
|
|
read_restricted, resolved = Category.resolve_permissions(everyone: :full)
|
|
|
|
expect(read_restricted).to be false
|
|
expect(resolved).to be_blank
|
|
end
|
|
end
|
|
|
|
describe "permissions_params" do
|
|
it "returns the right group names and permission type" do
|
|
category = Fabricate(:category_with_definition)
|
|
group = Fabricate(:group)
|
|
category_group = Fabricate(:category_group, category: category, group: group)
|
|
expect(category.permissions_params).to eq("#{group.name}" => category_group.permission_type)
|
|
end
|
|
end
|
|
|
|
describe "#review_group_id" do
|
|
fab!(:group) { Fabricate(:group) }
|
|
fab!(:category) { Fabricate(:category_with_definition, reviewable_by_group: group) }
|
|
fab!(:topic) { Fabricate(:topic, category: category) }
|
|
fab!(:post) { Fabricate(:post, topic: topic) }
|
|
fab!(:user) { Fabricate(:user) }
|
|
|
|
it "will add the group to the reviewable" do
|
|
SiteSetting.enable_category_group_moderation = true
|
|
reviewable = PostActionCreator.spam(user, post).reviewable
|
|
expect(reviewable.reviewable_by_group_id).to eq(group.id)
|
|
end
|
|
|
|
it "will add the group to the reviewable even if created manually" do
|
|
SiteSetting.enable_category_group_moderation = true
|
|
reviewable =
|
|
ReviewableFlaggedPost.create!(
|
|
created_by: user,
|
|
payload: {
|
|
raw: "test raw",
|
|
},
|
|
category: category,
|
|
)
|
|
expect(reviewable.reviewable_by_group_id).to eq(group.id)
|
|
end
|
|
|
|
it "will not add add the group to the reviewable" do
|
|
SiteSetting.enable_category_group_moderation = false
|
|
reviewable = PostActionCreator.spam(user, post).reviewable
|
|
expect(reviewable.reviewable_by_group_id).to be_nil
|
|
end
|
|
|
|
it "will nullify the group_id if destroyed" do
|
|
reviewable = PostActionCreator.spam(user, post).reviewable
|
|
group.destroy
|
|
expect(category.reload.reviewable_by_group).to be_blank
|
|
expect(reviewable.reload.reviewable_by_group_id).to be_blank
|
|
end
|
|
|
|
it "will remove the reviewable_by_group if the category is updated" do
|
|
SiteSetting.enable_category_group_moderation = true
|
|
reviewable = PostActionCreator.spam(user, post).reviewable
|
|
category.reviewable_by_group_id = nil
|
|
category.save!
|
|
expect(reviewable.reload.reviewable_by_group_id).to be_nil
|
|
end
|
|
end
|
|
|
|
describe "topic_create_allowed and post_create_allowed" do
|
|
fab!(:group) { Fabricate(:group) }
|
|
|
|
fab!(:user) do
|
|
user = Fabricate(:user)
|
|
group.add(user)
|
|
group.save
|
|
user
|
|
end
|
|
|
|
fab!(:admin) { Fabricate(:admin) }
|
|
|
|
fab!(:default_category) { Fabricate(:category_with_definition) }
|
|
|
|
fab!(:full_category) do
|
|
c = Fabricate(:category_with_definition)
|
|
c.set_permissions(group => :full)
|
|
c.save
|
|
c
|
|
end
|
|
|
|
fab!(:can_post_category) do
|
|
c = Fabricate(:category_with_definition)
|
|
c.set_permissions(group => :create_post)
|
|
c.save
|
|
c
|
|
end
|
|
|
|
fab!(:can_read_category) do
|
|
c = Fabricate(:category_with_definition)
|
|
c.set_permissions(group => :readonly)
|
|
c.save
|
|
end
|
|
|
|
let(:user_guardian) { Guardian.new(user) }
|
|
let(:admin_guardian) { Guardian.new(admin) }
|
|
let(:anon_guardian) { Guardian.new(nil) }
|
|
|
|
context "when disabling uncategorized" do
|
|
before { SiteSetting.allow_uncategorized_topics = false }
|
|
|
|
it "allows everything to admins unconditionally" do
|
|
count = Category.count
|
|
|
|
expect(Category.topic_create_allowed(admin_guardian).count).to eq(count)
|
|
expect(Category.post_create_allowed(admin_guardian).count).to eq(count)
|
|
expect(Category.secured(admin_guardian).count).to eq(count)
|
|
end
|
|
|
|
it "allows normal users correct access to all categories" do
|
|
# Sam: I am mixed here, once disabling uncategorized maybe users should no
|
|
# longer be allowed to know about it so all counts should go down?
|
|
expect(Category.secured(user_guardian).count).to eq(5)
|
|
expect(Category.post_create_allowed(user_guardian).count).to eq(4)
|
|
expect(Category.topic_create_allowed(user_guardian).count).to eq(2)
|
|
end
|
|
end
|
|
|
|
it "allows everything to admins unconditionally" do
|
|
count = Category.count
|
|
|
|
expect(Category.topic_create_allowed(admin_guardian).count).to eq(count)
|
|
expect(Category.post_create_allowed(admin_guardian).count).to eq(count)
|
|
expect(Category.secured(admin_guardian).count).to eq(count)
|
|
end
|
|
|
|
it "allows normal users correct access to all categories" do
|
|
expect(Category.secured(user_guardian).count).to eq(5)
|
|
expect(Category.post_create_allowed(user_guardian).count).to eq(4)
|
|
expect(Category.topic_create_allowed(user_guardian).count).to eq(3)
|
|
end
|
|
|
|
it "allows anon correct access" do
|
|
expect(Category.scoped_to_permissions(anon_guardian, [:readonly]).count).to eq(2)
|
|
expect(Category.post_create_allowed(anon_guardian).count).to eq(0)
|
|
expect(Category.topic_create_allowed(anon_guardian).count).to eq(0)
|
|
|
|
# nil has special semantics
|
|
expect(Category.scoped_to_permissions(nil, [:readonly]).count).to eq(2)
|
|
end
|
|
|
|
it "handles :everyone scope" do
|
|
can_post_category.set_permissions(everyone: :create_post)
|
|
can_post_category.save
|
|
|
|
expect(Category.post_create_allowed(user_guardian).count).to eq(4)
|
|
|
|
# anonymous has permission to create no topics
|
|
expect(Category.scoped_to_permissions(user_guardian, [:readonly]).count).to eq(3)
|
|
end
|
|
end
|
|
|
|
describe "security" do
|
|
fab!(:category) { Fabricate(:category_with_definition) }
|
|
fab!(:category_2) { Fabricate(:category_with_definition) }
|
|
fab!(:user) { Fabricate(:user) }
|
|
fab!(:group) { Fabricate(:group) }
|
|
|
|
it "secures categories correctly" do
|
|
expect(category.read_restricted?).to be false
|
|
|
|
category.set_permissions({})
|
|
expect(category.read_restricted?).to be true
|
|
|
|
category.set_permissions(everyone: :full)
|
|
expect(category.read_restricted?).to be false
|
|
|
|
expect(user.secure_categories).to be_empty
|
|
|
|
group.add(user)
|
|
group.save
|
|
|
|
category.set_permissions(group.id => :full)
|
|
category.save
|
|
|
|
user.reload
|
|
expect(user.secure_categories).to eq([category])
|
|
end
|
|
|
|
it "lists all secured categories correctly" do
|
|
uncategorized = Category.find(SiteSetting.uncategorized_category_id)
|
|
|
|
group.add(user)
|
|
category.set_permissions(group.id => :full)
|
|
category.save!
|
|
category_2.set_permissions(group.id => :full)
|
|
category_2.save!
|
|
|
|
expect(Category.secured).to match_array([uncategorized])
|
|
expect(Category.secured(Guardian.new(user))).to match_array(
|
|
[uncategorized, category, category_2],
|
|
)
|
|
end
|
|
end
|
|
|
|
it "strips leading blanks" do
|
|
expect(Fabricate(:category_with_definition, name: " music").name).to eq("music")
|
|
end
|
|
|
|
it "strips trailing blanks" do
|
|
expect(Fabricate(:category_with_definition, name: "bugs ").name).to eq("bugs")
|
|
end
|
|
|
|
it "strips leading and trailing blanks" do
|
|
expect(Fabricate(:category_with_definition, name: " blanks ").name).to eq("blanks")
|
|
end
|
|
|
|
it "sets name_lower" do
|
|
expect(Fabricate(:category_with_definition, name: "Not MySQL").name_lower).to eq("not mysql")
|
|
end
|
|
|
|
it "has custom fields" do
|
|
category = Fabricate(:category_with_definition, name: " music")
|
|
expect(category.custom_fields["a"]).to be_nil
|
|
|
|
category.custom_fields["bob"] = "marley"
|
|
category.custom_fields["jack"] = "black"
|
|
category.save
|
|
|
|
category = Category.find(category.id)
|
|
expect(category.custom_fields).to eq("bob" => "marley", "jack" => "black")
|
|
end
|
|
|
|
describe "short name" do
|
|
fab!(:category) { Fabricate(:category_with_definition, name: "xx") }
|
|
|
|
it "creates the category" do
|
|
expect(category).to be_present
|
|
end
|
|
|
|
it "has one topic" do
|
|
expect(Topic.where(category_id: category.id).count).to eq(1)
|
|
end
|
|
end
|
|
|
|
describe "non-english characters" do
|
|
context "when using ascii slug generator" do
|
|
before do
|
|
SiteSetting.slug_generation_method = "ascii"
|
|
@category = Fabricate(:category_with_definition, name: "测试")
|
|
end
|
|
after { @category.destroy }
|
|
|
|
it "creates a blank slug" do
|
|
expect(@category.slug).to be_blank
|
|
expect(@category.slug_for_url).to eq("#{@category.id}-category")
|
|
end
|
|
end
|
|
|
|
context "when using none slug generator" do
|
|
before do
|
|
SiteSetting.slug_generation_method = "none"
|
|
@category = Fabricate(:category_with_definition, name: "测试")
|
|
end
|
|
after do
|
|
SiteSetting.slug_generation_method = "ascii"
|
|
@category.destroy
|
|
end
|
|
|
|
it "creates a blank slug" do
|
|
expect(@category.slug).to be_blank
|
|
expect(@category.slug_for_url).to eq("#{@category.id}-category")
|
|
end
|
|
end
|
|
|
|
context "when using encoded slug generator" do
|
|
before do
|
|
SiteSetting.slug_generation_method = "encoded"
|
|
@category = Fabricate(:category_with_definition, name: "测试")
|
|
end
|
|
after do
|
|
SiteSetting.slug_generation_method = "ascii"
|
|
@category.destroy
|
|
end
|
|
|
|
it "creates a slug" do
|
|
expect(@category.slug).to eq("%E6%B5%8B%E8%AF%95")
|
|
expect(@category.slug_for_url).to eq("%E6%B5%8B%E8%AF%95")
|
|
end
|
|
|
|
it "keeps the encoded slug after saving" do
|
|
@category.save
|
|
expect(@category.slug).to eq("%E6%B5%8B%E8%AF%95")
|
|
expect(@category.slug_for_url).to eq("%E6%B5%8B%E8%AF%95")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "slug would be a number" do
|
|
let(:category) { Fabricate.build(:category, name: "2") }
|
|
|
|
it "creates a blank slug" do
|
|
expect(category.slug).to be_blank
|
|
expect(category.slug_for_url).to eq("#{category.id}-category")
|
|
end
|
|
end
|
|
|
|
describe "custom slug can be provided" do
|
|
it "can be sanitized" do
|
|
@c = Fabricate(:category_with_definition, name: "Fun Cats", slug: "fun-cats")
|
|
@cat = Fabricate(:category_with_definition, name: "love cats", slug: "love-cats")
|
|
|
|
@c.slug = " invalid slug"
|
|
@c.save
|
|
expect(@c.slug).to eq("invalid-slug")
|
|
|
|
c = Fabricate.build(:category, name: "More Fun Cats", slug: "love-cats")
|
|
expect(c).not_to be_valid
|
|
expect(c.errors[:slug]).to be_present
|
|
|
|
@cat.slug = "#{@c.id}-category"
|
|
expect(@cat).not_to be_valid
|
|
expect(@cat.errors[:slug]).to be_present
|
|
|
|
@cat.slug = "#{@cat.id}-category"
|
|
expect(@cat).to be_valid
|
|
expect(@cat.errors[:slug]).not_to be_present
|
|
end
|
|
|
|
context "if SiteSettings.slug_generation_method = ascii" do
|
|
before { SiteSetting.slug_generation_method = "ascii" }
|
|
|
|
it "fails if slug contains non-ascii characters" do
|
|
c = Fabricate.build(:category, name: "Sem acentuação", slug: "sem-acentuação")
|
|
expect(c).not_to be_valid
|
|
|
|
expect(c.errors[:slug]).to be_present
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "description_text" do
|
|
it "correctly generates text description as needed" do
|
|
c = Category.new
|
|
expect(c.description_text).to be_nil
|
|
c.description = "<hello <a>foo/bar</a>."
|
|
expect(c.description_text).to eq("<hello foo/bar.")
|
|
end
|
|
end
|
|
|
|
describe "after create" do
|
|
before do
|
|
@category = Fabricate(:category_with_definition, name: "Amazing Category")
|
|
@topic = @category.topic
|
|
end
|
|
|
|
it "is created correctly" do
|
|
expect(@category.slug).to eq("amazing-category")
|
|
expect(@category.slug_for_url).to eq(@category.slug)
|
|
|
|
expect(@category.description).to be_blank
|
|
|
|
expect(Topic.where(category_id: @category).count).to eq(1)
|
|
|
|
expect(@topic).to be_present
|
|
|
|
expect(@topic.category).to eq(@category)
|
|
|
|
expect(@topic).to be_visible
|
|
|
|
expect(@topic.pinned_at).to be_present
|
|
|
|
expect(Guardian.new(@category.user).can_delete?(@topic)).to be false
|
|
|
|
expect(@topic.posts.count).to eq(1)
|
|
|
|
expect(@category.topic_url).to be_present
|
|
|
|
expect(@category.posts_week).to eq(0)
|
|
expect(@category.posts_month).to eq(0)
|
|
expect(@category.posts_year).to eq(0)
|
|
|
|
expect(@category.topics_week).to eq(0)
|
|
expect(@category.topics_month).to eq(0)
|
|
expect(@category.topics_year).to eq(0)
|
|
end
|
|
|
|
it "cooks the definition" do
|
|
category =
|
|
Category.create(
|
|
name: "little-test",
|
|
user_id: Discourse.system_user.id,
|
|
description: "click the link [here](https://fakeurl.com)",
|
|
)
|
|
expect(category.description.include?("[here]")).to eq(false)
|
|
expect(category.description).to eq(category.topic.first_post.cooked)
|
|
end
|
|
|
|
it "renames the definition when renamed" do
|
|
@category.update(name: "Troutfishing")
|
|
@topic.reload
|
|
expect(@topic.title).to match(/Troutfishing/)
|
|
expect(@topic.fancy_title).to match(/Troutfishing/)
|
|
end
|
|
|
|
it "doesn't raise an error if there is no definition topic to rename (uncategorized)" do
|
|
expect { @category.update(name: "Troutfishing", topic_id: nil) }.to_not raise_error
|
|
end
|
|
|
|
it "creates permalink when category slug is changed" do
|
|
@category.update(slug: "new-category")
|
|
expect(Permalink.count).to eq(1)
|
|
end
|
|
|
|
it "reuses existing permalink when category slug is changed" do
|
|
permalink = Permalink.create!(url: "c/#{@category.slug}/#{@category.id}", category_id: 42)
|
|
|
|
expect { @category.update(slug: "new-slug") }.to_not change { Permalink.count }
|
|
expect(permalink.reload.category_id).to eq(@category.id)
|
|
end
|
|
|
|
it "creates permalink when sub category slug is changed" do
|
|
sub_category =
|
|
Fabricate(:category_with_definition, slug: "sub-category", parent_category_id: @category.id)
|
|
sub_category.update(slug: "new-sub-category")
|
|
expect(Permalink.count).to eq(1)
|
|
end
|
|
|
|
it "deletes permalink when category slug is reused" do
|
|
Fabricate(:permalink, url: "/c/bikeshed-category")
|
|
Fabricate(:category_with_definition, slug: "bikeshed-category")
|
|
expect(Permalink.count).to eq(0)
|
|
end
|
|
|
|
it "deletes permalink when sub category slug is reused" do
|
|
Fabricate(:permalink, url: "/c/main-category/sub-category")
|
|
main_category = Fabricate(:category_with_definition, slug: "main-category")
|
|
Fabricate(
|
|
:category_with_definition,
|
|
slug: "sub-category",
|
|
parent_category_id: main_category.id,
|
|
)
|
|
expect(Permalink.count).to eq(0)
|
|
end
|
|
|
|
it "correctly creates permalink when category slug is changed in subfolder install" do
|
|
set_subfolder "/forum"
|
|
old_url = @category.url
|
|
@category.update(slug: "new-category")
|
|
permalink = Permalink.last
|
|
expect(permalink.url).to eq(old_url[1..-1])
|
|
end
|
|
|
|
it "should not set its description topic to auto-close" do
|
|
category = Fabricate(:category_with_definition, name: "Closing Topics", auto_close_hours: 1)
|
|
expect(category.topic.public_topic_timer).to eq(nil)
|
|
end
|
|
|
|
describe "creating a new category with the same slug" do
|
|
it "should have a blank slug if at the same level" do
|
|
category = Fabricate(:category_with_definition, name: "Amazing Categóry")
|
|
expect(category.slug).to be_blank
|
|
expect(category.slug_for_url).to eq("#{category.id}-category")
|
|
end
|
|
|
|
it "doesn't have a blank slug if not at the same level" do
|
|
parent = Fabricate(:category_with_definition, name: "Other parent")
|
|
category =
|
|
Fabricate(
|
|
:category_with_definition,
|
|
name: "Amazing Categóry",
|
|
parent_category_id: parent.id,
|
|
)
|
|
expect(category.slug).to eq("amazing-category")
|
|
expect(category.slug_for_url).to eq("amazing-category")
|
|
end
|
|
end
|
|
|
|
describe "trying to change the category topic's category" do
|
|
before do
|
|
@new_cat = Fabricate(:category_with_definition, name: "2nd Category", user: @category.user)
|
|
@topic.change_category_to_id(@new_cat.id)
|
|
@topic.reload
|
|
@category.reload
|
|
end
|
|
|
|
it "does not cause changes" do
|
|
expect(@category.topic_count).to eq(0)
|
|
expect(@topic.category).to eq(@category)
|
|
expect(@category.topic).to eq(@topic)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "new" do
|
|
subject { Fabricate.build(:category, user: Fabricate(:user)) }
|
|
|
|
it "triggers a extensibility event" do
|
|
event = DiscourseEvent.track_events { subject.save! }.last
|
|
|
|
expect(event[:event_name]).to eq(:category_created)
|
|
expect(event[:params].first).to eq(subject)
|
|
end
|
|
end
|
|
|
|
describe "update" do
|
|
it "should enforce uniqueness of slug" do
|
|
Fabricate(:category_with_definition, slug: "the-slug")
|
|
c2 = Fabricate(:category_with_definition, slug: "different-slug")
|
|
c2.slug = "the-slug"
|
|
expect(c2).to_not be_valid
|
|
expect(c2.errors[:slug]).to be_present
|
|
end
|
|
end
|
|
|
|
describe "destroy" do
|
|
before do
|
|
@category = Fabricate(:category_with_definition)
|
|
@category_id = @category.id
|
|
@topic_id = @category.topic_id
|
|
SiteSetting.shared_drafts_category = @category.id.to_s
|
|
end
|
|
|
|
it "is deleted correctly" do
|
|
@category.destroy
|
|
expect(Category.exists?(id: @category_id)).to be false
|
|
expect(Topic.with_deleted.where.not(deleted_at: nil).exists?(id: @topic_id)).to be true
|
|
expect(SiteSetting.shared_drafts_category).to be_blank
|
|
end
|
|
|
|
it "deletes related embeddable host" do
|
|
embeddable_host = Fabricate(:embeddable_host, category: @category)
|
|
@category.destroy!
|
|
expect { embeddable_host.reload }.to raise_error(ActiveRecord::RecordNotFound)
|
|
end
|
|
|
|
it "triggers a extensibility event" do
|
|
event = DiscourseEvent.track(:category_destroyed) { @category.destroy }
|
|
|
|
expect(event[:event_name]).to eq(:category_destroyed)
|
|
expect(event[:params].first).to eq(@category)
|
|
end
|
|
end
|
|
|
|
describe "latest" do
|
|
it "should be updated correctly" do
|
|
category = freeze_time(1.minute.ago) { Fabricate(:category_with_definition) }
|
|
post = create_post(category: category.id, created_at: 15.seconds.ago)
|
|
|
|
category.reload
|
|
expect(category.latest_post_id).to eq(post.id)
|
|
expect(category.latest_topic_id).to eq(post.topic_id)
|
|
|
|
post2 = create_post(category: category.id, created_at: 10.seconds.ago)
|
|
post3 = create_post(topic_id: post.topic_id, category: category.id, created_at: 5.seconds.ago)
|
|
|
|
category.reload
|
|
expect(category.latest_post_id).to eq(post3.id)
|
|
expect(category.latest_topic_id).to eq(post2.topic_id)
|
|
|
|
post3.reload
|
|
|
|
destroyer = PostDestroyer.new(Fabricate(:admin), post3)
|
|
destroyer.destroy
|
|
|
|
category.reload
|
|
expect(category.latest_post_id).to eq(post2.id)
|
|
end
|
|
end
|
|
|
|
describe "update_stats" do
|
|
before { @category = Fabricate(:category_with_definition) }
|
|
|
|
context "with regular topics" do
|
|
before do
|
|
create_post(user: @category.user, category: @category.id)
|
|
Category.update_stats
|
|
@category.reload
|
|
end
|
|
|
|
it "updates topic stats" do
|
|
expect(@category.topics_week).to eq(1)
|
|
expect(@category.topics_month).to eq(1)
|
|
expect(@category.topics_year).to eq(1)
|
|
expect(@category.topic_count).to eq(1)
|
|
expect(@category.post_count).to eq(1)
|
|
expect(@category.posts_year).to eq(1)
|
|
expect(@category.posts_month).to eq(1)
|
|
expect(@category.posts_week).to eq(1)
|
|
end
|
|
end
|
|
|
|
context "with deleted topics" do
|
|
before do
|
|
@category.topics << Fabricate(:deleted_topic, user: @category.user)
|
|
Category.update_stats
|
|
@category.reload
|
|
end
|
|
|
|
it "does not count deleted topics" do
|
|
expect(@category.topics_week).to eq(0)
|
|
expect(@category.topic_count).to eq(0)
|
|
expect(@category.topics_month).to eq(0)
|
|
expect(@category.topics_year).to eq(0)
|
|
expect(@category.post_count).to eq(0)
|
|
expect(@category.posts_year).to eq(0)
|
|
expect(@category.posts_month).to eq(0)
|
|
expect(@category.posts_week).to eq(0)
|
|
end
|
|
end
|
|
|
|
context "with revised post" do
|
|
before do
|
|
post = create_post(user: @category.user, category: @category.id)
|
|
|
|
SiteSetting.editing_grace_period = 1.minute
|
|
post.revise(post.user, { raw: "updated body" }, revised_at: post.updated_at + 2.minutes)
|
|
|
|
Category.update_stats
|
|
@category.reload
|
|
end
|
|
|
|
it "doesn't count each version of a post" do
|
|
expect(@category.post_count).to eq(1)
|
|
expect(@category.posts_year).to eq(1)
|
|
expect(@category.posts_month).to eq(1)
|
|
expect(@category.posts_week).to eq(1)
|
|
end
|
|
end
|
|
|
|
context "for uncategorized category" do
|
|
before do
|
|
@uncategorized = Category.find(SiteSetting.uncategorized_category_id)
|
|
create_post(user: Fabricate(:user), category: @uncategorized.id)
|
|
Category.update_stats
|
|
@uncategorized.reload
|
|
end
|
|
|
|
it "updates topic stats" do
|
|
expect(@uncategorized.topics_week).to eq(1)
|
|
expect(@uncategorized.topics_month).to eq(1)
|
|
expect(@uncategorized.topics_year).to eq(1)
|
|
expect(@uncategorized.topic_count).to eq(1)
|
|
expect(@uncategorized.post_count).to eq(1)
|
|
expect(@uncategorized.posts_year).to eq(1)
|
|
expect(@uncategorized.posts_month).to eq(1)
|
|
expect(@uncategorized.posts_week).to eq(1)
|
|
end
|
|
end
|
|
|
|
context "when there are no topics left" do
|
|
let!(:topic) { create_post(user: @category.user, category: @category.id).reload.topic }
|
|
|
|
it "can update the topic count to zero" do
|
|
@category.reload
|
|
expect(@category.topic_count).to eq(1)
|
|
expect(@category.topics.count).to eq(2)
|
|
topic.delete # Delete so the post trash/destroy hook doesn't fire
|
|
|
|
Category.update_stats
|
|
@category.reload
|
|
expect(@category.topics.count).to eq(1)
|
|
expect(@category.topic_count).to eq(0)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#url" do
|
|
before_all { SiteSetting.max_category_nesting = 3 }
|
|
|
|
fab!(:category) { Fabricate(:category, name: "root") }
|
|
|
|
fab!(:sub_category) { Fabricate(:category, name: "child", parent_category_id: category.id) }
|
|
|
|
fab!(:sub_sub_category) do
|
|
Fabricate(:category, name: "child_of_child", parent_category_id: sub_category.id)
|
|
end
|
|
|
|
describe "for normal categories" do
|
|
it "builds a url" do
|
|
expect(category.url).to eq("/c/root/#{category.id}")
|
|
end
|
|
end
|
|
|
|
describe "for subcategories" do
|
|
it "builds a url" do
|
|
expect(sub_category.url).to eq("/c/root/child/#{sub_category.id}")
|
|
end
|
|
end
|
|
|
|
describe "for sub-sub-categories" do
|
|
it "builds a url" do
|
|
expect(sub_sub_category.url).to eq("/c/root/child/child-of-child/#{sub_sub_category.id}")
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "uncategorized" do
|
|
let(:cat) { Category.where(id: SiteSetting.uncategorized_category_id).first }
|
|
|
|
it "reports as `uncategorized?`" do
|
|
expect(cat).to be_uncategorized
|
|
end
|
|
|
|
it "cannot have a parent category" do
|
|
cat.parent_category_id = Fabricate(:category_with_definition).id
|
|
expect(cat).to_not be_valid
|
|
end
|
|
end
|
|
|
|
describe "parent categories" do
|
|
fab!(:user) { Fabricate(:user) }
|
|
fab!(:parent_category) { Fabricate(:category_with_definition, user: user) }
|
|
|
|
it "can be associated with a parent category" do
|
|
sub_category = Fabricate.build(:category, parent_category_id: parent_category.id, user: user)
|
|
expect(sub_category).to be_valid
|
|
expect(sub_category.parent_category).to eq(parent_category)
|
|
end
|
|
|
|
it "cannot associate a category with itself" do
|
|
category = Fabricate(:category_with_definition, user: user)
|
|
category.parent_category_id = category.id
|
|
expect(category).to_not be_valid
|
|
end
|
|
|
|
it "cannot have a category two levels deep" do
|
|
sub_category =
|
|
Fabricate(:category_with_definition, parent_category_id: parent_category.id, user: user)
|
|
nested_sub_category =
|
|
Fabricate.build(:category, parent_category_id: sub_category.id, user: user)
|
|
expect(nested_sub_category).to_not be_valid
|
|
end
|
|
|
|
describe ".query_parent_category" do
|
|
it "should return the parent category id given a parent slug" do
|
|
parent_category.name = "Amazing Category"
|
|
expect(parent_category.id).to eq(Category.query_parent_category(parent_category.slug))
|
|
end
|
|
end
|
|
|
|
describe ".query_category" do
|
|
it "should return the category" do
|
|
category =
|
|
Fabricate(
|
|
:category_with_definition,
|
|
name: "Amazing Category",
|
|
parent_category_id: parent_category.id,
|
|
user: user,
|
|
)
|
|
parent_category.name = "Amazing Parent Category"
|
|
expect(category).to eq(Category.query_category(category.slug, parent_category.id))
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "find_by_email" do
|
|
it "is case insensitive" do
|
|
c1 = Fabricate(:category_with_definition, email_in: "lower@example.com")
|
|
c2 = Fabricate(:category_with_definition, email_in: "UPPER@EXAMPLE.COM")
|
|
c3 = Fabricate(:category_with_definition, email_in: "Mixed.Case@Example.COM")
|
|
expect(Category.find_by_email("LOWER@EXAMPLE.COM")).to eq(c1)
|
|
expect(Category.find_by_email("upper@example.com")).to eq(c2)
|
|
expect(Category.find_by_email("mixed.case@example.com")).to eq(c3)
|
|
expect(Category.find_by_email("MIXED.CASE@EXAMPLE.COM")).to eq(c3)
|
|
end
|
|
end
|
|
|
|
describe "find_by_slug" do
|
|
fab!(:category) { Fabricate(:category_with_definition, slug: "awesome-category") }
|
|
|
|
fab!(:subcategory) do
|
|
Fabricate(
|
|
:category_with_definition,
|
|
parent_category_id: category.id,
|
|
slug: "awesome-sub-category",
|
|
)
|
|
end
|
|
|
|
it "finds a category that exists" do
|
|
expect(Category.find_by_slug("awesome-category")).to eq(category)
|
|
end
|
|
|
|
it "finds a subcategory that exists" do
|
|
expect(Category.find_by_slug("awesome-sub-category", "awesome-category")).to eq(subcategory)
|
|
end
|
|
|
|
it "produces nil if the parent doesn't exist" do
|
|
expect(Category.find_by_slug("awesome-sub-category", "no-such-category")).to eq(nil)
|
|
end
|
|
|
|
it "produces nil if the parent doesn't exist and the requested category is a root category" do
|
|
expect(Category.find_by_slug("awesome-category", "no-such-category")).to eq(nil)
|
|
end
|
|
|
|
it "produces nil if the subcategory doesn't exist" do
|
|
expect(Category.find_by_slug("no-such-category", "awesome-category")).to eq(nil)
|
|
end
|
|
end
|
|
|
|
describe "validate email_in" do
|
|
fab!(:user) { Fabricate(:user) }
|
|
|
|
it "works with a valid email" do
|
|
expect(Category.new(name: "test", user: user, email_in: "test@example.com").valid?).to eq(
|
|
true,
|
|
)
|
|
end
|
|
|
|
it "adds an error with an invalid email" do
|
|
category = Category.new(name: "test", user: user, email_in: "<sup>test</sup>")
|
|
expect(category.valid?).to eq(false)
|
|
expect(category.errors.full_messages.join).not_to match(/<sup>/)
|
|
end
|
|
|
|
context "with a duplicate email in a group" do
|
|
fab!(:group) { Fabricate(:group, name: "testgroup", incoming_email: "test@example.com") }
|
|
|
|
it "adds an error with an invalid email" do
|
|
category = Category.new(name: "test", user: user, email_in: group.incoming_email)
|
|
expect(category.valid?).to eq(false)
|
|
end
|
|
end
|
|
|
|
context "with duplicate email in a category" do
|
|
fab!(:category) do
|
|
Fabricate(
|
|
:category_with_definition,
|
|
user: user,
|
|
name: "<b>cool</b>",
|
|
email_in: "test@example.com",
|
|
)
|
|
end
|
|
|
|
it "adds an error with an invalid email" do
|
|
category = Category.new(name: "test", user: user, email_in: "test@example.com")
|
|
expect(category.valid?).to eq(false)
|
|
expect(category.errors.full_messages.join).not_to match(/<b>/)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "require topic/post approval" do
|
|
fab!(:category) { Fabricate(:category_with_definition) }
|
|
|
|
describe "#require_topic_approval?" do
|
|
before do
|
|
category.custom_fields[Category::REQUIRE_TOPIC_APPROVAL] = true
|
|
category.save
|
|
end
|
|
|
|
it { expect(category.reload.require_topic_approval?).to eq(true) }
|
|
end
|
|
|
|
describe "#require_reply_approval?" do
|
|
before do
|
|
category.custom_fields[Category::REQUIRE_REPLY_APPROVAL] = true
|
|
category.save
|
|
end
|
|
|
|
it { expect(category.reload.require_reply_approval?).to eq(true) }
|
|
end
|
|
end
|
|
|
|
describe "auto bump" do
|
|
it "should correctly automatically bump topics" do
|
|
freeze_time
|
|
category = Fabricate(:category_with_definition, created_at: 1.minute.ago)
|
|
category.clear_auto_bump_cache!
|
|
|
|
post1 = create_post(category: category, created_at: 15.seconds.ago)
|
|
_post2 = create_post(category: category, created_at: 10.seconds.ago)
|
|
_post3 = create_post(category: category, created_at: 5.seconds.ago)
|
|
|
|
# no limits on post creation or category creation please
|
|
RateLimiter.enable
|
|
|
|
time = freeze_time 1.month.from_now
|
|
|
|
expect(category.auto_bump_topic!).to eq(false)
|
|
expect(Topic.where(bumped_at: time).count).to eq(0)
|
|
|
|
category.num_auto_bump_daily = 2
|
|
category.save!
|
|
|
|
expect(category.auto_bump_topic!).to eq(true)
|
|
expect(Topic.where(bumped_at: time).count).to eq(1)
|
|
# our extra bump message
|
|
expect(post1.topic.reload.posts_count).to eq(2)
|
|
|
|
time = freeze_time 13.hours.from_now
|
|
|
|
expect(category.auto_bump_topic!).to eq(true)
|
|
expect(Topic.where(bumped_at: time).count).to eq(1)
|
|
|
|
expect(category.auto_bump_topic!).to eq(false)
|
|
expect(Topic.where(bumped_at: time).count).to eq(1)
|
|
|
|
time = freeze_time 1.month.from_now
|
|
|
|
category.auto_bump_limiter.clear!
|
|
expect(Category.auto_bump_topic!).to eq(true)
|
|
expect(Topic.where(bumped_at: time).count).to eq(1)
|
|
|
|
category.num_auto_bump_daily = ""
|
|
category.save!
|
|
|
|
expect(Category.auto_bump_topic!).to eq(false)
|
|
end
|
|
|
|
it "should not auto-bump the same topic within the cooldown" do
|
|
freeze_time
|
|
category =
|
|
Fabricate(
|
|
:category_with_definition,
|
|
num_auto_bump_daily: 2,
|
|
created_at: 1.minute.ago,
|
|
category_setting_attributes: {
|
|
auto_bump_cooldown_days: 1,
|
|
},
|
|
)
|
|
category.clear_auto_bump_cache!
|
|
|
|
post1 = create_post(category: category, created_at: 15.seconds.ago)
|
|
|
|
# no limits on post creation or category creation please
|
|
RateLimiter.enable
|
|
|
|
time = freeze_time 1.month.from_now
|
|
|
|
expect(category.auto_bump_topic!).to eq(true)
|
|
expect(Topic.where(bumped_at: time).count).to eq(1)
|
|
|
|
time = freeze_time 13.hours.from_now
|
|
|
|
expect(category.auto_bump_topic!).to eq(false)
|
|
expect(Topic.where(bumped_at: time).count).to eq(0)
|
|
|
|
time = freeze_time 13.hours.from_now
|
|
|
|
expect(category.auto_bump_topic!).to eq(true)
|
|
expect(Topic.where(bumped_at: time).count).to eq(1)
|
|
end
|
|
|
|
it "should not automatically bump topics with a bump scheduled" do
|
|
freeze_time
|
|
category = Fabricate(:category_with_definition, created_at: 1.second.ago)
|
|
category.clear_auto_bump_cache!
|
|
|
|
post1 = create_post(category: category)
|
|
|
|
# no limits on post creation or category creation please
|
|
RateLimiter.enable
|
|
|
|
time = freeze_time 1.month.from_now
|
|
|
|
expect(category.auto_bump_topic!).to eq(false)
|
|
expect(Topic.where(bumped_at: time).count).to eq(0)
|
|
|
|
category.num_auto_bump_daily = 2
|
|
category.save!
|
|
|
|
topic = Topic.find_by_id(post1.topic_id)
|
|
|
|
TopicTimer.create!(
|
|
user_id: -1,
|
|
topic: topic,
|
|
execute_at: 1.hour.from_now,
|
|
status_type: TopicTimer.types[:bump],
|
|
)
|
|
|
|
expect(
|
|
Topic.joins(:topic_timers).where(topic_timers: { status_type: 6, deleted_at: nil }).count,
|
|
).to eq(1)
|
|
|
|
expect(category.auto_bump_topic!).to eq(false)
|
|
expect(Topic.where(bumped_at: time).count).to eq(0)
|
|
# does not include a bump message
|
|
expect(post1.topic.reload.posts_count).to eq(1)
|
|
end
|
|
end
|
|
|
|
describe "validate permissions compatibility" do
|
|
fab!(:admin) { Fabricate(:admin) }
|
|
fab!(:group) { Fabricate(:group) }
|
|
fab!(:group2) { Fabricate(:group) }
|
|
fab!(:parent_category) { Fabricate(:category_with_definition, name: "parent") }
|
|
fab!(:subcategory) do
|
|
Fabricate(:category_with_definition, name: "child1", parent_category_id: parent_category.id)
|
|
end
|
|
|
|
context "when changing subcategory permissions" do
|
|
it "it is not valid if permissions are less restrictive" do
|
|
subcategory.set_permissions(group => :readonly)
|
|
subcategory.save!
|
|
|
|
parent_category.set_permissions(group => :readonly)
|
|
parent_category.save!
|
|
|
|
subcategory.set_permissions(group => :full, group2 => :readonly)
|
|
|
|
expect(subcategory.valid?).to eq(false)
|
|
expect(subcategory.errors.full_messages).to contain_exactly(
|
|
I18n.t("category.errors.permission_conflict", group_names: group2.name),
|
|
)
|
|
end
|
|
|
|
it "is valid if permissions are same or more restrictive" do
|
|
subcategory.set_permissions(group => :full, group2 => :create_post)
|
|
subcategory.save!
|
|
|
|
parent_category.set_permissions(group => :full, group2 => :create_post)
|
|
parent_category.save!
|
|
|
|
subcategory.set_permissions(group => :create_post, group2 => :full)
|
|
|
|
expect(subcategory.valid?).to eq(true)
|
|
end
|
|
|
|
it "is valid if everyone has access to parent category" do
|
|
parent_category.set_permissions(everyone: :readonly)
|
|
parent_category.save!
|
|
|
|
subcategory.set_permissions(group => :create_post, group2 => :create_post)
|
|
|
|
expect(subcategory.valid?).to eq(true)
|
|
end
|
|
end
|
|
|
|
context "when changing parent category permissions" do
|
|
fab!(:subcategory2) do
|
|
Fabricate(:category_with_definition, name: "child2", parent_category_id: parent_category.id)
|
|
end
|
|
|
|
it "is not valid if subcategory permissions are less restrictive" do
|
|
subcategory.set_permissions(group => :create_post)
|
|
subcategory.save!
|
|
subcategory2.set_permissions(group => :create_post, group2 => :create_post)
|
|
subcategory2.save!
|
|
|
|
parent_category.set_permissions(group => :readonly)
|
|
|
|
expect(parent_category.valid?).to eq(false)
|
|
expect(parent_category.errors.full_messages).to contain_exactly(
|
|
I18n.t("category.errors.permission_conflict", group_names: group2.name),
|
|
)
|
|
end
|
|
|
|
it "is not valid if the subcategory has no category groups, but the parent does" do
|
|
parent_category.set_permissions(group => :readonly)
|
|
|
|
expect(parent_category).not_to be_valid
|
|
end
|
|
|
|
it "is valid if subcategory permissions are same or more restrictive" do
|
|
subcategory.set_permissions(group => :create_post)
|
|
subcategory.save!
|
|
subcategory2.set_permissions(group => :create_post, group2 => :create_post)
|
|
subcategory2.save!
|
|
|
|
parent_category.set_permissions(group => :full, group2 => :create_post)
|
|
|
|
expect(parent_category.valid?).to eq(true)
|
|
end
|
|
|
|
it "is valid if everyone has access to parent category" do
|
|
subcategory.set_permissions(group => :create_post)
|
|
subcategory.save
|
|
parent_category.set_permissions(everyone: :readonly)
|
|
|
|
expect(parent_category.valid?).to eq(true)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "tree metrics" do
|
|
fab!(:category) { Category.create!(user: user, name: "foo") }
|
|
|
|
fab!(:subcategory) { Category.create!(user: user, name: "bar", parent_category: category) }
|
|
|
|
context "with a self-parent" do
|
|
before_all { DB.exec(<<-SQL, id: category.id) }
|
|
UPDATE categories
|
|
SET parent_category_id = :id
|
|
WHERE id = :id
|
|
SQL
|
|
|
|
describe "#depth_of_descendants" do
|
|
it "should produce max_depth" do
|
|
expect(category.depth_of_descendants(3)).to eq(3)
|
|
end
|
|
end
|
|
|
|
describe "#height_of_ancestors" do
|
|
it "should produce max_height" do
|
|
expect(category.height_of_ancestors(3)).to eq(3)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with a prospective self-parent" do
|
|
before { category.parent_category_id = category.id }
|
|
|
|
describe "#depth_of_descendants" do
|
|
it "should produce max_depth" do
|
|
expect(category.depth_of_descendants(3)).to eq(3)
|
|
end
|
|
end
|
|
|
|
describe "#height_of_ancestors" do
|
|
it "should produce max_height" do
|
|
expect(category.height_of_ancestors(3)).to eq(3)
|
|
end
|
|
end
|
|
end
|
|
|
|
context "with a prospective loop" do
|
|
before { category.parent_category_id = subcategory.id }
|
|
|
|
describe "#depth_of_descendants" do
|
|
it "should produce max_depth" do
|
|
expect(category.depth_of_descendants(3)).to eq(3)
|
|
end
|
|
end
|
|
|
|
describe "#height_of_ancestors" do
|
|
it "should produce max_height" do
|
|
expect(category.height_of_ancestors(3)).to eq(3)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#depth_of_descendants" do
|
|
it "should be 0 when the category has no descendants" do
|
|
expect(subcategory.depth_of_descendants).to eq(0)
|
|
end
|
|
|
|
it "should be 1 when the category has a descendant" do
|
|
expect(category.depth_of_descendants).to eq(1)
|
|
end
|
|
end
|
|
|
|
describe "#height_of_ancestors" do
|
|
it "should be 0 when the category has no ancestors" do
|
|
expect(category.height_of_ancestors).to eq(0)
|
|
end
|
|
|
|
it "should be 1 when the category has an ancestor" do
|
|
expect(subcategory.height_of_ancestors).to eq(1)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "messageBus" do
|
|
it "does not publish notification level when publishing to /categories" do
|
|
category = Fabricate(:category)
|
|
category.name = "Amazing category"
|
|
messages = MessageBus.track_publish("/categories") { category.save! }
|
|
|
|
expect(messages.length).to eq(1)
|
|
message = messages.first
|
|
|
|
category_hash = message.data[:categories].first
|
|
|
|
expect(category_hash[:name]).to eq(category.name)
|
|
expect(category_hash.key?(:notification_level)).to eq(false)
|
|
end
|
|
end
|
|
|
|
describe "#ensure_consistency!" do
|
|
it "creates category topic" do
|
|
# corrupt a category topic
|
|
uncategorized = Category.find(SiteSetting.uncategorized_category_id)
|
|
uncategorized.create_category_definition
|
|
uncategorized.topic.posts.first.destroy!
|
|
|
|
# make stuff extra broken
|
|
uncategorized.topic.trash!
|
|
|
|
category = Fabricate(:category_with_definition)
|
|
category_destroyed = Fabricate(:category_with_definition)
|
|
category_trashed = Fabricate(:category_with_definition)
|
|
|
|
category_topic_id = category.topic.id
|
|
category_destroyed.topic.destroy!
|
|
category_trashed.topic.trash!
|
|
|
|
Category.ensure_consistency!
|
|
# step one fix corruption
|
|
expect(uncategorized.reload.topic_id).to eq(nil)
|
|
|
|
Category.ensure_consistency!
|
|
# step two don't create a category definition for uncategorized
|
|
expect(uncategorized.reload.topic_id).to eq(nil)
|
|
|
|
expect(category.reload.topic_id).to eq(category_topic_id)
|
|
expect(category_destroyed.reload.topic).to_not eq(nil)
|
|
expect(category_trashed.reload.topic).to_not eq(nil)
|
|
end
|
|
end
|
|
|
|
describe "#find_by_slug_path" do
|
|
it "works for categories with slugs" do
|
|
category = Fabricate(:category, slug: "cat1")
|
|
|
|
expect(Category.find_by_slug_path(["cat1"])).to eq(category)
|
|
end
|
|
|
|
it "works for categories without slugs" do
|
|
SiteSetting.slug_generation_method = "none"
|
|
|
|
category = Fabricate(:category, slug: "cat1")
|
|
|
|
expect(Category.find_by_slug_path(["#{category.id}-category"])).to eq(category)
|
|
end
|
|
|
|
it "works for subcategories with slugs" do
|
|
category = Fabricate(:category, slug: "cat1")
|
|
subcategory = Fabricate(:category, slug: "cat2", parent_category: category)
|
|
|
|
expect(Category.find_by_slug_path(%w[cat1 cat2])).to eq(subcategory)
|
|
end
|
|
|
|
it "works for subcategories without slugs" do
|
|
SiteSetting.slug_generation_method = "none"
|
|
|
|
category = Fabricate(:category, slug: "cat1")
|
|
subcategory = Fabricate(:category, slug: "cat2", parent_category: category)
|
|
|
|
expect(Category.find_by_slug_path(["cat1", "#{subcategory.id}-category"])).to eq(subcategory)
|
|
expect(
|
|
Category.find_by_slug_path(["#{category.id}-category", "#{subcategory.id}-category"]),
|
|
).to eq(subcategory)
|
|
end
|
|
end
|
|
|
|
describe "#cannot_delete_reason" do
|
|
fab!(:admin) { Fabricate(:admin) }
|
|
let(:guardian) { Guardian.new(admin) }
|
|
fab!(:category) { Fabricate(:category) }
|
|
|
|
describe "when category is uncategorized" do
|
|
it "should return the reason" do
|
|
category = Category.find(SiteSetting.uncategorized_category_id)
|
|
|
|
expect(category.cannot_delete_reason).to eq(I18n.t("category.cannot_delete.uncategorized"))
|
|
end
|
|
end
|
|
|
|
describe "when category has subcategories" do
|
|
it "should return the right reason" do
|
|
category.subcategories << Fabricate(:category)
|
|
|
|
expect(category.cannot_delete_reason).to eq(
|
|
I18n.t("category.cannot_delete.has_subcategories"),
|
|
)
|
|
end
|
|
end
|
|
|
|
describe "when category has topics" do
|
|
it "should return the right reason" do
|
|
topic =
|
|
Fabricate(
|
|
:topic,
|
|
title: "</a><script>alert(document.cookie);</script><a>",
|
|
category: category,
|
|
)
|
|
|
|
category.reload
|
|
|
|
expect(category.cannot_delete_reason).to eq(
|
|
I18n.t(
|
|
"category.cannot_delete.topic_exists",
|
|
count: 1,
|
|
topic_link:
|
|
"<a href=\"#{topic.url}\"></a><script>alert(document.cookie);</script><a></a>",
|
|
),
|
|
)
|
|
end
|
|
end
|
|
end
|
|
|
|
describe "#deleting the general category" do
|
|
fab!(:category) { Fabricate(:category) }
|
|
|
|
it "should empty out the general_category_id site_setting" do
|
|
SiteSetting.general_category_id = category.id
|
|
category.destroy
|
|
|
|
expect(SiteSetting.general_category_id).to_not eq(category.id)
|
|
expect(SiteSetting.general_category_id).to be < 1
|
|
end
|
|
end
|
|
end
|