Many updates
This commit is contained in:
675
test/models/network_range_test.rb
Normal file
675
test/models/network_range_test.rb
Normal file
@@ -0,0 +1,675 @@
|
||||
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 "sibling_ranges finds same-level networks" do
|
||||
# Create sibling networks
|
||||
sibling1 = NetworkRange.create!(network: "192.168.0.0/24")
|
||||
@ipv4_range.save! # 192.168.1.0/24
|
||||
sibling2 = NetworkRange.create!(network: "192.168.2.0/24")
|
||||
|
||||
siblings = @ipv4_range.sibling_ranges
|
||||
assert_includes siblings, sibling1
|
||||
assert_includes siblings, sibling2
|
||||
assert_not_includes siblings, @ipv4_range
|
||||
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
|
||||
Reference in New Issue
Block a user