require 'rails_helper'

describe IpComparison do
  describe '.compare_ips' do
    let(:candidate_ip) {'98.200.245.149'}

    context 'with an empty comparison list' do
      let(:comparison_ips) {[]}

      it 'returns NO_MATCH' do
        expect(IpComparison.compare_ips(candidate_ip, comparison_ips).match_type).to eq(IpComparison::NO_MATCH)
      end
    end

    context 'with a list that contains an exact match' do
      let(:comparison_ips) {['98.200.245.149', '127.0.0.1']}

      it 'returns MATCH' do
        expect(IpComparison.compare_ips(candidate_ip, comparison_ips).match_type).to eq(IpComparison::MATCH)
      end
    end

    context 'with a list that contains a partial match' do
      let(:comparison_ips) {['98.200.245.84', '127.0.0.1']}
      let(:candidate_result) {MaxMindDB::Result.new({'country' => 'US', 'city' => 'san francisco', 'subdivisions' => ['california']})}
      let(:comparison_result) {MaxMindDB::Result.new({'country' => 'US', 'city' => 'san francisco', 'subdivisions' => ['california']})}
      let(:candidate_isp) {MaxMindDB::Result.new({'autonomous_system_number' => 7922, 'autonomous_system_organization' => 'Comcast Cable Communications, LLC', 'isp' => 'Comcast Cable', 'organization' => 'Comcast Cable', 'network' => '98.200.128.0/17'})}
      let(:comparison_isp) {MaxMindDB::Result.new({'autonomous_system_number' => 7922, 'autonomous_system_organization' => 'Comcast Cable Communications, LLC', 'isp' => 'Comcast Cable', 'organization' => 'Comcast Cable', 'network' => '98.200.128.0/17'})}

      before do
        expect(Maxmind).to receive(:lookup).with(candidate_ip).and_return(candidate_result)
        expect(Maxmind).to receive(:lookup).with('98.200.245.84').and_return(comparison_result)
        expect(Maxmind).to receive(:isp_lookup).with(candidate_ip).and_return(candidate_isp)
        expect(Maxmind).to receive(:isp_lookup).with('98.200.245.84').and_return(comparison_isp)
      end

      it 'returns PARTIAL_MATCH' do
        expect(IpComparison.compare_ips(candidate_ip, comparison_ips).match_type).to eq(IpComparison::PARTIAL_MATCH)
      end
    end

    context 'with a list that does not contain a match' do
      let(:comparison_ips) {['0.0.0.0', '127.0.0.1']}

      it 'returns NO_MATCH' do
        expect(IpComparison.compare_ips(candidate_ip, comparison_ips).match_type).to eq(IpComparison::NO_MATCH)
      end
    end
  end

  describe '.partial_ip_match_by_prefix' do
    let(:candidate_ip) {'98.200.245.149'}

    context 'with a malformed IP address' do
      let(:comparison_ip) {'9.0.4'}

      it 'returns false' do
        expect(IpComparison.partial_ip_match_by_prefix(candidate_ip, comparison_ip)).to be false
      end
    end

    context 'with no octets matching' do
      let(:comparison_ip) {'127.0.0.1'}

      it 'returns false' do
        expect(IpComparison.partial_ip_match_by_prefix(candidate_ip, comparison_ip)).to be false
      end
    end

    context 'with octets matching' do
      let(:comparison_ip) {'98.200.1.1'}

      it 'returns false' do
        expect(IpComparison.partial_ip_match_by_prefix(candidate_ip, comparison_ip)).to be true
      end
    end
  end

  describe '.partial_ip_match_by_geo' do
    let(:candidate_ip) {'98.200.245.149'}
    let(:comparison_ip) {'98.200.1.1'}
    let(:candidate_result) {MaxMindDB::Result.new({'country' => 'US', 'city' => 'san francisco', 'subdivisions' => [{'iso_code' => 'CA', 'names' => {'en' => 'California'}}]})}
    let(:comparison_result) {MaxMindDB::Result.new({'country' => 'US', 'city' => 'san francisco', 'subdivisions' => [{'iso_code' => 'CA', 'names' => {'en' => 'California'}}]})}
    let(:candidate_isp) {MaxMindDB::Result.new({'autonomous_system_number' => 7922, 'autonomous_system_organization' => 'Comcast Cable Communications, LLC', 'isp' => 'Comcast Cable', 'organization' => 'Comcast Cable', 'network' => '98.200.128.0/17'})}
    let(:comparison_isp) {MaxMindDB::Result.new({'autonomous_system_number' => 7922, 'autonomous_system_organization' => 'Comcast Cable Communications, LLC', 'isp' => 'Comcast Cable', 'organization' => 'Comcast Cable', 'network' => '98.200.128.0/17'})}

    before do
      expect(Maxmind).to receive(:lookup).with(candidate_ip).and_return(candidate_result)
      expect(Maxmind).to receive(:lookup).with(comparison_ip).and_return(comparison_result)
      expect(Maxmind).to receive(:isp_lookup).with(candidate_ip).and_return(candidate_isp)
      expect(Maxmind).to receive(:isp_lookup).with(comparison_ip).and_return(comparison_isp)
    end

    context 'with ISP and geo matching' do

      it 'returns true' do
        expect(IpComparison.partial_ip_match_by_geo(candidate_ip, comparison_ip).match_type).to eq(IpComparison::PARTIAL_MATCH)
      end
    end

    context 'with ISP not matching and geo matching' do
      let(:comparison_isp) {MaxMindDB::Result.new({'autonomous_system_number' => 35994, 'autonomous_system_organization' => 'Akamai Technologies, Inc.', 'isp' => 'Akamai Technologies', 'organization' => 'Akamai Technologies', 'network' => '172.232.0.0/13'})}

      it 'returns true' do
        expect(IpComparison.partial_ip_match_by_geo(candidate_ip, comparison_ip).match_type).to eq(IpComparison::PARTIAL_MATCH)
      end
    end

    context 'with ISP matching and geo matching except city' do
      let(:candidate_result) {MaxMindDB::Result.new({'country' => 'US', 'city' => 'san jose', 'subdivisions' => [{'iso_code' => 'CA', 'names' => {'en' => 'California'}}]})}

      it 'returns true' do
        expect(IpComparison.partial_ip_match_by_geo(candidate_ip, comparison_ip).match_type).to eq(IpComparison::PARTIAL_MATCH)
      end
    end

    context 'with ISP matching and geo matching and missing city' do
      let(:candidate_result) {MaxMindDB::Result.new({'country' => 'US', 'subdivisions' => [{'iso_code' => 'CA', 'names' => {'en' => 'California'}}]})}
      let(:comparison_result) {MaxMindDB::Result.new({'country' => 'US', 'subdivisions' => [{'iso_code' => 'CA', 'names' => {'en' => 'California'}}]})}

      it 'returns true' do
        expect(IpComparison.partial_ip_match_by_geo(candidate_ip, comparison_ip).match_type).to eq(IpComparison::PARTIAL_MATCH)
      end
    end

    context 'with ISP matching and geo matching and missing region' do
      let(:candidate_result) {MaxMindDB::Result.new({'country' => 'US', 'city' => 'san francisco'})}
      let(:comparison_result) {MaxMindDB::Result.new({'country' => 'US', 'city' => 'san francisco'})}

      it 'returns true' do
        expect(IpComparison.partial_ip_match_by_geo(candidate_ip, comparison_ip).match_type).to eq(IpComparison::PARTIAL_MATCH)
      end
    end

    context 'with ISP matching but region and city not matching' do
      let(:comparison_result) {MaxMindDB::Result.new({'country' => 'US', 'city' => 'las vegas', 'subdivisions' => [{'iso_code' => 'NV', 'names' => {'en' => 'Nevada'}}]})}

      it 'returns false' do
        expect(IpComparison.partial_ip_match_by_geo(candidate_ip, comparison_ip).match_type).to eq(IpComparison::NO_MATCH)
      end
    end
  end

  describe '.ips_from_text' do
    subject {IpComparison.ips_from_text(input_text)}

    context "with two valid IPs" do
      let(:input_text) {"ah yes 0.0.0.0 zzz that's the 192.168.1.1 stuff"}

      it 'returns two arrays of each IP address split by octets' do
        expect(subject).to match_array(["0.0.0.0", "192.168.1.1"])
      end
    end

    context "with one valid IP and one invalid IP" do
      let(:input_text) {"ah yes 256.85.113.47 zzz that's the 192.168.1.1 stuff"}

      it 'returns one array of the valid IP address split by octets' do
        expect(subject).to match_array(["192.168.1.1"])
      end
    end
  end
end
