# encoding: utf-8
require 'rails_helper'
require_dependency 'post_creator'
describe Category do
let(:user) { Fabricate(:user) }
it { is_expected.to validate_presence_of :user_id }
it { is_expected.to validate_presence_of :name }
it 'validates uniqueness of name' do
Fabricate(:category)
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.keys).to contain_exactly(:search_priority)
end
it 'validates uniqueness in case insensitive way' do
Fabricate(:category, name: "Cats")
cats = Fabricate.build(:category, name: "cats")
expect(cats).to_not be_valid
expect(cats.errors[:name]).to be_present
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)
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 "topic_create_allowed and post_create_allowed" do
it "works" do
# NOTE we also have the uncategorized category ... hence the increased count
_default_category = Fabricate(:category)
full_category = Fabricate(:category)
can_post_category = Fabricate(:category)
can_read_category = Fabricate(:category)
user = Fabricate(:user)
group = Fabricate(:group)
group.add(user)
group.save
admin = Fabricate(:admin)
full_category.set_permissions(group => :full)
full_category.save
can_post_category.set_permissions(group => :create_post)
can_post_category.save
can_read_category.set_permissions(group => :readonly)
can_read_category.save
guardian = Guardian.new(admin)
expect(Category.topic_create_allowed(guardian).count).to be(5)
expect(Category.post_create_allowed(guardian).count).to be(5)
expect(Category.secured(guardian).count).to be(5)
guardian = Guardian.new(user)
expect(Category.secured(guardian).count).to be(5)
expect(Category.post_create_allowed(guardian).count).to be(4)
expect(Category.topic_create_allowed(guardian).count).to be(3) # explicitly allowed once, default allowed once
expect(Category.scoped_to_permissions(nil, [:readonly]).count).to be(2)
# everyone has special semantics, test it as well
can_post_category.set_permissions(everyone: :create_post)
can_post_category.save
expect(Category.post_create_allowed(guardian).count).to be(4)
# anonymous has permission to create no topics
guardian = Guardian.new(nil)
expect(Category.post_create_allowed(guardian).count).to be(0)
expect(Category.topic_create_allowed(guardian).count).to be(0)
expect(Category.scoped_to_permissions(guardian, [:readonly]).count).to be(3)
end
end
describe "security" do
let(:category) { Fabricate(:category) }
let(:category_2) { Fabricate(:category) }
let(:user) { Fabricate(:user) }
let(: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, name: " music").name).to eq("music")
end
it "strips trailing blanks" do
expect(Fabricate(:category, name: "bugs ").name).to eq("bugs")
end
it "strips leading and trailing blanks" do
expect(Fabricate(:category, name: " blanks ").name).to eq("blanks")
end
it "sets name_lower" do
expect(Fabricate(:category, name: "Not MySQL").name_lower).to eq("not mysql")
end
it "has custom fields" do
category = Fabricate(:category, 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
let!(:category) { Fabricate(:category, 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 'uses ascii slug generator' do
before do
SiteSetting.slug_generation_method = 'ascii'
@category = Fabricate(:category, 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 'uses none slug generator' do
before do
SiteSetting.slug_generation_method = 'none'
@category = Fabricate(:category, 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 'uses encoded slug generator' do
before do
SiteSetting.slug_generation_method = 'encoded'
@category = Fabricate(:category, name: "测试")
end
after do
SiteSetting.slug_generation_method = 'ascii'
@category.destroy
end
it "creates a slug" do
expect(@category.slug).to eq("测试")
expect(@category.slug_for_url).to eq("测试")
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, name: "Fun Cats", slug: "fun-cats")
@cat = Fabricate(:category, 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
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 test."
expect(c.description_text).to eq("test')
expect(category.valid?).to eq(false)
expect(category.errors.full_messages.join).not_to match(//)
end
context "with a duplicate email in a group" do
let(: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
let!(:category) { Fabricate(:category, user: user, name: 'cool', email_in: 'test@example.com') }
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(//)
end
end
end
describe 'require topic/post approval' do
let(:category) { Fabricate(:category) }
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
after do
RateLimiter.disable
end
it 'should correctly automatically bump topics' do
freeze_time 1.second.ago
category = Fabricate(:category)
category.clear_auto_bump_cache!
freeze_time 1.second.from_now
post1 = create_post(category: category)
freeze_time 1.second.from_now
_post2 = create_post(category: category)
freeze_time 1.second.from_now
_post3 = create_post(category: category)
# no limits on post creation or category creation please
RateLimiter.enable
time = 1.month.from_now
freeze_time time
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 = time + 13.hours
freeze_time time
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 = 1.month.from_now
freeze_time time
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
end
describe "validate permissions compatibility" do
let(:admin) { Fabricate(:admin) }
let(:group) { Fabricate(:group) }
let(:group2) { Fabricate(:group) }
let(:parent_category) { Fabricate(:category, name: "parent") }
let(:subcategory) { Fabricate(:category, name: "child1", parent_category_id: parent_category.id) }
let(:subcategory2) { Fabricate(:category, name: "child2", parent_category_id: parent_category.id) }
context "when changing subcategory permissions" do
it "it is not valid if permissions are less restrictive" do
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
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
it "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 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
end