require File.dirname(__FILE__) + '/../test_helper'

class CommentTest < Test::Rails::TestCase
  fixtures :comments, :users, :topics, :ratings
  
  def setup
    User.current_user = users(:ernest)

    # for testing action mailer
    @emails = ActionMailer::Base.deliveries 
    @emails.clear
  end
  
  # test userstamp plugin
  def test_userstamp
    assert_equal users(:ernest), Comment.create(:topic => topics(:direct_democracy), :body => 'Yeah, totally!').user
  end
  
  def test_acts_as_threaded_not_explosive
    assert Comment.create(:topic => topics(:direct_democracy), :body => 'Yeah, totally!').root?    
  end
  
  def test_comment_with_reply_is_a_parent
    assert_difference(comments(:first_post), :children_count) do
      assert Comment.create(:topic => topics(:direct_democracy), :parent_id => comments(:first_post).id, :body => 'No way!')
      comments(:first_post).reload
    end
  end
  
  def test_reply_is_child_of_original
    assert_equal 1, 
      Comment.create(:topic => topics(:direct_democracy), :parent_id => 1, :body => 'No way!').parent_id
  end
  
  def test_reply_is_a_child
    assert Comment.create(:topic => topics(:direct_democracy), :parent_id => 1, :body => 'No way!').child?
  end
  
  def test_direct_children_count_root
    assert_equal 1, comments(:first_post).direct_children.length
  end

  def test_direct_children_count_last
    assert_equal 0, comments(:second_reply).direct_children.length
  end
  
  def test_reply_creation_notifies_original_author
    c = Comment.create(:topic => topics(:direct_democracy), :parent_id => 3, :body => 'No way!')
    assert_equal 1, @emails.length
  end
  
  def test_comment_on_topic_does_not_send_email
    c = Comment.create(:topic => topics(:direct_democracy), :parent_id => 0, :body => 'Good topic')
    assert_equal 0, @emails.length    
  end
  
  def test_most_rated_comment
    assert_kind_of Comment, Comment::find_most_rated
    assert_equal comments(:long_first_post), Comment::find_most_rated
  end

  def test_find_mean_highest_rated
    assert_kind_of Comment, Comment::find_best_mean_rated
    assert_equal comments(:day_old_reply), Comment::find_best_mean_rated
  end
  
  def test_find_most_replied
    assert_kind_of Comment, Comment::find_most_replied
    assert_equal 2, Comment::find_most_replied.id
  end
  
  def test_word_count
    assert_equal 3, Comment.find(4).word_count
  end
  
  def test_creating_a_comment_sets_word_count
    c = Comment.create(
       :user => users(:ernest), 
       :parent => comments(:first_post), 
       :topic => topics(:direct_democracy),
       :body => "This! should -- be seven words long."
       )
    assert_equal 7, c.word_count
  end
  
  # return nil when no comments to find
  def test_most_rated_comment_no_comments
    Comment.destroy_all
    assert_kind_of NilClass, Comment::find_most_rated
  end

  def test_find_mean_highest_rated_no_comments
    Comment.destroy_all
    assert_kind_of NilClass, Comment::find_best_mean_rated
  end
  
  def test_find_most_replied_no_comments
    Comment.destroy_all
    assert_kind_of NilClass, Comment::find_most_replied
  end
  
  def test_count_visible
    assert_difference(Comment, :count_visible, nil, -1) do
      comments(:first_post).toggle!(:removed)
    end
  end

  def test_find_three_most_read_returns_comments_with_at_least_one_read
    assert_equal 1, Comment.find_three_most_read.length
  end
  
  def test_find_three_most_read
    assert_equal comments(:long_first_post), Comment.find_three_most_read.first
  end
  
  def test_adding_a_reading_changes_count_cache
    assert_difference(comments(:long_first_post), :reading_count) do
      users(:grace).record_read_of(comments(:long_first_post))
      comments(:long_first_post).reload
    end
  end
  
  # There was a bug that ignores readings of new comments 
  def test_adding_a_reading_changes_count_cache_new_comment
    c = create_comment
    assert_difference(c, :reading_count) do
      users(:grace).record_read_of(c)
      c.reload
    end
  end
  
  # The default value of reading_count was nil when it should have been zero
  def test_reading_count_cache_should_default_to_zero
    c = create_comment 
    assert_equal 0, c.reading_count
  end  
  
  def test_find_three_highest_avg_rating
    assert_nothing_raised { Comment.find_three_highest_avg_rating}
    assert_equal 3, Comment.find_three_highest_avg_rating.length
  end
  
  def test_find_three_newest
    results = Comment.find_three_newest
    assert_equal 3, results.length
    assert(results[0].created_at > results[1].created_at)
    assert(results[1].created_at > results[2].created_at)
  end
  
  def test_ratings_for_user
    assert_kind_of CommentRating, comments(:second_reply).ratings.for_user(users(:ernest))
  end
  
  def test_ratings_for_user_self_rating
    assert_nil comments(:first_post).ratings.for_user(users(:ernest))
  end
  
  def test_ratings_for_user_participant_already_rated
    ernest_rates_graces_comment = CommentRating.find(4)
    graces_comment = Comment.find(3)
    sanity_check "ernest rated graces comment", ernest_rates_graces_comment.subject.id == graces_comment.id
    
    rating = graces_comment.ratings.for_user(users(:ernest))
    assert_equal ernest_rates_graces_comment, rating
    assert !rating.new_record?
  end
  
  def test_ratings_for_user_guest
    assert_kind_of CommentRating, comments(:second_reply).ratings.for_user(users(:visitor1))
  end
  
  def test_ratings_for_user_guest_already_rated
    rating = Comment.find(4).ratings.for_user(users(:visitor1))
    assert_equal CommentRating.find(23), rating
    assert !rating.new_record?
  end

  def test_word_count_not_explosive_when_no_body
    c = Comment.new
    assert_nothing_raised { c.count_words }
  end
  
  def test_update_average_rating_cache
    @comment = comments(:day_old_reply)
    @comment.ratings.first.update_attribute(:value, 1) # can just create new rating, since that would trigger it's callback
    @comment.update_average_rating_cache
    assert_in_delta(2.33, @comment.avg_rating, 0.1) 
  end
  
  def test_find_three_highest_avg_rating_should_scope_to_topic
    t = topics(:direct_democracy)
    comments = t.comments.find_three_highest_avg_rating
    assert 2, comments.length
    assert comments.all?{|c| c.topic == t}
  end
  
  def test_all_parent_children_ids_is_hash_of_arrays
    assert_kind_of(Hash, Comment.all_parent_children_ids([1,2,3]))
    assert Comment.all_parent_children_ids([1,2,3]).all?{|r| r.is_a?(Array)}
  end
  
  def test_all_parent_children_ids_has_value_for_each_comment_under_topic
    comments_on_topic = topics(:direct_democracy).comments
    @r = Comment.all_parent_children_ids(comments_on_topic.collect{|c| c.id})
    comments_on_topic.each{|comment_on_topic| 
      assert @r.values.flatten.include?(comment_on_topic.id), "missing some comment #{comment_on_topic.id} in #{@r.to_yaml}"
    }
  end
  
  def test_all_parent_children_ids_has_no_index_for_comments_without_children
    comments_on_topic = topics(:direct_democracy).comments
    comments_without_children = comments_on_topic.select{|c|c.children.empty?}
    @r = Comment.all_parent_children_ids(comments_on_topic.collect{|c| c.id})
    assert @r.values.all?{|id| !comments_without_children.include?(id)}
  end
  
  def test_all_parent_children_ids_not_explosive_when_empty_set_given
    assert_nothing_raised() { Comment.all_parent_children_ids([]) }
  end
  
  
  def create_comment
    Comment.create(
       :user => users(:ernest), 
       :parent => comments(:first_post), 
       :topic => topics(:direct_democracy),
       :body => "Hey there tiger."
       )    
  end
end  

