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

class CassandraBaseStoreTest < TestCase
  include Assertions
  # Testing through CassandraStores::Follow for easy testing
  context "CassandraStores::Base through CassandraStores::Follow" do
    setup do
      use_cassandra_store
      @klass = Socialization::CassandraStores::Follow
      @klass.touch nil
      @klass.after_follow nil
      @klass.after_unfollow nil
      @follower1 = ImAFollower.create
      @follower2 = ImAFollower.create
      @followable1 = ImAFollowable.create
      @followable2 = ImAFollowable.create
    end

    context "Stores" do
      should "inherit Socialization::CassandraStores::Follow" do
        assert_equal Socialization::CassandraStores::Follow, Socialization.follow_model
      end
    end

    context "#follow!" do
      should "create follow records" do
        @klass.follow!(@follower1, @followable1)
        row = Socialization.cassandra_session.execute("SELECT actor_type, actor_id FROM #{@klass.forward_table_name} where victim_type='#{@followable1.class}' AND victim_id='#{@followable1.id}' ALLOW FILTERING").rows.to_a.first
        assert_array_similarity ["#{@follower1.class}:#{@follower1.id}"], ["#{row['actor_type']}:#{row['actor_id']}"]
        row = Socialization.cassandra_session.execute("SELECT victim_type, victim_id FROM #{@klass.backward_table_name} where actor_type='#{@follower1.class}' AND actor_id='#{@follower1.id}' ALLOW FILTERING").rows.to_a.last
        assert_array_similarity ["#{@followable1.class}:#{@followable1.id}"], ["#{row['victim_type']}:#{row['victim_id']}"]

        @klass.follow!(@follower2, @followable1)
        rows = Socialization.cassandra_session.execute("SELECT actor_type, actor_id FROM #{@klass.forward_table_name} where victim_type='#{@followable1.class}' AND victim_id='#{@followable1.id}' ALLOW FILTERING").rows
        assert_array_similarity ["#{@follower1.class}:#{@follower1.id}", "#{@follower2.class}:#{@follower2.id}"], (rows.collect {|i| "#{i['actor_type']}:#{i['actor_id']}"})

        row = Socialization.cassandra_session.execute("SELECT victim_type, victim_id FROM #{@klass.backward_table_name} where actor_type='#{@follower1.class}' AND actor_id='#{@follower1.id}' ALLOW FILTERING").rows.to_a.last
        assert_array_similarity ["#{@followable1.class}:#{@followable1.id}"], ["#{row['victim_type']}:#{row['victim_id']}"]
        row = Socialization.cassandra_session.execute("SELECT victim_type, victim_id FROM #{@klass.backward_table_name} where actor_type='#{@follower2.class}' AND actor_id='#{@follower2.id}' ALLOW FILTERING").rows.to_a.first
        assert_array_similarity ["#{@followable1.class}:#{@followable1.id}"], ["#{row['victim_type']}:#{row['victim_id']}"]
      end

      should "touch follower when instructed" do
        @klass.touch :follower
        assert_send([@follower1, :touch])
        assert_send([@followable1, :touch])
        @klass.follow!(@follower1, @followable1)
      end

      should "touch followable when instructed" do
        @klass.touch :followable
        !assert_send([@follower1, :touch])
        assert_send([@followable1, :touch])
        @klass.follow!(@follower1, @followable1)
      end

      should "touch all when instructed" do
        @klass.touch :all
        assert_send([@follower1, :touch])
        assert_send([@followable1, :touch])
        @klass.follow!(@follower1, @followable1)
      end

      # should "call after follow hook" do
      #   @klass.after_follow :after_follow
      #   assert_send([@klass, :after_follow])
      #   @klass.follow!(@follower1, @followable1)
      # end

      # should "call after unfollow hook" do
      #   @klass.after_follow :after_unfollow
      #   assert_send([@klass, :after_unfollow])
      #   @klass.follow!(@follower1, @followable1)
      # end
    end

    context "#unfollow!" do
      setup do
        @klass.follow!(@follower1, @followable1)
      end

      should "remove follow records" do
        @klass.unfollow!(@follower1, @followable1)
        assert_empty Socialization.cassandra_session.execute("SELECT * FROM #{@klass.backward_table_name} where actor_type='#{@follower1.class}' AND actor_id='#{@follower1.id}' ALLOW FILTERING").rows.to_a
        assert_empty Socialization.cassandra_session.execute("SELECT * FROM #{@klass.forward_table_name} where victim_type='#{@followable1.class}' AND victim_id='#{@followable1.id}' ALLOW FILTERING").rows.to_a
      end
    end

    context "#follows?" do
      should "return true when follow exists" do
        @klass.follow!(@follower1, @followable1)
        assert_true @klass.follows?(@follower1, @followable1)
      end

      should "return false when follow doesn't exist" do
        assert_false @klass.follows?(@follower1, @followable1)
      end
    end

    context "#followers" do
      should "return an array of followers" do
        follower1 = ImAFollower.create
        follower2 = ImAFollower.create
        follower1.follow!(@followable1)
        follower2.follow!(@followable1)
        assert_array_similarity [follower1, follower2], @klass.followers(@followable1, follower1.class)
      end

      should "return an array of follower ids when plucking" do
        follower1 = ImAFollower.create
        follower2 = ImAFollower.create
        follower1.follow!(@followable1)
        follower2.follow!(@followable1)
        assert_array_similarity [follower1.id.to_s, follower2.id.to_s], @klass.followers(@followable1, follower1.class, :pluck => :id)
      end
    end

    context "#followables" do
      should "return an array of followables" do
        followable1 = ImAFollowable.create
        followable2 = ImAFollowable.create
        @follower1.follow!(followable1)
        @follower1.follow!(followable2)

        assert_array_similarity [followable1, followable2], @klass.followables(@follower1, followable1.class)
      end

      should "return an array of followables ids when plucking" do
        followable1 = ImAFollowable.create
        followable2 = ImAFollowable.create
        @follower1.follow!(followable1)
        @follower1.follow!(followable2)
        assert_array_similarity [followable1.id.to_s, followable2.id.to_s], @klass.followables(@follower1, followable1.class, :pluck => :id)
      end
    end

    context "#remove_followers" do
      should "delete all followers relationships for a followable" do
        @follower1.follow!(@followable1)
        @follower2.follow!(@followable1)
        assert_equal 2, @followable1.followers(@follower1.class).count

        @klass.remove_followers(@followable1)
        assert_equal 0, @followable1.followers(@follower1.class).count
        assert_empty Socialization.cassandra_session.execute("SELECT * FROM #{@klass.forward_table_name} where victim_type='#{@followable1.class}' AND victim_id='#{@followable1.id}' ALLOW FILTERING").rows.to_a
        assert_empty Socialization.cassandra_session.execute("SELECT * FROM #{@klass.backward_table_name} where actor_type='#{@follower1.class}' AND actor_id='#{@follower1.id}' ALLOW FILTERING").rows.to_a
        assert_empty Socialization.cassandra_session.execute("SELECT * FROM #{@klass.backward_table_name} where actor_type='#{@follower2.class}' AND actor_id='#{@follower2.id}' ALLOW FILTERING").rows.to_a
      end
    end

    context "#remove_followables" do
      should "delete all followables relationships for a follower" do
        @follower1.follow!(@followable1)
        @follower1.follow!(@followable2)
        assert_equal 2, @follower1.followables(@followable1.class).count

        @klass.remove_followables(@follower1)
        assert_equal 0, @follower1.followables(@followable1.class).count
        assert_empty Socialization.cassandra_session.execute("SELECT * FROM #{@klass.forward_table_name} where victim_type='#{@followable1.class}' AND victim_id='#{@followable1.id}' ALLOW FILTERING").rows.to_a
        assert_empty Socialization.cassandra_session.execute("SELECT * FROM #{@klass.backward_table_name} where actor_type='#{@follower1.class}' AND actor_id='#{@follower1.id}' ALLOW FILTERING").rows.to_a
        assert_empty Socialization.cassandra_session.execute("SELECT * FROM #{@klass.backward_table_name} where actor_type='#{@follower2.class}' AND actor_id='#{@follower2.id}' ALLOW FILTERING").rows.to_a
        # assert_empty Socialization.redis.smembers backward_key(@followable1)
        # assert_empty Socialization.redis.smembers backward_key(@follower2)
        # assert_empty Socialization.redis.smembers forward_key(@follower1)
      end
    end

  end

  # Helpers
  def assert_match_follower(follow_record, follower)
    assert follow_record.follower_type ==  follower.class.to_s && follow_record.follower_id == follower.id
  end

  def assert_match_followable(follow_record, followable)
    assert follow_record.followable_type ==  followable.class.to_s && follow_record.followable_id == followable.id
  end

  def forward_key(followable)
    Socialization::CassandraStores::Follow.send(:forward_table_name)
  end

  def backward_key(follower)
    Socialization::CassandraStores::Follow.send(:backward_table_name)
  end
end