710 lines
24 KiB
Ruby
710 lines
24 KiB
Ruby
require "test_helper"
|
|
|
|
class NetworkRangeTest < ActiveSupport::TestCase
|
|
setup do
|
|
@ipv4_range = NetworkRange.new(network: "192.168.1.0/24")
|
|
@ipv6_range = NetworkRange.new(network: "2001:db8::/32")
|
|
@user = users(:jason)
|
|
end
|
|
|
|
# Validations
|
|
test "should be valid with network address" do
|
|
assert @ipv4_range.valid?
|
|
assert @ipv6_range.valid?
|
|
end
|
|
|
|
test "should not be valid without network" do
|
|
@ipv4_range.network = nil
|
|
assert_not @ipv4_range.valid?
|
|
assert_includes @ipv4_range.errors[:network], "can't be blank"
|
|
end
|
|
|
|
test "should validate network uniqueness" do
|
|
@ipv4_range.save!
|
|
duplicate = NetworkRange.new(network: "192.168.1.0/24")
|
|
assert_not duplicate.valid?
|
|
assert_includes duplicate.errors[:network], "has already been taken"
|
|
end
|
|
|
|
test "should validate source inclusion" do
|
|
valid_sources = %w[api_imported user_created manual auto_generated inherited geolite_asn geolite_country]
|
|
valid_sources.each do |source|
|
|
@ipv4_range.source = source
|
|
assert @ipv4_range.valid?, "Source #{source} should be valid"
|
|
end
|
|
|
|
@ipv4_range.source = "invalid_source"
|
|
assert_not @ipv4_range.valid?
|
|
assert_includes @ipv4_range.errors[:source], "is not included in the list"
|
|
end
|
|
|
|
test "should validate ASN numericality" do
|
|
@ipv4_range.asn = 12345
|
|
assert @ipv4_range.valid?
|
|
|
|
@ipv4_range.asn = 0
|
|
assert_not @ipv4_range.valid?
|
|
assert_includes @ipv4_range.errors[:asn], "must be greater than 0"
|
|
|
|
@ipv4_range.asn = -1
|
|
assert_not @ipv4_range.valid?
|
|
assert_includes @ipv4_range.errors[:asn], "must be greater than 0"
|
|
|
|
@ipv4_range.asn = "not_a_number"
|
|
assert_not @ipv4_range.valid?
|
|
|
|
@ipv4_range.asn = nil
|
|
assert @ipv4_range.valid?
|
|
end
|
|
|
|
# Callbacks
|
|
test "should set default source before validation" do
|
|
range = NetworkRange.new(network: "10.0.0.0/8")
|
|
range.valid?
|
|
assert_equal "api_imported", range.source
|
|
end
|
|
|
|
test "should not override existing source" do
|
|
range = NetworkRange.new(network: "10.0.0.0/8", source: "user_created")
|
|
range.valid?
|
|
assert_equal "user_created", range.source
|
|
end
|
|
|
|
# Virtual Attributes (CIDR)
|
|
test "cidr getter returns network as string" do
|
|
@ipv4_range.save!
|
|
assert_equal "192.168.1.0/24", @ipv4_range.cidr
|
|
assert_equal "192.168.1.0/24", @ipv4_range.network.to_s
|
|
end
|
|
|
|
test "cidr setter sets network from string" do
|
|
range = NetworkRange.new
|
|
range.cidr = "10.0.0.0/16"
|
|
assert_equal "10.0.0.0/16", range.network.to_s
|
|
end
|
|
|
|
# Network Properties
|
|
test "prefix_length returns correct network prefix" do
|
|
@ipv4_range.save!
|
|
@ipv6_range.save!
|
|
|
|
assert_equal 24, @ipv4_range.prefix_length
|
|
assert_equal 32, @ipv6_range.prefix_length
|
|
end
|
|
|
|
test "network_address returns network address" do
|
|
@ipv4_range.save!
|
|
assert_equal "192.168.1.0", @ipv4_range.network_address
|
|
end
|
|
|
|
test "broadcast_address returns correct broadcast for IPv4" do
|
|
@ipv4_range.save!
|
|
assert_equal "192.168.1.255", @ipv4_range.broadcast_address
|
|
end
|
|
|
|
test "broadcast_address returns nil for IPv6" do
|
|
@ipv6_range.save!
|
|
assert_nil @ipv6_range.broadcast_address
|
|
end
|
|
|
|
test "family detection works correctly" do
|
|
@ipv4_range.save!
|
|
@ipv6_range.save!
|
|
|
|
assert_equal 4, @ipv4_range.family
|
|
assert_equal 6, @ipv6_range.family
|
|
end
|
|
|
|
test "ipv4? and ipv6? predicate methods work" do
|
|
@ipv4_range.save!
|
|
@ipv6_range.save!
|
|
|
|
assert @ipv4_range.ipv4?
|
|
assert_not @ipv4_range.ipv6?
|
|
|
|
assert @ipv6_range.ipv6?
|
|
assert_not @ipv6_range.ipv4?
|
|
end
|
|
|
|
test "virtual? works correctly" do
|
|
range = NetworkRange.new(network: "10.0.0.0/8")
|
|
assert range.virtual?
|
|
|
|
range.save!
|
|
assert_not range.virtual?
|
|
end
|
|
|
|
# Network Containment
|
|
test "contains_ip? works correctly" do
|
|
@ipv4_range.save!
|
|
|
|
assert @ipv4_range.contains_ip?("192.168.1.1")
|
|
assert @ipv4_range.contains_ip?("192.168.1.254")
|
|
assert_not @ipv4_range.contains_ip?("192.168.2.1")
|
|
assert_not @ipv4_range.contains_ip?("2001:db8::1")
|
|
|
|
# Test IPv6
|
|
@ipv6_range.save!
|
|
assert @ipv6_range.contains_ip?("2001:db8::1")
|
|
assert @ipv6_range.contains_ip?("2001:db8:ffff::ffff")
|
|
assert_not @ipv6_range.contains_ip?("2001:db9::1")
|
|
end
|
|
|
|
test "contains_network? works correctly" do
|
|
@ipv4_range.save!
|
|
|
|
# More specific network
|
|
assert @ipv4_range.contains_network?("192.168.1.0/25")
|
|
assert @ipv4_range.contains_network?("192.168.1.128/25")
|
|
|
|
# Same network
|
|
assert @ipv4_range.contains_network?("192.168.1.0/24")
|
|
|
|
# Less specific network
|
|
assert_not @ipv4_range.contains_network?("192.168.0.0/16")
|
|
|
|
# Different network
|
|
assert_not @ipv4_range.contains_network?("10.0.0.0/8")
|
|
end
|
|
|
|
test "overlaps? works correctly" do
|
|
@ipv4_range.save!
|
|
|
|
# Overlapping networks
|
|
assert @ipv4_range.overlaps?("192.168.1.0/25") # More specific
|
|
assert @ipv4_range.overlaps?("192.168.0.0/23") # Less specific
|
|
assert @ipv4_range.overlaps?("192.168.1.128/25") # Partial overlap
|
|
|
|
# Non-overlapping
|
|
assert_not @ipv4_range.overlaps?("10.0.0.0/8")
|
|
assert_not @ipv4_range.overlaps?("172.16.0.0/12")
|
|
end
|
|
|
|
# Parent/Child Relationships
|
|
test "parent_ranges finds containing networks" do
|
|
# Create parent and child networks
|
|
parent = NetworkRange.create!(network: "192.168.0.0/16")
|
|
@ipv4_range.save! # 192.168.1.0/24
|
|
child = NetworkRange.create!(network: "192.168.1.0/25")
|
|
|
|
parents = @ipv4_range.parent_ranges
|
|
assert_includes parents, parent
|
|
assert_not_includes parents, child
|
|
assert_not_includes parents, @ipv4_range
|
|
|
|
# Should be ordered by specificity (more specific first)
|
|
assert_equal parent, parents.first
|
|
end
|
|
|
|
test "child_ranges finds contained networks" do
|
|
# Create parent and child networks
|
|
parent = NetworkRange.create!(network: "192.168.0.0/16")
|
|
@ipv4_range.save! # 192.168.1.0/24
|
|
child = NetworkRange.create!(network: "192.168.1.0/25")
|
|
|
|
children = parent.child_ranges
|
|
assert_includes children, @ipv4_range
|
|
assert_includes children, child
|
|
assert_not_includes children, parent
|
|
|
|
# Should be ordered by specificity (less specific first)
|
|
assert_equal @ipv4_range, children.first
|
|
end
|
|
|
|
test "child_ranges works with Apple network hierarchy - 17.240.0.0/14" do
|
|
# This test demonstrates the current bug in child_ranges method
|
|
# Expected: 17.240.0.0/14 should have parents but no children in this test setup
|
|
|
|
# Create the target network
|
|
target_network = NetworkRange.create!(network: "17.240.0.0/14", source: "manual")
|
|
|
|
# Create parent networks
|
|
parent1 = NetworkRange.create!(network: "17.240.0.0/13", source: "manual") # Should contain 17.240.0.0/14
|
|
parent2 = NetworkRange.create!(network: "17.128.0.0/9", source: "manual") # Should also contain 17.240.0.0/14
|
|
|
|
# Create some child networks (more specific networks contained by 17.240.0.0/14)
|
|
child1 = NetworkRange.create!(network: "17.240.0.0/15", source: "manual") # First half of /14
|
|
child2 = NetworkRange.create!(network: "17.242.0.0/15", source: "manual") # Second half of /14
|
|
child3 = NetworkRange.create!(network: "17.240.0.0/16", source: "manual") # More specific
|
|
child4 = NetworkRange.create!(network: "17.241.0.0/16", source: "manual") # More specific
|
|
|
|
# Test parent_ranges works correctly
|
|
parents = target_network.parent_ranges
|
|
assert_includes parents, parent1, "17.240.0.0/13 should be a parent of 17.240.0.0/14"
|
|
assert_includes parents, parent2, "17.128.0.0/9 should be a parent of 17.240.0.0/14"
|
|
|
|
# Test child_ranges - this is currently failing due to the bug
|
|
children = target_network.child_ranges
|
|
assert_includes children, child1, "17.240.0.0/15 should be a child of 17.240.0.0/14"
|
|
assert_includes children, child2, "17.242.0.0/15 should be a child of 17.240.0.0/14"
|
|
assert_includes children, child3, "17.240.0.0/16 should be a child of 17.240.0.0/14"
|
|
assert_includes children, child4, "17.241.0.0/16 should be a child of 17.240.0.0/14"
|
|
assert_not_includes children, parent1, "Parent networks should not be in child_ranges"
|
|
assert_not_includes children, parent2, "Parent networks should not be in child_ranges"
|
|
assert_not_includes children, target_network, "Self should not be in child_ranges"
|
|
|
|
# Test that parent can find child in its child_ranges
|
|
parent1_children = parent1.child_ranges
|
|
assert_includes parent1_children, target_network, "17.240.0.0/14 should be in child_ranges of 17.240.0.0/13"
|
|
|
|
parent2_children = parent2.child_ranges
|
|
assert_includes parent2_children, target_network, "17.240.0.0/14 should be in child_ranges of 17.128.0.0/9"
|
|
|
|
# Test bidirectional consistency
|
|
assert target_network.parent_ranges.include?(parent1), "Parent should list child"
|
|
assert parent1.child_ranges.include?(target_network), "Child should list parent"
|
|
|
|
assert target_network.parent_ranges.include?(parent2), "Parent should list child"
|
|
assert parent2.child_ranges.include?(target_network), "Child should list parent"
|
|
end
|
|
|
|
# Intelligence and Inheritance
|
|
test "has_intelligence? detects available intelligence data" do
|
|
range = NetworkRange.new(network: "10.0.0.0/8")
|
|
assert_not range.has_intelligence?
|
|
|
|
range.asn = 12345
|
|
assert range.has_intelligence?
|
|
|
|
range.asn = nil
|
|
range.company = "Test Company"
|
|
assert range.has_intelligence?
|
|
|
|
range.company = nil
|
|
range.country = "US"
|
|
assert range.has_intelligence?
|
|
|
|
range.country = nil
|
|
range.is_datacenter = true
|
|
assert range.has_intelligence?
|
|
end
|
|
|
|
test "own_intelligence returns correct data structure" do
|
|
range = NetworkRange.create!(
|
|
network: "10.0.0.0/8",
|
|
asn: 12345,
|
|
asn_org: "Test ASN",
|
|
company: "Test Company",
|
|
country: "US",
|
|
is_datacenter: true,
|
|
is_proxy: false,
|
|
is_vpn: false,
|
|
source: "manual"
|
|
)
|
|
|
|
intelligence = range.own_intelligence
|
|
assert_equal 12345, intelligence[:asn]
|
|
assert_equal "Test ASN", intelligence[:asn_org]
|
|
assert_equal "Test Company", intelligence[:company]
|
|
assert_equal "US", intelligence[:country]
|
|
assert_equal true, intelligence[:is_datacenter]
|
|
assert_equal false, intelligence[:is_proxy]
|
|
assert_equal false, intelligence[:is_vpn]
|
|
assert_equal false, intelligence[:inherited]
|
|
assert_equal "manual", intelligence[:source]
|
|
end
|
|
|
|
test "inherited_intelligence returns own data when available" do
|
|
child = NetworkRange.create!(
|
|
network: "192.168.1.0/24",
|
|
country: "US",
|
|
company: "Test Company"
|
|
)
|
|
|
|
intelligence = child.inherited_intelligence
|
|
assert_equal "US", intelligence[:country]
|
|
assert_equal "Test Company", intelligence[:company]
|
|
assert_equal false, intelligence[:inherited]
|
|
end
|
|
|
|
test "inherited_intelligence inherits from parent when needed" do
|
|
parent = NetworkRange.create!(
|
|
network: "192.168.0.0/16",
|
|
country: "US",
|
|
company: "Test Company"
|
|
)
|
|
child = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
intelligence = child.inherited_intelligence
|
|
assert_equal "US", intelligence[:country]
|
|
assert_equal "Test Company", intelligence[:company]
|
|
assert_equal true, intelligence[:inherited]
|
|
assert_equal parent.cidr, intelligence[:parent_cidr]
|
|
end
|
|
|
|
test "parent_with_intelligence finds nearest parent with data" do
|
|
grandparent = NetworkRange.create!(network: "10.0.0.0/8", country: "US")
|
|
parent = NetworkRange.create!(network: "10.1.0.0/16")
|
|
child = NetworkRange.create!(network: "10.1.1.0/24")
|
|
|
|
found_parent = child.parent_with_intelligence
|
|
assert_equal grandparent, found_parent
|
|
assert_not_equal parent, found_parent
|
|
end
|
|
|
|
# API Data Management
|
|
test "is_fetching_api_data? tracks active fetches" do
|
|
range = NetworkRange.create!(network: "10.0.0.0/8")
|
|
|
|
assert_not range.is_fetching_api_data?(:ipapi)
|
|
|
|
range.mark_as_fetching_api_data!(:ipapi)
|
|
assert range.is_fetching_api_data?(:ipapi)
|
|
|
|
range.clear_fetching_status!(:ipapi)
|
|
assert_not range.is_fetching_api_data?(:ipapi)
|
|
end
|
|
|
|
test "should_fetch_api_data? prevents duplicate fetches" do
|
|
range = NetworkRange.create!(network: "10.0.0.0/8")
|
|
|
|
# Should fetch initially
|
|
assert range.should_fetch_api_data?(:ipapi)
|
|
|
|
# Should not fetch while fetching
|
|
range.mark_as_fetching_api_data!(:ipapi)
|
|
assert_not range.should_fetch_api_data?(:ipapi)
|
|
|
|
# Should fetch again after clearing
|
|
range.clear_fetching_status!(:ipapi)
|
|
assert range.should_fetch_api_data?(:ipapi)
|
|
end
|
|
|
|
test "has_ipapi_data_available? checks inheritance" do
|
|
parent = NetworkRange.create!(network: "10.0.0.0/8")
|
|
parent.set_network_data(:ipapi, { country: "US" })
|
|
parent.save!
|
|
|
|
child = NetworkRange.create!(network: "10.0.1.0/24")
|
|
|
|
assert child.has_ipapi_data_available?
|
|
end
|
|
|
|
test "should_fetch_ipapi_data? respects parent fetching status" do
|
|
parent = NetworkRange.create!(network: "10.0.0.0/8")
|
|
child = NetworkRange.create!(network: "10.0.1.0/24")
|
|
|
|
parent.mark_as_fetching_api_data!(:ipapi)
|
|
assert_not child.should_fetch_ipapi_data?
|
|
|
|
parent.clear_fetching_status!(:ipapi)
|
|
assert child.should_fetch_ipapi_data?
|
|
end
|
|
|
|
# Network Data Management
|
|
test "network_data_for and set_network_data work correctly" do
|
|
range = NetworkRange.create!(network: "10.0.0.0/8")
|
|
|
|
assert_equal({}, range.network_data_for(:ipapi))
|
|
|
|
data = { country: "US", city: "New York" }
|
|
range.set_network_data(:ipapi, data)
|
|
range.save!
|
|
|
|
assert_equal data, range.network_data_for(:ipapi)
|
|
end
|
|
|
|
test "has_network_data_from? checks data presence" do
|
|
range = NetworkRange.create!(network: "10.0.0.0/8")
|
|
|
|
assert_not range.has_network_data_from?(:ipapi)
|
|
|
|
range.set_network_data(:ipapi, { country: "US" })
|
|
range.save!
|
|
|
|
assert range.has_network_data_from?(:ipapi)
|
|
end
|
|
|
|
# IPAPI Tracking Methods
|
|
test "find_or_create_tracking_network_for_ip works correctly" do
|
|
# IPv4 - should create /24
|
|
tracking_range = NetworkRange.find_or_create_tracking_network_for_ip("192.168.1.100")
|
|
assert_equal "192.168.1.0/24", tracking_range.network.to_s
|
|
assert_equal "auto_generated", tracking_range.source
|
|
assert_equal "IPAPI tracking network", tracking_range.creation_reason
|
|
|
|
# IPv6 - should create /64
|
|
ipv6_tracking = NetworkRange.find_or_create_tracking_network_for_ip("2001:db8::1")
|
|
assert_equal "2001:db8::/64", ipv6_tracking.network.to_s
|
|
end
|
|
|
|
test "should_fetch_ipapi_for_ip? works correctly" do
|
|
tracking_range = NetworkRange.create!(network: "192.168.1.0/8")
|
|
|
|
# Should fetch initially
|
|
assert NetworkRange.should_fetch_ipapi_for_ip?("192.168.1.100")
|
|
|
|
# Mark as queried recently
|
|
tracking_range.mark_ipapi_queried!("192.168.1.0/24")
|
|
assert_not NetworkRange.should_fetch_ipapi_for_ip?("192.168.1.100")
|
|
|
|
# Should fetch for old queries
|
|
tracking_range.network_data['ipapi_queried_at'] = 2.years.ago.to_i
|
|
tracking_range.save!
|
|
assert NetworkRange.should_fetch_ipapi_for_ip?("192.168.1.100")
|
|
end
|
|
|
|
test "mark_ipapi_queried! stores query metadata" do
|
|
range = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
range.mark_ipapi_queried!("192.168.1.128/25")
|
|
|
|
assert range.network_data['ipapi_queried_at'] > 5.seconds.ago.to_i
|
|
assert_equal "192.168.1.128/25", range.network_data['ipapi_returned_cidr']
|
|
end
|
|
|
|
# JSON Helper Methods
|
|
test "abuser_scores_hash handles JSON correctly" do
|
|
range = NetworkRange.create!(network: "10.0.0.0/8")
|
|
|
|
assert_equal({}, range.abuser_scores_hash)
|
|
|
|
range.abuser_scores_hash = { ipquality: 85, abuseipdb: 92 }
|
|
range.save!
|
|
|
|
assert_equal({ "ipquality" => 85, "abuseipdb" => 92 }, JSON.parse(range.abuser_scores))
|
|
assert_equal({ ipquality: 85, abuseipdb: 92 }, range.abuser_scores_hash)
|
|
end
|
|
|
|
test "additional_data_hash handles JSON correctly" do
|
|
range = NetworkRange.create!(network: "10.0.0.0/8")
|
|
|
|
assert_equal({}, range.additional_data_hash)
|
|
|
|
range.additional_data_hash = { tags: ["malicious", "botnet"], notes: "Test data" }
|
|
range.save!
|
|
|
|
parsed_data = JSON.parse(range.additional_data)
|
|
assert_equal ["malicious", "botnet"], parsed_data["tags"]
|
|
assert_equal "Test data", parsed_data["notes"]
|
|
end
|
|
|
|
# Scopes
|
|
test "ipv4 and ipv6 scopes work correctly" do
|
|
ipv4_range = NetworkRange.create!(network: "192.168.1.0/24")
|
|
ipv6_range = NetworkRange.create!(network: "2001:db8::/32")
|
|
|
|
assert_includes NetworkRange.ipv4, ipv4_range
|
|
assert_not_includes NetworkRange.ipv4, ipv6_range
|
|
|
|
assert_includes NetworkRange.ipv6, ipv6_range
|
|
assert_not_includes NetworkRange.ipv6, ipv4_range
|
|
end
|
|
|
|
test "filtering scopes work correctly" do
|
|
range1 = NetworkRange.create!(network: "192.168.1.0/24", country: "US", company: "Google", asn: 15169, is_datacenter: true)
|
|
range2 = NetworkRange.create!(network: "10.0.0.0/8", country: "BR", company: "Amazon", asn: 16509, is_proxy: true)
|
|
|
|
assert_includes NetworkRange.by_country("US"), range1
|
|
assert_not_includes NetworkRange.by_country("US"), range2
|
|
|
|
assert_includes NetworkRange.by_company("Google"), range1
|
|
assert_not_includes NetworkRange.by_company("Google"), range2
|
|
|
|
assert_includes NetworkRange.by_asn(15169), range1
|
|
assert_not_includes NetworkRange.by_asn(15169), range2
|
|
|
|
assert_includes NetworkRange.datacenter, range1
|
|
assert_not_includes NetworkRange.datacenter, range2
|
|
|
|
assert_includes NetworkRange.proxy, range2
|
|
assert_not_includes NetworkRange.proxy, range1
|
|
end
|
|
|
|
# Class Methods
|
|
test "contains_ip class method finds most specific network" do
|
|
parent = NetworkRange.create!(network: "192.168.0.0/16")
|
|
child = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
found = NetworkRange.contains_ip("192.168.1.100")
|
|
assert_equal child, found.first # More specific should come first
|
|
end
|
|
|
|
test "overlapping class method finds overlapping networks" do
|
|
range1 = NetworkRange.create!(network: "192.168.0.0/16")
|
|
range2 = NetworkRange.create!(network: "192.168.1.0/24")
|
|
range3 = NetworkRange.create!(network: "10.0.0.0/8")
|
|
|
|
overlapping = NetworkRange.overlapping("192.168.1.0/24")
|
|
assert_includes overlapping, range1
|
|
assert_includes overlapping, range2
|
|
assert_not_includes overlapping, range3
|
|
end
|
|
|
|
test "find_or_create_by_cidr works correctly" do
|
|
# Creates new record
|
|
range = NetworkRange.find_or_create_by_cidr("10.0.0.0/8", user: @user, source: "manual")
|
|
assert range.persisted?
|
|
assert_equal "10.0.0.0/8", range.network.to_s
|
|
assert_equal @user, range.user
|
|
assert_equal "manual", range.source
|
|
|
|
# Returns existing record
|
|
existing = NetworkRange.find_or_create_by_cidr("10.0.0.0/8")
|
|
assert_equal range.id, existing.id
|
|
end
|
|
|
|
test "find_by_ip_or_network handles both IP and network inputs" do
|
|
range = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
# Find by IP within range
|
|
found_by_ip = NetworkRange.find_by_ip_or_network("192.168.1.100")
|
|
assert_includes found_by_ip, range
|
|
|
|
# Find by exact network
|
|
found_by_network = NetworkRange.find_by_ip_or_network("192.168.1.0/24")
|
|
assert_includes found_by_network, range
|
|
|
|
# Return none for invalid input
|
|
assert_equal NetworkRange.none, NetworkRange.find_by_ip_or_network("")
|
|
assert_equal NetworkRange.none, NetworkRange.find_by_ip_or_network("invalid")
|
|
end
|
|
|
|
# Analytics Methods
|
|
test "events_count returns counter cache value" do
|
|
range = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
assert_equal 0, range.events_count
|
|
|
|
# Update counter cache manually for testing
|
|
range.update_column(:events_count, 5)
|
|
assert_equal 5, range.events_count
|
|
end
|
|
|
|
test "events method finds events within range" do
|
|
range = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
# Create test events
|
|
matching_event = Event.create!(
|
|
request_id: "test-1",
|
|
timestamp: Time.current,
|
|
payload: {},
|
|
ip_address: "192.168.1.100"
|
|
)
|
|
non_matching_event = Event.create!(
|
|
request_id: "test-2",
|
|
timestamp: Time.current,
|
|
payload: {},
|
|
ip_address: "10.0.0.1"
|
|
)
|
|
|
|
found_events = range.events
|
|
assert_includes found_events, matching_event
|
|
assert_not_includes found_events, non_matching_event
|
|
end
|
|
|
|
test "blocking_rules and active_rules work correctly" do
|
|
range = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
blocking_rule = Rule.create!(
|
|
rule_type: "network",
|
|
action: "deny",
|
|
network_range: range,
|
|
user: @user,
|
|
enabled: true
|
|
)
|
|
allow_rule = Rule.create!(
|
|
rule_type: "network",
|
|
action: "allow",
|
|
network_range: range,
|
|
user: @user,
|
|
enabled: true
|
|
)
|
|
disabled_rule = Rule.create!(
|
|
rule_type: "network",
|
|
action: "deny",
|
|
network_range: range,
|
|
user: @user,
|
|
enabled: false
|
|
)
|
|
|
|
assert_includes range.blocking_rules, blocking_rule
|
|
assert_not_includes range.blocking_rules, allow_rule
|
|
assert_not_includes range.blocking_rules, disabled_rule
|
|
|
|
assert_includes range.active_rules, blocking_rule
|
|
assert_includes range.active_rules, allow_rule
|
|
assert_not_includes range.active_rules, disabled_rule
|
|
end
|
|
|
|
# Policy Evaluation
|
|
test "needs_policy_evaluation? works correctly" do
|
|
range = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
# Should evaluate if never evaluated
|
|
assert range.needs_policy_evaluation?
|
|
|
|
# Should evaluate if policies updated since last evaluation
|
|
range.update!(policies_evaluated_at: 1.hour.ago)
|
|
WafPolicy.create!(name: "Test Policy", policy_type: "country", targets: ["US"], policy_action: "deny", user: @user)
|
|
assert range.needs_policy_evaluation?
|
|
|
|
# Should not evaluate if up to date
|
|
range.update!(policies_evaluated_at: 5.minutes.ago)
|
|
assert_not range.needs_policy_evaluation?
|
|
end
|
|
|
|
# String Representations
|
|
test "to_s returns cidr" do
|
|
@ipv4_range.save!
|
|
assert_equal @ipv4_range.cidr, @ipv4_range.to_s
|
|
end
|
|
|
|
test "to_param parameterizes cidr" do
|
|
@ipv4_range.save!
|
|
assert_equal "192.168.1.0_24", @ipv4_range.to_param
|
|
end
|
|
|
|
# Geographic Lookup
|
|
test "geo_lookup_country! updates country when available" do
|
|
range = NetworkRange.create!(network: "8.8.8.0/24") # Google's network
|
|
|
|
# Mock GeoIpService
|
|
GeoIpService.expects(:lookup_country).with("8.8.8.0").returns("US")
|
|
|
|
range.geo_lookup_country!
|
|
assert_equal "US", range.reload.country
|
|
end
|
|
|
|
test "geo_lookup_country! handles errors gracefully" do
|
|
range = NetworkRange.create!(network: "192.168.1.0/24")
|
|
|
|
# Mock GeoIpService to raise error
|
|
GeoIpService.expects(:lookup_country).with("192.168.1.0").raises(StandardError.new("Service error"))
|
|
|
|
# Should not raise error but log it
|
|
assert_nothing_raised do
|
|
range.geo_lookup_country!
|
|
end
|
|
|
|
assert_nil range.reload.country
|
|
end
|
|
|
|
# Stats Methods
|
|
test "import_stats_by_source returns statistics" do
|
|
NetworkRange.create!(network: "10.0.0.0/8", source: "manual")
|
|
NetworkRange.create!(network: "192.168.1.0/24", source: "api_imported")
|
|
NetworkRange.create!(network: "172.16.0.0/12", source: "api_imported")
|
|
|
|
stats = NetworkRange.import_stats_by_source
|
|
assert_equal 2, stats.count
|
|
|
|
api_stats = stats.find { |s| s.source == "api_imported" }
|
|
assert_equal 2, api_stats.count
|
|
end
|
|
|
|
test "geolite_coverage_stats returns detailed coverage information" do
|
|
NetworkRange.create!(network: "10.0.0.0/8", source: "geolite_asn", asn: 12345)
|
|
NetworkRange.create!(network: "192.168.1.0/24", source: "geolite_country", country: "US")
|
|
NetworkRange.create!(network: "172.16.0.0/12", source: "geolite_asn", country: "BR")
|
|
|
|
stats = NetworkRange.geolite_coverage_stats
|
|
assert_equal 3, stats[:total_networks]
|
|
assert_equal 2, stats[:asn_networks]
|
|
assert_equal 1, stats[:country_networks]
|
|
assert_equal 2, stats[:with_asn_data]
|
|
assert_equal 1, stats[:with_country_data]
|
|
assert_equal 2, stats[:unique_countries]
|
|
assert_equal 2, stats[:unique_asns]
|
|
end
|
|
end |