Fix some blocked/allow laggards after migrating. Add DuckDB for outstanding analyitcs performance. Start adding an import for all bot networks
This commit is contained in:
233
test/models/rule_path_pattern_test.rb
Normal file
233
test/models/rule_path_pattern_test.rb
Normal file
@@ -0,0 +1,233 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
require "test_helper"
|
||||
|
||||
class RulePathPatternTest < ActiveSupport::TestCase
|
||||
setup do
|
||||
@user = User.create!(email_address: "test@example.com", password: "password123")
|
||||
end
|
||||
|
||||
test "create_path_pattern_rule creates valid rule" do
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/admin/users",
|
||||
match_type: "exact",
|
||||
action: "deny",
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert rule.persisted?, "Rule should be persisted"
|
||||
assert_equal "path_pattern", rule.waf_rule_type
|
||||
assert_equal "deny", rule.waf_action
|
||||
assert_equal "exact", rule.path_match_type
|
||||
assert_equal 2, rule.path_segment_ids.length
|
||||
end
|
||||
|
||||
test "create_path_pattern_rule auto-creates PathSegments" do
|
||||
initial_count = PathSegment.count
|
||||
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/new/path/here",
|
||||
match_type: "prefix",
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert_equal initial_count + 3, PathSegment.count, "Should create 3 new segments"
|
||||
assert_equal 3, rule.path_segment_ids.length
|
||||
end
|
||||
|
||||
test "create_path_pattern_rule normalizes to lowercase" do
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/Admin/Users",
|
||||
match_type: "exact",
|
||||
user: @user
|
||||
)
|
||||
|
||||
segments = rule.path_segments_text
|
||||
assert_equal ["admin", "users"], segments, "Segments should be lowercase"
|
||||
end
|
||||
|
||||
test "create_path_pattern_rule reuses existing PathSegments" do
|
||||
# Create segment first
|
||||
PathSegment.find_or_create_segment("admin")
|
||||
initial_count = PathSegment.count
|
||||
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/admin",
|
||||
match_type: "exact",
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert_equal initial_count, PathSegment.count, "Should not create duplicate segment"
|
||||
assert_equal 1, rule.path_segment_ids.length
|
||||
end
|
||||
|
||||
test "create_path_pattern_rule validates match_type" do
|
||||
assert_raises(ArgumentError, "Should raise for invalid match_type") do
|
||||
Rule.create_path_pattern_rule(
|
||||
pattern: "/admin",
|
||||
match_type: "invalid",
|
||||
user: @user
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
test "create_path_pattern_rule validates pattern not empty" do
|
||||
assert_raises(ArgumentError, "Should raise for empty pattern") do
|
||||
Rule.create_path_pattern_rule(
|
||||
pattern: "/",
|
||||
match_type: "exact",
|
||||
user: @user
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
test "validation requires segment_ids for path_pattern rules" do
|
||||
rule = Rule.new(
|
||||
waf_rule_type: "path_pattern",
|
||||
waf_action: "deny",
|
||||
conditions: { match_type: "exact" }, # Missing segment_ids
|
||||
user: @user
|
||||
)
|
||||
|
||||
refute rule.valid?, "Rule should be invalid without segment_ids"
|
||||
assert_includes rule.errors[:conditions], "must include 'segment_ids' array for path_pattern rules"
|
||||
end
|
||||
|
||||
test "validation requires match_type for path_pattern rules" do
|
||||
admin_seg = PathSegment.find_or_create_segment("admin")
|
||||
|
||||
rule = Rule.new(
|
||||
waf_rule_type: "path_pattern",
|
||||
waf_action: "deny",
|
||||
conditions: { segment_ids: [admin_seg.id] }, # Missing match_type
|
||||
user: @user
|
||||
)
|
||||
|
||||
refute rule.valid?, "Rule should be invalid without match_type"
|
||||
assert_includes rule.errors[:conditions], "match_type must be one of: exact, prefix, suffix, contains"
|
||||
end
|
||||
|
||||
test "validation checks match_type is valid" do
|
||||
admin_seg = PathSegment.find_or_create_segment("admin")
|
||||
|
||||
rule = Rule.new(
|
||||
waf_rule_type: "path_pattern",
|
||||
waf_action: "deny",
|
||||
conditions: { segment_ids: [admin_seg.id], match_type: "invalid" },
|
||||
user: @user
|
||||
)
|
||||
|
||||
refute rule.valid?, "Rule should be invalid with invalid match_type"
|
||||
assert_includes rule.errors[:conditions], "match_type must be one of: exact, prefix, suffix, contains"
|
||||
end
|
||||
|
||||
test "validation checks segment IDs exist" do
|
||||
rule = Rule.new(
|
||||
waf_rule_type: "path_pattern",
|
||||
waf_action: "deny",
|
||||
conditions: { segment_ids: [99999], match_type: "exact" }, # Non-existent ID
|
||||
user: @user
|
||||
)
|
||||
|
||||
refute rule.valid?, "Rule should be invalid with non-existent segment IDs"
|
||||
assert_match(/non-existent path segment IDs/, rule.errors[:conditions].first)
|
||||
end
|
||||
|
||||
test "path_pattern_display returns human-readable path" do
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/admin/users",
|
||||
match_type: "exact",
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert_equal "/admin/users", rule.path_pattern_display
|
||||
end
|
||||
|
||||
test "path_segments_text returns segment text array" do
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/api/v1/users",
|
||||
match_type: "prefix",
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert_equal ["api", "v1", "users"], rule.path_segments_text
|
||||
end
|
||||
|
||||
test "to_agent_format includes segment_ids and match_type for path rules" do
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/admin",
|
||||
match_type: "prefix",
|
||||
action: "deny",
|
||||
user: @user
|
||||
)
|
||||
|
||||
agent_format = rule.to_agent_format
|
||||
|
||||
assert_equal "path_pattern", agent_format[:waf_rule_type]
|
||||
assert_equal "deny", agent_format[:waf_action]
|
||||
assert agent_format[:conditions].key?(:segment_ids), "Should include segment_ids"
|
||||
assert_equal "prefix", agent_format[:conditions][:match_type]
|
||||
assert_kind_of Array, agent_format[:conditions][:segment_ids]
|
||||
end
|
||||
|
||||
test "supports all four match types" do
|
||||
%w[exact prefix suffix contains].each do |match_type|
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/admin",
|
||||
match_type: match_type,
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert rule.persisted?, "Should create rule with #{match_type} match type"
|
||||
assert_equal match_type, rule.path_match_type
|
||||
end
|
||||
end
|
||||
|
||||
test "supports all action types" do
|
||||
%w[allow deny challenge].each do |action|
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/admin",
|
||||
match_type: "exact",
|
||||
action: action,
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert rule.persisted?, "Should create rule with #{action} action"
|
||||
assert_equal action, rule.waf_action
|
||||
end
|
||||
end
|
||||
|
||||
test "supports redirect action with metadata" do
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/admin",
|
||||
match_type: "exact",
|
||||
action: "redirect",
|
||||
user: @user,
|
||||
metadata: { redirect_url: "https://example.com" }
|
||||
)
|
||||
|
||||
assert rule.persisted?, "Should create rule with redirect action"
|
||||
assert_equal "redirect", rule.waf_action
|
||||
end
|
||||
|
||||
test "stores metadata with human-readable segments" do
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/admin/users",
|
||||
match_type: "exact",
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert_equal ["admin", "users"], rule.metadata["segments"]
|
||||
assert_equal "/admin/users", rule.metadata["pattern_display"]
|
||||
end
|
||||
|
||||
test "stores original pattern in conditions" do
|
||||
rule = Rule.create_path_pattern_rule(
|
||||
pattern: "/Admin/Users", # Mixed case
|
||||
match_type: "exact",
|
||||
user: @user
|
||||
)
|
||||
|
||||
assert_equal "/Admin/Users", rule.conditions["original_pattern"]
|
||||
end
|
||||
end
|
||||
Reference in New Issue
Block a user