215 lines
5.7 KiB
Elixir
215 lines
5.7 KiB
Elixir
defmodule Towerops.GeoIPTest do
|
|
use Towerops.DataCase, async: true
|
|
|
|
alias Towerops.GeoIP
|
|
alias Towerops.GeoIP.Block
|
|
alias Towerops.GeoIP.Location
|
|
|
|
describe "lookup/1" do
|
|
test "returns country code for valid IP with location data" do
|
|
# Insert test location
|
|
location = insert_location(%{geoname_id: 5_375_480, country_code: "US"})
|
|
|
|
# Insert test block for IP 8.8.8.8 (134744072)
|
|
insert_block(%{
|
|
geoname_id: location.geoname_id,
|
|
start_ip_int: 134_744_072,
|
|
end_ip_int: 134_744_072
|
|
})
|
|
|
|
assert GeoIP.lookup("8.8.8.8") == "US"
|
|
end
|
|
|
|
test "returns nil for valid IP with no location data" do
|
|
assert GeoIP.lookup("192.168.1.1") == nil
|
|
end
|
|
|
|
test "returns nil for invalid IP address" do
|
|
assert GeoIP.lookup("invalid") == nil
|
|
end
|
|
|
|
test "returns nil for empty string" do
|
|
assert GeoIP.lookup("") == nil
|
|
end
|
|
|
|
test "returns nil for IP with too many octets" do
|
|
assert GeoIP.lookup("1.2.3.4.5") == nil
|
|
end
|
|
|
|
test "returns nil for IP with invalid octets" do
|
|
assert GeoIP.lookup("999.999.999.999") == nil
|
|
end
|
|
end
|
|
|
|
describe "lookup_full/1" do
|
|
test "returns full location data for valid IP" do
|
|
# Insert test location with full data
|
|
location =
|
|
insert_location(%{
|
|
geoname_id: 5_375_480,
|
|
country_code: "US",
|
|
country_name: "United States",
|
|
city_name: "Mountain View",
|
|
subdivision_1_name: "California",
|
|
subdivision_2_name: nil,
|
|
latitude: 37.386,
|
|
longitude: -122.0838
|
|
})
|
|
|
|
# Insert test block for IP 8.8.8.8
|
|
insert_block(%{
|
|
geoname_id: location.geoname_id,
|
|
start_ip_int: 134_744_072,
|
|
end_ip_int: 134_744_072
|
|
})
|
|
|
|
result = GeoIP.lookup_full("8.8.8.8")
|
|
|
|
assert result.country_code == "US"
|
|
assert result.country_name == "United States"
|
|
assert result.city_name == "Mountain View"
|
|
assert result.subdivision_1_name == "California"
|
|
assert result.subdivision_2_name == nil
|
|
assert result.latitude == 37.386
|
|
assert result.longitude == -122.0838
|
|
end
|
|
|
|
test "returns location data with minimal fields" do
|
|
location = insert_location(%{geoname_id: 1234, country_code: "CA"})
|
|
|
|
insert_block(%{
|
|
geoname_id: location.geoname_id,
|
|
start_ip_int: 100,
|
|
end_ip_int: 200
|
|
})
|
|
|
|
result = GeoIP.lookup_full("0.0.0.100")
|
|
|
|
assert result.country_code == "CA"
|
|
assert result.country_name == nil
|
|
assert result.city_name == nil
|
|
end
|
|
|
|
test "returns nil for IP not in any block" do
|
|
# Create location and block, but query different IP
|
|
location = insert_location(%{geoname_id: 5678, country_code: "US"})
|
|
|
|
insert_block(%{
|
|
geoname_id: location.geoname_id,
|
|
start_ip_int: 1000,
|
|
end_ip_int: 2000
|
|
})
|
|
|
|
assert GeoIP.lookup_full("192.168.1.1") == nil
|
|
end
|
|
|
|
test "returns nil for invalid IP string" do
|
|
assert GeoIP.lookup_full("not-an-ip") == nil
|
|
end
|
|
|
|
test "returns nil for empty string" do
|
|
assert GeoIP.lookup_full("") == nil
|
|
end
|
|
|
|
test "handles IP at start of range" do
|
|
location = insert_location(%{geoname_id: 9999, country_code: "FR"})
|
|
|
|
insert_block(%{
|
|
geoname_id: location.geoname_id,
|
|
start_ip_int: 167_772_160,
|
|
end_ip_int: 167_772_170
|
|
})
|
|
|
|
# 10.0.0.0 = 167772160
|
|
result = GeoIP.lookup_full("10.0.0.0")
|
|
assert result.country_code == "FR"
|
|
end
|
|
|
|
test "handles IP at end of range" do
|
|
location = insert_location(%{geoname_id: 8888, country_code: "DE"})
|
|
|
|
insert_block(%{
|
|
geoname_id: location.geoname_id,
|
|
start_ip_int: 167_772_160,
|
|
end_ip_int: 167_772_170
|
|
})
|
|
|
|
# 10.0.0.10 = 167772170
|
|
result = GeoIP.lookup_full("10.0.0.10")
|
|
assert result.country_code == "DE"
|
|
end
|
|
|
|
test "handles IP in middle of range" do
|
|
location = insert_location(%{geoname_id: 7777, country_code: "GB"})
|
|
|
|
insert_block(%{
|
|
geoname_id: location.geoname_id,
|
|
start_ip_int: 167_772_160,
|
|
end_ip_int: 167_772_170
|
|
})
|
|
|
|
# 10.0.0.5 = 167772165
|
|
result = GeoIP.lookup_full("10.0.0.5")
|
|
assert result.country_code == "GB"
|
|
end
|
|
|
|
test "returns first matching block when multiple blocks overlap" do
|
|
location1 = insert_location(%{geoname_id: 1111, country_code: "US"})
|
|
location2 = insert_location(%{geoname_id: 2222, country_code: "CA"})
|
|
|
|
insert_block(%{
|
|
network: "0.0.0.100/24",
|
|
geoname_id: location1.geoname_id,
|
|
start_ip_int: 100,
|
|
end_ip_int: 200
|
|
})
|
|
|
|
insert_block(%{
|
|
network: "0.0.0.50/24",
|
|
geoname_id: location2.geoname_id,
|
|
start_ip_int: 50,
|
|
end_ip_int: 150
|
|
})
|
|
|
|
# Query returns first match due to limit: 1
|
|
result = GeoIP.lookup_full("0.0.0.150")
|
|
assert result.country_code in ["US", "CA"]
|
|
end
|
|
end
|
|
|
|
# Helper functions to insert test data
|
|
defp insert_location(attrs) do
|
|
default_attrs = %{
|
|
geoname_id: System.unique_integer([:positive]),
|
|
country_code: "US",
|
|
country_name: nil,
|
|
city_name: nil,
|
|
subdivision_1_name: nil,
|
|
subdivision_2_name: nil,
|
|
latitude: nil,
|
|
longitude: nil
|
|
}
|
|
|
|
attrs = Map.merge(default_attrs, Map.new(attrs))
|
|
|
|
%Location{}
|
|
|> Location.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
|
|
defp insert_block(attrs) do
|
|
default_attrs = %{
|
|
network: "0.0.0.0/32",
|
|
start_ip_int: 0,
|
|
end_ip_int: 0,
|
|
geoname_id: nil,
|
|
registered_country_geoname_id: nil
|
|
}
|
|
|
|
attrs = Map.merge(default_attrs, Map.new(attrs))
|
|
|
|
%Block{}
|
|
|> Block.changeset(attrs)
|
|
|> Repo.insert!()
|
|
end
|
|
end
|