2013-02-06 15:47:36 -05:00
# encoding: UTF-8
2013-02-05 14:16:51 -05:00
require 'spec_helper'
2013-03-18 17:52:29 -04:00
require_dependency 'post_destroyer'
2013-02-05 14:16:51 -05:00
describe Topic do
it { should validate_presence_of :title }
it { should belong_to :category }
2013-02-25 19:42:20 +03:00
it { should belong_to :user }
it { should belong_to :last_poster }
2013-02-05 14:16:51 -05:00
it { should belong_to :featured_user1 }
it { should belong_to :featured_user2 }
it { should belong_to :featured_user3 }
it { should belong_to :featured_user4 }
it { should have_many :posts }
it { should have_many :topic_users }
2013-02-25 19:42:20 +03:00
it { should have_many :topic_links }
2013-02-05 14:16:51 -05:00
it { should have_many :topic_allowed_users }
it { should have_many :allowed_users }
it { should have_many :invites }
it { should rate_limit }
2013-02-08 08:17:05 -05:00
it_behaves_like " a versioned model "
2013-02-14 17:13:03 -05:00
context 'slug' do
let ( :title ) { " hello world topic " }
let ( :slug ) { " hello-world-slug " }
it " returns a Slug for a title " do
Slug . expects ( :for ) . with ( title ) . returns ( slug )
Fabricate . build ( :topic , title : title ) . slug . should == slug
end
it " returns 'topic' when the slug is empty (say, non-english chars) " do
Slug . expects ( :for ) . with ( title ) . returns ( " " )
Fabricate . build ( :topic , title : title ) . slug . should == " topic "
end
end
2013-02-05 14:16:51 -05:00
context 'topic title uniqueness' do
let! ( :topic ) { Fabricate ( :topic ) }
let ( :new_topic ) { Fabricate . build ( :topic , title : topic . title ) }
context " when duplicates aren't allowed " do
before do
SiteSetting . expects ( :allow_duplicate_topic_titles? ) . returns ( false )
end
it " won't allow another topic to be created with the same name " do
new_topic . should_not be_valid
end
it " won't allow another topic with an upper case title to be created " do
new_topic . title = new_topic . title . upcase
new_topic . should_not be_valid
end
it " allows it when the topic is deleted " do
topic . destroy
new_topic . should be_valid
end
it " allows a private message to be created with the same topic " do
new_topic . archetype = Archetype . private_message
new_topic . should be_valid
end
end
context " when duplicates are allowed " do
before do
SiteSetting . expects ( :allow_duplicate_topic_titles? ) . returns ( true )
end
2013-05-22 21:52:12 -07:00
it " will allow another topic to be created with the same name " do
2013-02-05 14:16:51 -05:00
new_topic . should be_valid
end
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
end
2013-02-19 16:08:23 -05:00
context 'html in title' do
2013-04-22 13:48:05 +10:00
def build_topic_with_title ( title )
2013-05-22 21:52:12 -07:00
build ( :topic , title : title ) . tap { | t | t . valid? }
2013-04-22 13:48:05 +10:00
end
let ( :topic_bold ) { build_topic_with_title ( " Topic with <b>bold</b> text in its title " ) }
let ( :topic_image ) { build_topic_with_title ( " Topic with <img src='something'> image in its title " ) }
let ( :topic_script ) { build_topic_with_title ( " Topic with <script>alert('title')</script> script in its title " ) }
2013-02-19 16:08:23 -05:00
2013-03-06 11:36:42 -05:00
it " escapes script contents " do
2013-04-10 11:00:50 +02:00
topic_script . title . should == " Topic with script in its title "
2013-03-06 11:36:42 -05:00
end
it " escapes bold contents " do
2013-04-10 11:00:50 +02:00
topic_bold . title . should == " Topic with bold text in its title "
2013-03-06 11:36:42 -05:00
end
2013-04-10 11:00:50 +02:00
it " escapes image contents " do
topic_image . title . should == " Topic with image in its title "
2013-02-19 16:08:23 -05:00
end
end
context 'fancy title' do
2013-04-22 13:48:05 +10:00
let ( :topic ) { Fabricate . build ( :topic , title : " \" this topic \" -- has ``fancy stuff'' " ) }
2013-02-19 16:08:23 -05:00
context 'title_fancy_entities disabled' do
before do
SiteSetting . stubs ( :title_fancy_entities ) . returns ( false )
end
it " doesn't change the title to add entities " do
topic . fancy_title . should == topic . title
end
end
context 'title_fancy_entities enabled' do
before do
SiteSetting . stubs ( :title_fancy_entities ) . returns ( true )
end
it " converts the title to have fancy entities " do
topic . fancy_title . should == " “this topic” – has “fancy stuff” "
end
end
end
2013-02-05 14:16:51 -05:00
2013-03-14 14:45:29 -04:00
context 'similar_to' do
it 'returns blank with nil params' do
Topic . similar_to ( nil , nil ) . should be_blank
end
context 'with a similar topic' do
let! ( :topic ) { Fabricate ( :topic , title : " Evil trout is the dude who posted this topic " ) }
it 'returns the similar topic if the title is similar' do
Topic . similar_to ( " has evil trout made any topics? " , " i am wondering has evil trout made any topics? " ) . should == [ topic ]
end
end
end
2013-02-05 14:16:51 -05:00
context 'post_numbers' do
let! ( :topic ) { Fabricate ( :topic ) }
let! ( :p1 ) { Fabricate ( :post , topic : topic , user : topic . user ) }
let! ( :p2 ) { Fabricate ( :post , topic : topic , user : topic . user ) }
let! ( :p3 ) { Fabricate ( :post , topic : topic , user : topic . user ) }
it " returns the post numbers of the topic " do
topic . post_numbers . should == [ 1 , 2 , 3 ]
p2 . destroy
2013-04-22 13:48:05 +10:00
topic . reload
2013-02-05 14:16:51 -05:00
topic . post_numbers . should == [ 1 , 3 ]
end
end
context 'move_posts' do
let ( :user ) { Fabricate ( :user ) }
2013-03-12 12:33:42 -04:00
let ( :another_user ) { Fabricate ( :evil_trout ) }
2013-02-05 14:16:51 -05:00
let ( :category ) { Fabricate ( :category , user : user ) }
2013-02-25 19:42:20 +03:00
let! ( :topic ) { Fabricate ( :topic , user : user , category : category ) }
2013-02-05 14:16:51 -05:00
let! ( :p1 ) { Fabricate ( :post , topic : topic , user : user ) }
2013-03-12 12:33:42 -04:00
let! ( :p2 ) { Fabricate ( :post , topic : topic , user : another_user ) }
2013-02-05 14:16:51 -05:00
let! ( :p3 ) { Fabricate ( :post , topic : topic , user : user ) }
let! ( :p4 ) { Fabricate ( :post , topic : topic , user : user ) }
2013-03-12 12:33:42 -04:00
before do
# add a like to a post
PostAction . act ( another_user , p4 , PostActionType . types [ :like ] )
end
2013-02-05 14:16:51 -05:00
context 'success' do
it " enqueues a job to notify users " do
topic . stubs ( :add_moderator_post )
2013-05-13 14:06:16 -04:00
Jobs . expects ( :enqueue ) . with ( :notify_moved_posts , post_ids : [ p2 . id , p4 . id ] , moved_by_id : user . id )
topic . move_posts ( user , [ p2 . id , p4 . id ] , title : " new testing topic name " )
2013-02-05 14:16:51 -05:00
end
it " adds a moderator post at the location of the first moved post " do
topic . expects ( :add_moderator_post ) . with ( user , instance_of ( String ) , has_entries ( post_number : 2 ) )
2013-05-08 13:33:58 -04:00
topic . move_posts ( user , [ p2 . id , p4 . id ] , title : " new testing topic name " )
2013-02-05 14:16:51 -05:00
end
end
context " errors " do
it " raises an error when one of the posts doesn't exist " do
2013-05-08 13:33:58 -04:00
lambda { topic . move_posts ( user , [ 1003 ] , title : " new testing topic name " ) } . should raise_error ( Discourse :: InvalidParameters )
2013-02-05 14:16:51 -05:00
end
2013-05-25 17:39:05 -07:00
it " raises an error and does not create a topic if no posts were moved " do
Topic . count . tap do | original_topic_count |
lambda {
topic . move_posts ( user , [ ] , title : " new testing topic name " )
} . should raise_error ( Discourse :: InvalidParameters )
2013-02-05 14:16:51 -05:00
2013-05-25 17:39:05 -07:00
expect ( Topic . count ) . to eq original_topic_count
end
end
2013-02-05 14:16:51 -05:00
end
2013-05-08 13:33:58 -04:00
context " successfully moved " do
2013-02-05 14:16:51 -05:00
before do
topic . expects ( :add_moderator_post )
TopicUser . update_last_read ( user , topic . id , p4 . post_number , 0 )
end
2013-02-25 19:42:20 +03:00
2013-05-08 13:33:58 -04:00
context " to a new topic " do
let! ( :new_topic ) { topic . move_posts ( user , [ p2 . id , p4 . id ] , title : " new testing topic name " ) }
2013-05-13 14:06:16 -04:00
it " works correctly " do
2013-05-08 13:33:58 -04:00
TopicUser . where ( user_id : user . id , topic_id : topic . id ) . first . last_read_post_number . should == p3 . post_number
new_topic . should be_present
new_topic . featured_user1_id . should == another_user . id
new_topic . like_count . should == 1
new_topic . category . should == category
topic . featured_user1_id . should be_blank
new_topic . posts . should =~ [ p2 , p4 ]
new_topic . reload
new_topic . posts_count . should == 2
new_topic . highest_post_number . should == 2
p2 . reload
p2 . sort_order . should == 1
p2 . post_number . should == 1
p4 . reload
p4 . post_number . should == 2
p4 . sort_order . should == 2
topic . reload
topic . featured_user1_id . should be_blank
topic . like_count . should == 0
topic . posts_count . should == 2
topic . posts . should =~ [ p1 , p3 ]
topic . highest_post_number . should == p3 . post_number
end
end
2013-02-05 14:16:51 -05:00
2013-05-08 13:33:58 -04:00
context " to an existing topic " do
let! ( :destination_topic ) { Fabricate ( :topic , user : user ) }
let! ( :destination_op ) { Fabricate ( :post , topic : destination_topic , user : user ) }
let! ( :moved_to ) { topic . move_posts ( user , [ p2 . id , p4 . id ] , destination_topic_id : destination_topic . id ) }
2013-05-13 14:06:16 -04:00
it " works correctly " do
2013-05-08 13:33:58 -04:00
moved_to . should == destination_topic
# Check out new topic
moved_to . reload
moved_to . posts_count . should == 3
moved_to . highest_post_number . should == 3
moved_to . featured_user1_id . should == another_user . id
moved_to . like_count . should == 1
moved_to . category . should be_blank
# Posts should be re-ordered
p2 . reload
p2 . sort_order . should == 2
p2 . post_number . should == 2
2013-05-13 14:06:16 -04:00
p2 . topic_id . should == moved_to . id
2013-05-08 13:33:58 -04:00
p4 . reload
p4 . post_number . should == 3
p4 . sort_order . should == 3
2013-05-13 14:06:16 -04:00
p4 . topic_id . should == moved_to . id
2013-05-08 13:33:58 -04:00
# Check out the original topic
topic . reload
topic . posts_count . should == 2
topic . highest_post_number . should == 3
topic . featured_user1_id . should be_blank
topic . like_count . should == 0
topic . posts_count . should == 2
topic . posts . should =~ [ p1 , p3 ]
topic . highest_post_number . should == p3 . post_number
# Should update last reads
TopicUser . where ( user_id : user . id , topic_id : topic . id ) . first . last_read_post_number . should == p3 . post_number
end
2013-05-13 14:06:16 -04:00
end
context " moving the first post " do
let! ( :new_topic ) { topic . move_posts ( user , [ p1 . id , p2 . id ] , title : " new testing topic name " ) }
it " copies the OP, doesn't delete it " do
new_topic . should be_present
new_topic . posts . first . raw . should == p1 . raw
new_topic . reload
new_topic . posts_count . should == 2
new_topic . highest_post_number . should == 2
# First post didn't move
p1 . reload
p1 . sort_order . should == 1
p1 . post_number . should == 1
p1 . topic_id == topic . id
# Second post is in a new topic
p2 . reload
p2 . post_number . should == 2
p2 . sort_order . should == 2
p2 . topic_id == new_topic . id
topic . reload
topic . posts . should =~ [ p1 , p3 , p4 ]
topic . highest_post_number . should == p4 . post_number
end
2013-02-05 14:16:51 -05:00
end
2013-05-08 13:33:58 -04:00
2013-05-13 14:06:16 -04:00
2013-02-05 14:16:51 -05:00
end
end
2013-02-25 19:42:20 +03:00
context 'private message' do
let ( :coding_horror ) { User . where ( username : 'CodingHorror' ) . first }
2013-02-05 14:16:51 -05:00
let ( :evil_trout ) { Fabricate ( :evil_trout ) }
2013-05-14 11:59:55 +10:00
let ( :topic ) { Fabricate ( :private_message_topic ) }
2013-02-05 14:16:51 -05:00
2013-04-22 13:48:05 +10:00
it " should integrate correctly " do
2013-02-05 14:16:51 -05:00
Guardian . new ( topic . user ) . can_see? ( topic ) . should be_true
Guardian . new . can_see? ( topic ) . should be_false
Guardian . new ( evil_trout ) . can_see? ( topic ) . should be_false
Guardian . new ( coding_horror ) . can_see? ( topic ) . should be_true
2013-03-27 16:17:49 -04:00
TopicQuery . new ( evil_trout ) . list_latest . topics . should_not include ( topic )
2013-04-22 13:48:05 +10:00
# invites
topic . invite ( topic . user , 'duhhhhh' ) . should be_false
2013-02-05 14:16:51 -05:00
end
context 'invite' do
it " delegates to topic.invite_by_email when the user doesn't exist, but it's an email " do
topic . expects ( :invite_by_email ) . with ( topic . user , 'jake@adventuretime.ooo' )
topic . invite ( topic . user , 'jake@adventuretime.ooo' )
end
context 'existing user' do
let ( :walter ) { Fabricate ( :walter_white ) }
context 'by username' do
it 'adds walter to the allowed users' do
2013-05-14 11:59:55 +10:00
topic . invite ( topic . user , walter . username ) . should be_true
2013-02-05 14:16:51 -05:00
topic . allowed_users . include? ( walter ) . should be_true
end
it 'creates a notification' do
lambda { topic . invite ( topic . user , walter . username ) } . should change ( Notification , :count )
end
end
context 'by email' do
it 'returns true' do
topic . invite ( topic . user , walter . email ) . should be_true
end
it 'adds walter to the allowed users' do
topic . invite ( topic . user , walter . email )
topic . allowed_users . include? ( walter ) . should be_true
end
it 'creates a notification' do
lambda { topic . invite ( topic . user , walter . email ) } . should change ( Notification , :count )
end
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
end
end
2013-02-25 19:42:20 +03:00
context " user actions " do
2013-02-05 14:16:51 -05:00
let ( :actions ) { topic . user . user_actions }
2013-04-22 13:48:05 +10:00
it " should set up actions correctly " do
2013-05-14 11:59:55 +10:00
ActiveRecord :: Base . observers . enable :all
2013-02-05 14:16:51 -05:00
actions . map { | a | a . action_type } . should_not include ( UserAction :: NEW_TOPIC )
actions . map { | a | a . action_type } . should include ( UserAction :: NEW_PRIVATE_MESSAGE )
coding_horror . user_actions . map { | a | a . action_type } . should include ( UserAction :: GOT_PRIVATE_MESSAGE )
end
2013-04-22 13:48:05 +10:00
2013-02-05 14:16:51 -05:00
end
context " other user " do
2013-05-14 11:59:55 +10:00
before do
# let! is weird, this test need a refactor
t = topic
end
2013-03-18 13:55:34 -04:00
let ( :creator ) { PostCreator . new ( topic . user , raw : Fabricate . build ( :post ) . raw , topic_id : topic . id ) }
2013-02-05 14:16:51 -05:00
it " sends the other user an email when there's a new post " do
UserNotifications . expects ( :private_message ) . with ( coding_horror , has_key ( :post ) )
2013-03-18 13:55:34 -04:00
creator . create
2013-02-05 14:16:51 -05:00
end
it " doesn't send the user an email when they have them disabled " do
coding_horror . update_column ( :email_private_messages , false )
UserNotifications . expects ( :private_message ) . with ( coding_horror , has_key ( :post ) ) . never
2013-03-18 13:55:34 -04:00
creator . create
2013-02-05 14:16:51 -05:00
end
end
end
context 'bumping topics' do
before do
@topic = Fabricate ( :topic , bumped_at : 1 . year . ago )
end
it 'updates the bumped_at field when a new post is made' do
2013-04-22 13:48:05 +10:00
@topic . bumped_at . should be_present
2013-02-05 14:16:51 -05:00
lambda {
Fabricate ( :post , topic : @topic , user : @topic . user )
@topic . reload
} . should change ( @topic , :bumped_at )
end
context 'editing posts' do
before do
@earlier_post = Fabricate ( :post , topic : @topic , user : @topic . user )
@last_post = Fabricate ( :post , topic : @topic , user : @topic . user )
@topic . reload
end
it " doesn't bump the topic on an edit to the last post that doesn't result in a new version " do
lambda {
SiteSetting . expects ( :ninja_edit_window ) . returns ( 5 . minutes )
@last_post . revise ( @last_post . user , 'updated contents' , revised_at : @last_post . created_at + 10 . seconds )
@topic . reload
} . should_not change ( @topic , :bumped_at )
end
it " bumps the topic when a new version is made of the last post " do
lambda {
@last_post . revise ( Fabricate ( :moderator ) , 'updated contents' )
@topic . reload
} . should change ( @topic , :bumped_at )
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
it " doesn't bump the topic when a post that isn't the last post receives a new version " do
lambda {
@earlier_post . revise ( Fabricate ( :moderator ) , 'updated contents' )
@topic . reload
} . should_not change ( @topic , :bumped_at )
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
end
end
context 'moderator posts' do
before do
@moderator = Fabricate ( :moderator )
@topic = Fabricate ( :topic )
@mod_post = @topic . add_moderator_post ( @moderator , " Moderator did something. http://discourse.org " , post_number : 999 )
end
it 'creates a moderator post' do
2013-02-25 19:42:20 +03:00
@mod_post . should be_present
2013-03-18 16:03:46 -04:00
@mod_post . post_type . should == Post . types [ :moderator_action ]
2013-02-05 14:16:51 -05:00
@mod_post . post_number . should == 999
@mod_post . sort_order . should == 999
@topic . topic_links . count . should == 1
2013-04-22 13:48:05 +10:00
@topic . reload
@topic . moderator_posts_count . should == 1
2013-02-05 14:16:51 -05:00
end
end
context 'update_status' do
before do
@topic = Fabricate ( :topic , bumped_at : 1 . hour . ago )
@topic . reload
@original_bumped_at = @topic . bumped_at . to_f
@user = @topic . user
2013-03-29 02:38:54 -04:00
@user . admin = true
2013-02-05 14:16:51 -05:00
end
context 'visibility' do
context 'disable' do
2013-02-25 19:42:20 +03:00
before do
2013-02-05 14:16:51 -05:00
@topic . update_status ( 'visible' , false , @user )
@topic . reload
end
2013-04-22 13:48:05 +10:00
it 'should not be visible and have correct counts' do
2013-02-25 19:42:20 +03:00
@topic . should_not be_visible
2013-02-05 14:16:51 -05:00
@topic . moderator_posts_count . should == 1
@topic . bumped_at . to_f . should == @original_bumped_at
end
end
context 'enable' do
before do
@topic . update_attribute :visible , false
@topic . update_status ( 'visible' , true , @user )
@topic . reload
end
2013-04-22 13:48:05 +10:00
it 'should be visible with correct counts' do
2013-02-25 19:42:20 +03:00
@topic . should be_visible
2013-02-05 14:16:51 -05:00
@topic . moderator_posts_count . should == 1
@topic . bumped_at . to_f . should == @original_bumped_at
2013-02-25 19:42:20 +03:00
end
end
2013-02-05 14:16:51 -05:00
end
context 'pinned' do
context 'disable' do
before do
@topic . update_status ( 'pinned' , false , @user )
@topic . reload
end
2013-04-22 13:48:05 +10:00
it " doesn't have a pinned_at but has correct dates " do
2013-03-06 15:17:07 -05:00
@topic . pinned_at . should be_blank
2013-02-05 14:16:51 -05:00
@topic . moderator_posts_count . should == 1
@topic . bumped_at . to_f . should == @original_bumped_at
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
end
context 'enable' do
before do
2013-03-06 15:17:07 -05:00
@topic . update_attribute :pinned_at , nil
2013-02-05 14:16:51 -05:00
@topic . update_status ( 'pinned' , true , @user )
@topic . reload
end
2013-04-22 13:48:05 +10:00
it 'should enable correctly' do
2013-03-06 15:17:07 -05:00
@topic . pinned_at . should be_present
2013-04-22 13:48:05 +10:00
@topic . bumped_at . to_f . should == @original_bumped_at
2013-02-05 14:16:51 -05:00
@topic . moderator_posts_count . should == 1
end
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
end
context 'archived' do
context 'disable' do
before do
@topic . update_status ( 'archived' , false , @user )
@topic . reload
end
2013-04-22 13:48:05 +10:00
it 'should archive correctly' do
2013-02-25 19:42:20 +03:00
@topic . should_not be_archived
2013-02-05 14:16:51 -05:00
@topic . bumped_at . to_f . should == @original_bumped_at
2013-04-22 13:48:05 +10:00
@topic . moderator_posts_count . should == 1
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
end
context 'enable' do
before do
@topic . update_attribute :archived , false
@topic . update_status ( 'archived' , true , @user )
@topic . reload
end
it 'should be archived' do
2013-02-25 19:42:20 +03:00
@topic . should be_archived
2013-02-05 14:16:51 -05:00
@topic . moderator_posts_count . should == 1
@topic . bumped_at . to_f . should == @original_bumped_at
2013-02-25 19:42:20 +03:00
end
2013-04-22 13:48:05 +10:00
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
end
2013-05-07 14:25:41 -04:00
shared_examples_for 'a status that closes a topic' do
2013-02-05 14:16:51 -05:00
context 'disable' do
before do
2013-05-07 14:25:41 -04:00
@topic . update_status ( status , false , @user )
2013-02-05 14:16:51 -05:00
@topic . reload
end
it 'should not be pinned' do
@topic . should_not be_closed
@topic . moderator_posts_count . should == 1
@topic . bumped_at . to_f . should_not == @original_bumped_at
2013-02-25 19:42:20 +03:00
end
2013-02-05 14:16:51 -05:00
end
context 'enable' do
before do
@topic . update_attribute :closed , false
2013-05-07 14:25:41 -04:00
@topic . update_status ( status , true , @user )
2013-02-05 14:16:51 -05:00
@topic . reload
end
it 'should be closed' do
2013-02-25 19:42:20 +03:00
@topic . should be_closed
2013-02-05 14:16:51 -05:00
@topic . bumped_at . to_f . should == @original_bumped_at
2013-04-22 13:48:05 +10:00
@topic . moderator_posts_count . should == 1
2013-02-25 19:42:20 +03:00
end
end
2013-02-05 14:16:51 -05:00
end
2013-05-07 14:25:41 -04:00
context 'closed' do
let ( :status ) { 'closed' }
it_should_behave_like 'a status that closes a topic'
end
context 'autoclosed' do
let ( :status ) { 'autoclosed' }
it_should_behave_like 'a status that closes a topic'
2013-05-27 17:59:43 -07:00
it 'puts the autoclose duration in the moderator post' do
@topic . created_at = 3 . days . ago
@topic . update_status ( status , true , @user )
expect ( @topic . posts . last . raw ) . to include " closed after 3 days "
end
2013-05-07 14:25:41 -04:00
end
2013-02-05 14:16:51 -05:00
end
describe 'toggle_star' do
2013-04-28 16:58:14 -04:00
shared_examples_for " adding a star to a topic " do
it 'triggers a forum topic user change with true' do
# otherwise no chance the mock will work
freeze_time do
TopicUser . expects ( :change ) . with ( @user , @topic . id , starred : true , starred_at : DateTime . now , unstarred_at : nil )
@topic . toggle_star ( @user , true )
end
end
2013-02-05 14:16:51 -05:00
2013-04-28 16:58:14 -04:00
it 'increases the star_count of the forum topic' do
lambda {
@topic . toggle_star ( @user , true )
@topic . reload
} . should change ( @topic , :star_count ) . by ( 1 )
2013-02-05 14:16:51 -05:00
end
2013-04-28 16:58:14 -04:00
it 'triggers the rate limiter' do
Topic :: FavoriteLimiter . any_instance . expects ( :performed! )
2013-02-25 19:42:20 +03:00
@topic . toggle_star ( @user , true )
2013-04-28 16:58:14 -04:00
end
2013-02-05 14:16:51 -05:00
end
2013-04-28 16:58:14 -04:00
before do
@topic = Fabricate ( :topic )
@user = @topic . user
2013-02-05 14:16:51 -05:00
end
2013-04-28 16:58:14 -04:00
it_should_behave_like " adding a star to a topic "
2013-02-05 14:16:51 -05:00
describe 'removing a star' do
before do
2013-02-25 19:42:20 +03:00
@topic . toggle_star ( @user , true )
@topic . reload
2013-02-05 14:16:51 -05:00
end
it 'rolls back the rate limiter' do
Topic :: FavoriteLimiter . any_instance . expects ( :rollback! )
@topic . toggle_star ( @user , false )
end
it 'triggers a forum topic user change with false' do
2013-04-28 16:58:14 -04:00
freeze_time do
TopicUser . expects ( :change ) . with ( @user , @topic . id , starred : false , unstarred_at : DateTime . now )
@topic . toggle_star ( @user , false )
end
2013-02-05 14:16:51 -05:00
end
it 'reduces the star_count' do
2013-02-25 19:42:20 +03:00
lambda {
@topic . toggle_star ( @user , false )
2013-02-05 14:16:51 -05:00
@topic . reload
2013-02-25 19:42:20 +03:00
} . should change ( @topic , :star_count ) . by ( - 1 )
2013-02-05 14:16:51 -05:00
end
2013-04-28 16:58:14 -04:00
describe 'and adding a star again' do
before do
@topic . toggle_star ( @user , false )
@topic . reload
end
it_should_behave_like " adding a star to a topic "
end
2013-02-05 14:16:51 -05:00
end
end
context 'last_poster info' do
before do
@user = Fabricate ( :user )
@post = Fabricate ( :post , user : @user )
@topic = @post . topic
end
it 'initially has the last_post_user_id of the OP' do
@topic . last_post_user_id . should == @user . id
end
context 'after a second post' do
before do
@second_user = Fabricate ( :coding_horror )
@new_post = Fabricate ( :post , topic : @topic , user : @second_user )
@topic . reload
end
it 'updates the last_post_user_id to the second_user' do
@topic . last_post_user_id . should == @second_user . id
@topic . last_posted_at . to_i . should == @new_post . created_at . to_i
topic_user = @second_user . topic_users . where ( topic_id : @topic . id ) . first
topic_user . posted? . should be_true
end
2013-04-22 13:48:05 +10:00
end
2013-02-05 14:16:51 -05:00
end
describe 'with category' do
before do
@category = Fabricate ( :category )
end
it " should not increase the topic_count with no category " do
lambda { Fabricate ( :topic , user : @category . user ) ; @category . reload } . should_not change ( @category , :topic_count )
end
it " should increase the category's topic_count " do
lambda { Fabricate ( :topic , user : @category . user , category_id : @category . id ) ; @category . reload } . should change ( @category , :topic_count ) . by ( 1 )
end
end
describe 'meta data' do
2013-03-23 20:32:59 +05:30
let ( :topic ) { Fabricate ( :topic , meta_data : { hello : 'world' } ) }
2013-02-05 14:16:51 -05:00
it 'allows us to create a topic with meta data' do
topic . meta_data [ 'hello' ] . should == 'world'
end
context 'updating' do
context 'existing key' do
before do
topic . update_meta_data ( hello : 'bane' )
end
it 'updates the key' do
topic . meta_data [ 'hello' ] . should == 'bane'
end
end
context 'new key' do
before do
topic . update_meta_data ( city : 'gotham' )
end
it 'adds the new key' do
topic . meta_data [ 'city' ] . should == 'gotham'
topic . meta_data [ 'hello' ] . should == 'world'
end
end
end
end
describe 'after create' do
let ( :topic ) { Fabricate ( :topic ) }
it 'is a regular topic by default' do
topic . archetype . should == Archetype . default
2013-02-25 19:42:20 +03:00
topic . has_best_of . should be_false
2013-03-28 13:02:59 -04:00
topic . percent_rank . should == 1 . 0
2013-02-05 14:16:51 -05:00
topic . should be_visible
2013-03-06 15:17:07 -05:00
topic . pinned_at . should be_blank
2013-02-05 14:16:51 -05:00
topic . should_not be_closed
topic . should_not be_archived
topic . moderator_posts_count . should == 0
end
context 'post' do
let ( :post ) { Fabricate ( :post , topic : topic , user : topic . user ) }
it 'has the same archetype as the topic' do
post . archetype . should == topic . archetype
end
end
end
2013-02-25 19:42:20 +03:00
2013-02-05 14:16:51 -05:00
describe 'versions' do
let ( :topic ) { Fabricate ( :topic ) }
it " has version 1 by default " do
topic . version . should == 1
end
context 'changing title' do
before do
2013-02-06 20:09:31 -05:00
topic . title = " new title for the topic "
2013-02-05 14:16:51 -05:00
topic . save
end
it " creates a new version " do
topic . version . should == 2
end
end
context 'changing category' do
let ( :category ) { Fabricate ( :category ) }
before do
topic . change_category ( category . name )
end
it " creates a new version " do
topic . version . should == 2
end
context " removing a category " do
before do
topic . change_category ( nil )
end
it " creates a new version " do
topic . version . should == 3
end
end
end
context 'bumping the topic' do
before do
topic . bumped_at = 10 . minutes . from_now
topic . save
end
it " doesn't craete a new version " do
topic . version . should == 1
end
end
end
describe 'change_category' do
before do
@topic = Fabricate ( :topic )
2013-02-25 19:42:20 +03:00
@category = Fabricate ( :category , user : @topic . user )
@user = @topic . user
2013-02-05 14:16:51 -05:00
end
describe 'without a previous category' do
it 'should not change the topic_count when not changed' do
lambda { @topic . change_category ( nil ) ; @category . reload } . should_not change ( @category , :topic_count )
end
describe 'changed category' do
before do
@topic . change_category ( @category . name )
@category . reload
end
2013-02-25 19:42:20 +03:00
it 'changes the category' do
2013-02-05 14:16:51 -05:00
@topic . category . should == @category
@category . topic_count . should == 1
end
end
it " doesn't change the category when it can't be found " do
@topic . change_category ( 'made up' )
@topic . category . should be_blank
end
end
describe 'with a previous category' do
before do
@topic . change_category ( @category . name )
@topic . reload
@category . reload
end
it 'increases the topic_count' do
@category . topic_count . should == 1
end
it " doesn't change the topic_count when the value doesn't change " do
lambda { @topic . change_category ( @category . name ) ; @category . reload } . should_not change ( @category , :topic_count )
end
it " doesn't reset the category when given a name that doesn't exist " do
@topic . change_category ( 'made up' )
@topic . category_id . should be_present
end
describe 'to a different category' do
before do
@new_category = Fabricate ( :category , user : @user , name : '2nd category' )
@topic . change_category ( @new_category . name )
@topic . reload
@new_category . reload
@category . reload
end
it " should increase the new category's topic count " do
@new_category . topic_count . should == 1
end
it " should lower the original category's topic count " do
@category . topic_count . should == 0
end
2013-02-25 19:42:20 +03:00
2013-02-05 14:16:51 -05:00
end
describe 'when the category exists' do
before do
@topic . change_category ( nil )
2013-02-25 19:42:20 +03:00
@category . reload
2013-02-05 14:16:51 -05:00
end
2013-02-25 19:42:20 +03:00
it " resets the category " do
2013-02-05 14:16:51 -05:00
@topic . category_id . should be_blank
@category . topic_count . should == 0
end
end
end
end
2013-02-27 19:36:12 -08:00
describe 'scopes' do
describe '#by_most_recently_created' do
it 'returns topics ordered by created_at desc, id desc' do
now = Time . now
a = Fabricate ( :topic , created_at : now - 2 . minutes )
b = Fabricate ( :topic , created_at : now )
c = Fabricate ( :topic , created_at : now )
d = Fabricate ( :topic , created_at : now - 2 . minutes )
Topic . by_newest . should == [ c , b , d , a ]
end
end
end
2013-05-07 14:25:41 -04:00
describe 'auto-close' do
context 'a new topic' do
context 'auto_close_at is set' do
it 'queues a job to close the topic' do
Timecop . freeze ( Time . zone . now ) do
Jobs . expects ( :enqueue_at ) . with ( 7 . days . from_now , :close_topic , all_of ( has_key ( :topic_id ) , has_key ( :user_id ) ) )
Fabricate ( :topic , auto_close_at : 7 . days . from_now , user : Fabricate ( :admin ) )
end
end
it 'when auto_close_user_id is nil, it will use the topic creator as the topic closer' do
topic_creator = Fabricate ( :admin )
Jobs . expects ( :enqueue_at ) . with do | datetime , job_name , job_args |
job_args [ :user_id ] == topic_creator . id
end
Fabricate ( :topic , auto_close_at : 7 . days . from_now , user : topic_creator )
end
it 'when auto_close_user_id is set, it will use it as the topic closer' do
topic_creator = Fabricate ( :admin )
topic_closer = Fabricate ( :user , admin : true )
Jobs . expects ( :enqueue_at ) . with do | datetime , job_name , job_args |
job_args [ :user_id ] == topic_closer . id
end
Fabricate ( :topic , auto_close_at : 7 . days . from_now , auto_close_user : topic_closer , user : topic_creator )
end
2013-05-15 15:19:41 -04:00
it " ignores the category's default auto-close " do
Timecop . freeze ( Time . zone . now ) do
Jobs . expects ( :enqueue_at ) . with ( 7 . days . from_now , :close_topic , all_of ( has_key ( :topic_id ) , has_key ( :user_id ) ) )
Fabricate ( :topic , auto_close_at : 7 . days . from_now , user : Fabricate ( :admin ) , category : Fabricate ( :category , auto_close_days : 2 ) )
end
end
2013-05-07 14:25:41 -04:00
end
end
context 'an existing topic' do
it 'when auto_close_at is set, it queues a job to close the topic' do
Timecop . freeze ( Time . zone . now ) do
topic = Fabricate ( :topic )
Jobs . expects ( :enqueue_at ) . with ( 12 . hours . from_now , :close_topic , has_entries ( topic_id : topic . id , user_id : topic . user_id ) )
topic . auto_close_at = 12 . hours . from_now
topic . save . should be_true
end
end
it 'when auto_close_at and auto_closer_user_id are set, it queues a job to close the topic' do
Timecop . freeze ( Time . zone . now ) do
topic = Fabricate ( :topic )
closer = Fabricate ( :admin )
Jobs . expects ( :enqueue_at ) . with ( 12 . hours . from_now , :close_topic , has_entries ( topic_id : topic . id , user_id : closer . id ) )
topic . auto_close_at = 12 . hours . from_now
topic . auto_close_user = closer
topic . save . should be_true
end
end
it 'when auto_close_at is removed, it cancels the job to close the topic' do
Jobs . stubs ( :enqueue_at ) . returns ( true )
topic = Fabricate ( :topic , auto_close_at : 1 . day . from_now )
Jobs . expects ( :cancel_scheduled_job ) . with ( :close_topic , { topic_id : topic . id } )
topic . auto_close_at = nil
topic . save . should be_true
topic . auto_close_user . should be_nil
end
it 'when auto_close_user is removed, it updates the job' do
Timecop . freeze ( Time . zone . now ) do
Jobs . stubs ( :enqueue_at ) . with ( 1 . day . from_now , :close_topic , anything ) . returns ( true )
topic = Fabricate ( :topic , auto_close_at : 1 . day . from_now , auto_close_user : Fabricate ( :admin ) )
Jobs . expects ( :cancel_scheduled_job ) . with ( :close_topic , { topic_id : topic . id } )
Jobs . expects ( :enqueue_at ) . with ( 1 . day . from_now , :close_topic , has_entries ( topic_id : topic . id , user_id : topic . user_id ) )
topic . auto_close_user = nil
topic . save . should be_true
end
end
it 'when auto_close_at value is changed, it reschedules the job' do
Timecop . freeze ( Time . zone . now ) do
Jobs . stubs ( :enqueue_at ) . returns ( true )
topic = Fabricate ( :topic , auto_close_at : 1 . day . from_now )
Jobs . expects ( :cancel_scheduled_job ) . with ( :close_topic , { topic_id : topic . id } )
Jobs . expects ( :enqueue_at ) . with ( 3 . days . from_now , :close_topic , has_entry ( topic_id : topic . id ) )
topic . auto_close_at = 3 . days . from_now
topic . save . should be_true
end
end
it 'when auto_close_user_id is changed, it updates the job' do
Timecop . freeze ( Time . zone . now ) do
admin = Fabricate ( :admin )
Jobs . stubs ( :enqueue_at ) . returns ( true )
topic = Fabricate ( :topic , auto_close_at : 1 . day . from_now )
Jobs . expects ( :cancel_scheduled_job ) . with ( :close_topic , { topic_id : topic . id } )
Jobs . expects ( :enqueue_at ) . with ( 1 . day . from_now , :close_topic , has_entries ( topic_id : topic . id , user_id : admin . id ) )
topic . auto_close_user = admin
topic . save . should be_true
end
end
it 'when auto_close_at and auto_close_user_id are not changed, it should not schedule another CloseTopic job' do
Timecop . freeze ( Time . zone . now ) do
Jobs . expects ( :enqueue_at ) . with ( 1 . day . from_now , :close_topic , has_key ( :topic_id ) ) . once . returns ( true )
Jobs . expects ( :cancel_scheduled_job ) . never
topic = Fabricate ( :topic , auto_close_at : 1 . day . from_now )
topic . title = 'A new title that is long enough'
topic . save . should be_true
end
end
2013-05-15 15:19:41 -04:00
it " ignores the category's default auto-close " do
Timecop . freeze ( Time . zone . now ) do
topic = Fabricate ( :topic , category : Fabricate ( :category , auto_close_days : 14 ) )
Jobs . expects ( :enqueue_at ) . with ( 12 . hours . from_now , :close_topic , has_entries ( topic_id : topic . id , user_id : topic . user_id ) )
topic . auto_close_at = 12 . hours . from_now
topic . save . should be_true
end
end
2013-05-07 14:25:41 -04:00
end
end
2013-05-19 23:04:53 -07:00
describe '#secure_category?' do
let ( :category ) { Category . new }
it " is true if the category is secure " do
category . stubs ( :secure ) . returns ( true )
Topic . new ( :category = > category ) . should be_secure_category
end
it " is false if the category is not secure " do
category . stubs ( :secure ) . returns ( false )
Topic . new ( :category = > category ) . should_not be_secure_category
end
it " is false if there is no category " do
Topic . new ( :category = > nil ) . should_not be_secure_category
end
end
2013-02-05 14:16:51 -05:00
end