require 'spec_helper'

describe JamTrackRight do
  include UsesTempFiles
  include CarrierWave::Test::Matchers
  it "created" do
    jam_track_right = FactoryGirl.create(:jam_track_right)

    user = jam_track_right.user
    jam_track = jam_track_right.jam_track

    # verify that the user sees this as a purchased jam_track
    user.purchased_jam_tracks.should == [jam_track]

    # verify that the jam_track sees the user as an owner
    jam_track.owners.should == [user]

  end

  describe "validations" do
    it "one purchase per user/jam_track combo" do
      user = FactoryGirl.create(:user)
      jam_track = FactoryGirl.create(:jam_track)

      right_1 = FactoryGirl.create(:jam_track_right, user: user, jam_track: jam_track)
      right_2 = FactoryGirl.build(:jam_track_right, user: user, jam_track: jam_track)
      right_2.valid?.should be_false
      right_2.errors[:user_id].should == ['has already been taken']
    end
  end

  describe "private keys automatically created" do
    it "created automatically" do
      jam_track_right = FactoryGirl.create(:jam_track_right)
      jam_track_right.private_key_44.should_not be_nil
      jam_track_right.private_key_48.should_not be_nil
      jam_track_right.private_key_44.should eq(jam_track_right.private_key_48)
    end
  end

  describe "JKZ" do
    before(:all) do
      original_storage = JamTrackTrackUploader.storage = :fog
      original_storage = JamTrackRightUploader.storage = :fog
    end

    after(:all) do
      JamTrackTrackUploader.storage = @original_storage
      JamTrackRightUploader.storage = @original_storage
    end

    before(:each) do
      #content_for_file('abc')
      pending "Not working in test, but does in production.  Needs ~/workspace/"

    end

    it "should fail if no tracks" do
      user = FactoryGirl.create(:user)
      jam_track = FactoryGirl.create(:jam_track)    
      right = JamTrackRight.create(:user=>user, :jam_track=>jam_track)
      expect {
        JamRuby::JamTracksManager.save_jam_track_jkz(user, jam_track)
      }.to raise_error(ArgumentError)
    end

    it "should create" do
      ogg_path = File.join('spec', 'files', 'on.ogg')
      user = FactoryGirl.create(:user)
      jam_track_track = FactoryGirl.create(:jam_track_track)
      jam_track = jam_track_track.jam_track

      s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)

      s3.upload(jam_track_track.manually_uploaded_filename(:url_48), ogg_path)
      jam_track_track[:url_48] = jam_track_track.manually_uploaded_filename(:url_48)
      jam_track_track.save!

      jam_track_track[:url_48].should == jam_track_track.manually_uploaded_filename(:url_48)

      # verify it's on S3
      s3.exists?(jam_track_track[:url_48]).should be_true
      s3.length(jam_track_track[:url_48]).should == File.size?(ogg_path)

      jam_track_right = JamTrackRight.create(:user=>user, :jam_track=>jam_track)
      #expect {
        JamRuby::JamTracksManager.save_jam_track_jkz(user, jam_track)
      #}.to_not raise_error(ArgumentError)
      jam_track_right.reload
      jam_track_right[:url_48].should == jam_track_right.store_dir + '/' + jam_track_right.filename(:url_48)

      # verify it's on S3
      url = jam_track_right[:url_48]
      s3 = S3Manager.new(APP_CONFIG.aws_bucket, APP_CONFIG.aws_access_key_id, APP_CONFIG.aws_secret_access_key)
      s3.exists?(url).should be_true
      s3.length(url).should > File.size?(ogg_path)

      JamTrackRight.ready_to_clean.count.should == 0
      jam_track_right.destroy
      s3.exists?(url).should be_false
    end
  end

  describe "list_keys" do
    let(:user) {FactoryGirl.create(:user)}

    it "empty" do
      JamTrackRight.list_keys(user, nil).should eq([])
    end

    it "bogus key" do
      JamTrackRight.list_keys(user, ['2112']).should eq([])
    end

    it "valid track with no rights to it by querying user" do
      jam_track = FactoryGirl.create(:jam_track)
      keys = JamTrackRight.list_keys(user, [jam_track.id])
      keys.should have(1).items
      keys[0]['private_key'].should be_nil
    end

    it "valid track with rights to it by querying user" do
      jam_track_right = FactoryGirl.create(:jam_track_right)
      keys = JamTrackRight.list_keys(jam_track_right.user, [jam_track_right.jam_track.id])
      keys.should have(1).items
      keys[0].id.should == jam_track_right.jam_track.id
      keys[0]['private_key_44'].should_not be_nil
      keys[0]['private_key_48'].should_not be_nil
      keys[0]['private_key_44'].should eq(jam_track_right.private_key_44)
      keys[0]['private_key_48'].should eq(jam_track_right.private_key_48)
    end
  end

  describe "signing_state" do
    it "quiet" do
      right = FactoryGirl.create(:jam_track_right)
      right.signing_state.should eq('QUIET')
    end

    it "signed" do
      right = FactoryGirl.create(:jam_track_right, signed_44: true, signing_started_at_44: Time.now)
      right.signing_state.should eq('SIGNED')
    end

    it "error" do
      right = FactoryGirl.create(:jam_track_right, error_count: 1)
      right.signing_state.should eq('ERROR')
    end

    it "signing" do
      right = FactoryGirl.create(:jam_track_right, signing_started_at_44: Time.now, packaging_steps: 3, current_packaging_step:0, last_step_at:Time.now)
      right.signing_state(44).should eq('SIGNING')
    end

    it "signing timeout" do
      right = FactoryGirl.create(:jam_track_right, signing_started_at_48: Time.now - 100, packaging_steps: 3, current_packaging_step:0, last_step_at:Time.now)
      right.signing_state(48).should eq('SIGNING_TIMEOUT')
    end

    it "queued" do
      right = FactoryGirl.create(:jam_track_right, signing_queued_at: Time.now)
      right.signing_state(44).should eq('QUEUED')
    end

    it "signing timeout" do
      right = FactoryGirl.create(:jam_track_right, signing_queued_at: Time.now - (APP_CONFIG.signing_job_queue_max_time + 1))
      right.signing_state(44).should eq('QUEUED_TIMEOUT')
    end
  end

  describe "stats" do

    it "empty" do
      JamTrackRight.stats['count'].should eq(0)
    end



    it "one" do
      right1 = FactoryGirl.create(:jam_track_right)
      JamTrackRight.stats.should eq('count' => 1,
                                         'signing_count' => 0,
                                         'redeemed_count' => 0,
                                         'purchased_count' => 1,
                                         'redeemed_and_dl_count' => 0)
    end

    it "two" do
      right1 = FactoryGirl.create(:jam_track_right)
      right2 = FactoryGirl.create(:jam_track_right)

      JamTrackRight.stats.should eq('count' => 2,
                                    'signing_count' => 0,
                                    'redeemed_count' => 0,
                                    'purchased_count' => 2,
                                    'redeemed_and_dl_count' => 0)

      right1.signing_44 = true
      right1.save!
      right2.signing_48 = true
      right2.save!

      JamTrackRight.stats.should eq('count' => 2,
                                    'signing_count' => 2,
                                    'redeemed_count' => 0,
                                    'purchased_count' => 2,
                                    'redeemed_and_dl_count' => 0)

      right1.redeemed = true
      right1.save!

      JamTrackRight.stats.should eq('count' => 2,
                                    'signing_count' => 2,
                                    'redeemed_count' => 1,
                                    'purchased_count' => 1,
                                    'redeemed_and_dl_count' => 0)

      right2.is_test_purchase = true
      right2.save!

      JamTrackRight.stats.should eq('count' => 1,
                                    'signing_count' => 1,
                                    'redeemed_count' => 1,
                                    'purchased_count' => 0,
                                    'redeemed_and_dl_count' => 0)
    end
  end

  describe "guard_against_fraud" do
    let(:user) {FactoryGirl.create(:user)}
    let(:other) {FactoryGirl.create(:user)}
    let(:first_fingerprint) { {all: 'all', running: 'running' } }
    let(:new_fingerprint) { {all: 'all_2', running: 'running' } }
    let(:full_fingerprint) { {all: :all_3, running: :running_3, all_3: { mac: "72:00:02:4C:1E:61", name: "en2", upstate: true }, running_3: { mac: "72:00:02:4C:1E:62", name: "en3", upstate: false } } }
    let(:remote_ip) {'1.1.1.1'}
    let(:remote_ip2) {'2.2.2.2'}
    let(:jam_track_right) { FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: false) }
    let(:jam_track_right_purchased) { FactoryGirl.create(:jam_track_right, user: user, redeemed: false, redeemed_and_fingerprinted: false) }
    let(:jam_track_right_other) { FactoryGirl.create(:jam_track_right, user: other, redeemed: true, redeemed_and_fingerprinted: false) }
    let(:jam_track_right_other_purchased) { FactoryGirl.create(:jam_track_right, user: other, redeemed: false, redeemed_and_fingerprinted: false) }

    it "denies no current_user" do
      jam_track_right.guard_against_fraud(nil, first_fingerprint, remote_ip).should eq('no user specified')
    end

    it "denies no fingerprint" do
      jam_track_right.guard_against_fraud(user, nil, remote_ip).should eq('no fingerprint specified')
    end

    it "allows redemption (success)" do
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
      jam_track_right.valid?.should be_true
      jam_track_right.redeemed_and_fingerprinted.should be_true


      mf = MachineFingerprint.find_by_fingerprint('all')
      mf.user.should eq(user)
      mf.fingerprint.should eq('all')
      mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
      mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ALL)
      mf.jam_track_right.should eq(jam_track_right)

      mf = MachineFingerprint.find_by_fingerprint('running')
      mf.user.should eq(user)
      mf.fingerprint.should eq('running')
      mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
      mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ACTIVE)
      mf.jam_track_right.should eq(jam_track_right)
    end

    it "ignores already successfully redeemed" do
      jam_track_right.redeemed_and_fingerprinted = true
      jam_track_right.save!

      jam_track_right.guard_against_fraud(user, new_fingerprint, remote_ip).should be_nil
      jam_track_right.valid?.should be_true

      # and no new fingerprints
      MachineFingerprint.count.should eq(0)
    end

    it "ignores already normally purchased" do
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip)
      MachineFingerprint.count.should eq(2)

      jam_track_right_purchased.guard_against_fraud(user, new_fingerprint, remote_ip).should be_nil
      jam_track_right_purchased.valid?.should be_true
      jam_track_right_purchased.redeemed_and_fingerprinted.should be_false # fingerprint should not be set on normal purchase

      jam_track_right.redeemed_and_fingerprinted.should be_true # should still be redeemed_and fingerprinted; just checking for weird side-effects

      # no new fingerprints
      MachineFingerprint.count.should eq(2)
    end

    it "protects against re-using fingerprint across users (conflicts on all fp)" do
      first_fingerprint2 = first_fingerprint.clone
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
      MachineFingerprint.count.should eq(2)
      first_fingerprint2[:running] = 'running_2'
      jam_track_right_other.guard_against_fraud(other, first_fingerprint2, remote_ip).should eq("other user has 'all' fingerprint")

      mf = MachineFingerprint.find_by_fingerprint('running')
      mf.user.should eq(user)
      mf.fingerprint.should eq('running')
      mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
      mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ACTIVE)
      mf.jam_track_right.should eq(jam_track_right)
      MachineFingerprint.count.should eq(4)
    end

    it "protects against re-using fingerprint across users (conflicts on running fp)" do
      first_fingerprint2 = first_fingerprint.clone
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
      MachineFingerprint.count.should eq(2)
      first_fingerprint2[:all] = 'all_2'
      jam_track_right_other.guard_against_fraud(other, first_fingerprint2, remote_ip).should eq("other user has 'running' fingerprint")

      mf = MachineFingerprint.find_by_fingerprint('all')
      mf.user.should eq(user)
      mf.fingerprint.should eq('all')
      mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
      mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ALL)
      mf.jam_track_right.should eq(jam_track_right)
      MachineFingerprint.count.should eq(4)

      FraudAlert.count.should eq(1)
      fraud = FraudAlert.first
      fraud.user.should eq(other)
      fraud.machine_fingerprint.should eq(MachineFingerprint.where(fingerprint:'running').where(user_id:other.id).first)
    end

    it "ignores whitelisted fingerprint" do
      whitelist = FingerprintWhitelist.new
      whitelist.fingerprint = first_fingerprint[:running]
      whitelist.save!

      first_fingerprint2 = first_fingerprint.clone
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
      MachineFingerprint.count.should eq(2)
      first_fingerprint2[:all] = 'all_2'
      jam_track_right_other.guard_against_fraud(other, first_fingerprint2, remote_ip).should be_nil

      FraudAlert.count.should eq(0)
    end

    it "does not conflict if same mac, but different IP address" do
      first_fingerprint2 = first_fingerprint.clone
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
      MachineFingerprint.count.should eq(2)
      first_fingerprint2[:all] = 'all_2'
      jam_track_right_other.guard_against_fraud(other, first_fingerprint2, remote_ip2).should eq(nil)

      mf = MachineFingerprint.find_by_fingerprint('all')
      mf.user.should eq(user)
      mf.fingerprint.should eq('all')
      mf.when_taken.should eq(MachineFingerprint::TAKEN_ON_SUCCESSFUL_DOWNLOAD)
      mf.print_type.should eq(MachineFingerprint::PRINT_TYPE_ALL)
      mf.jam_track_right.should eq(jam_track_right)
      MachineFingerprint.count.should eq(4)

      FraudAlert.count.should eq(0)
    end

    # if you try to buy a regular jamtrack with a fingerprint belonging to another user? so what. you paid for it
    it "allows re-use of fingerprint if jamtrack is a normal purchase" do
      first_fingerprint2 = first_fingerprint.clone
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
      MachineFingerprint.count.should eq(2)
      jam_track_right_other_purchased.guard_against_fraud(other, first_fingerprint2, remote_ip).should be_nil
      MachineFingerprint.count.should eq(2)
    end

    it "stops you from redeeming two jamtracks" do
      right1 = FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: true)
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should eq('already redeemed another')
      MachineFingerprint.count.should eq(0)
    end

    it "let's you download a free jamtrack if you have a second but undownloaded free one" do
      right1 = FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: false)
      first_fingerprint2 = first_fingerprint.clone
      jam_track_right.guard_against_fraud(user, first_fingerprint, remote_ip).should be_nil
      MachineFingerprint.count.should eq(2)

      right1.guard_against_fraud(user, first_fingerprint2, remote_ip).should eq('already redeemed another')
      MachineFingerprint.count.should eq(2)
    end

    it "creates metadata" do
      right1 = FactoryGirl.create(:jam_track_right, user: user, redeemed: true, redeemed_and_fingerprinted: false)

      jam_track_right.guard_against_fraud(user, full_fingerprint, remote_ip).should be_nil
      MachineFingerprint.count.should eq(2)
      MachineExtra.count.should eq(2)

    end
  end

end

