mirror of
https://github.com/discourse/discourse.git
synced 2024-12-02 15:56:31 +08:00
676d5fadab
We recently had a bug which caused auto-bumping to "not work". The problem was that the value had been set to 0.5, which when coerced to an integer turned into 0. So the feature is "working as intended", but there's a possibility of misconfiguration. When looking into this, I noticed that the inputs on the category settings page doesn't have any particular sanitisation in the front-end, and also one or two validations missing in the back-end. This change: - Takes an existing component, NumberField and enhances that by only allowing numeric input, essentially turning it into a managed input using the same approach as our PasswordField. - Changes the numeric inputs on category settings page to use this component. - Adds appropriate min constraints to the fields to disallow out-of-range values. - Adds missing back-end validations to relevant fields.
1316 lines
42 KiB
Ruby
1316 lines
42 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 "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>test</a>."
|
|
expect(c.description_text).to eq("<hello test.")
|
|
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 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
|