# frozen_string_literal: true require "test_helper" class RuleTest < ActiveSupport::TestCase # Validation tests test "should create valid network_v4 rule" do rule = Rule.new( rule_type: "network_v4", action: "deny", conditions: { cidr: "10.0.0.0/8" }, source: "manual" ) assert rule.valid? rule.save! assert_equal 8, rule.priority # Auto-calculated from CIDR prefix end test "should create valid network_v6 rule" do rule = Rule.new( rule_type: "network_v6", action: "deny", conditions: { cidr: "2001:db8::/32" }, source: "manual" ) assert rule.valid? rule.save! assert_equal 32, rule.priority end test "should create valid rate_limit rule" do rule = Rule.new( rule_type: "rate_limit", action: "rate_limit", conditions: { cidr: "0.0.0.0/0", scope: "global" }, metadata: { limit: 100, window: 60 }, source: "manual" ) assert rule.valid? end test "should create valid path_pattern rule" do rule = Rule.new( rule_type: "path_pattern", action: "log", conditions: { patterns: ["/.env", "/.git"] }, source: "default" ) assert rule.valid? end test "should require rule_type" do rule = Rule.new(action: "deny", conditions: { cidr: "10.0.0.0/8" }) assert_not rule.valid? assert_includes rule.errors[:rule_type], "can't be blank" end test "should require action" do rule = Rule.new(rule_type: "network_v4", conditions: { cidr: "10.0.0.0/8" }) assert_not rule.valid? assert_includes rule.errors[:action], "can't be blank" end test "should validate network_v4 has valid IPv4 CIDR" do rule = Rule.new( rule_type: "network_v4", action: "deny", conditions: { cidr: "2001:db8::/32" } # IPv6 in IPv4 rule ) assert_not rule.valid? assert_includes rule.errors[:conditions], "cidr must be IPv4 for network_v4 rules" end test "should validate rate_limit has limit and window in metadata" do rule = Rule.new( rule_type: "rate_limit", action: "rate_limit", conditions: { cidr: "0.0.0.0/0", scope: "global" }, metadata: { limit: 100 } # Missing window ) assert_not rule.valid? assert_includes rule.errors[:metadata], "must include 'limit' and 'window' for rate_limit rules" end # Default value tests test "should default enabled to true" do rule = Rule.create!( rule_type: "network_v4", action: "deny", conditions: { cidr: "10.0.0.0/8" } ) assert rule.enabled? end # Priority calculation tests test "should calculate priority from IPv4 CIDR prefix" do rule = Rule.create!( rule_type: "network_v4", action: "deny", conditions: { cidr: "192.168.1.0/24" } ) assert_equal 24, rule.priority end # Scope tests test "active scope returns enabled and non-expired rules" do active = Rule.create!( rule_type: "network_v4", action: "deny", conditions: { cidr: "10.0.0.0/8" }, enabled: true ) disabled = Rule.create!( rule_type: "network_v4", action: "deny", conditions: { cidr: "192.168.0.0/16" }, enabled: false ) expired = Rule.create!( rule_type: "network_v4", action: "deny", conditions: { cidr: "172.16.0.0/12" }, enabled: true, expires_at: 1.hour.ago ) results = Rule.active.to_a assert_includes results, active assert_not_includes results, disabled assert_not_includes results, expired end # Instance method tests test "active? returns true for enabled non-expired rule" do rule = Rule.create!( rule_type: "network_v4", action: "deny", conditions: { cidr: "10.0.0.0/8" }, enabled: true ) assert rule.active? end test "disable! sets enabled to false and adds metadata" do rule = Rule.create!( rule_type: "network_v4", action: "deny", conditions: { cidr: "10.0.0.0/8" } ) rule.disable!(reason: "False positive") assert_not rule.enabled? assert_equal "False positive", rule.metadata["disabled_reason"] assert rule.metadata["disabled_at"].present? end # Agent format tests test "to_agent_format returns correct structure" do rule = Rule.create!( rule_type: "network_v4", action: "deny", conditions: { cidr: "10.0.0.0/8" }, expires_at: 1.day.from_now, source: "manual", metadata: { reason: "Test" } ) format = rule.to_agent_format assert_equal rule.id, format[:id] assert_equal "network_v4", format[:rule_type] assert_equal "deny", format[:action] assert_equal 8, format[:priority] assert_equal true, format[:enabled] end end