Refer to correct scope from User.matches_ip and expand InetContainer coverage (#39501)

This commit is contained in:
Matt Jankowski
2026-06-22 06:17:19 -04:00
committed by GitHub
parent f914351cbc
commit 86097bb2c2
4 changed files with 74 additions and 1 deletions

View File

@@ -110,7 +110,7 @@ class User < ApplicationRecord
scope :disabled, -> { where(disabled: true) }
scope :active, -> { confirmed.signed_in_recently.account_not_suspended }
scope :matches_email, ->(value) { where(arel_table[:email].matches("#{value}%")) }
scope :matches_ip, ->(value) { left_joins(:ips).merge(IpBlock.contained_by(value)).group(users: [:id]) }
scope :matches_ip, ->(value) { left_joins(:ips).merge(UserIp.contained_by(value)).group(users: [:id]) }
before_validation :sanitize_role
before_create :set_approved

View File

@@ -4,6 +4,7 @@ require 'rails_helper'
RSpec.describe IpBlock do
it_behaves_like 'Expireable'
it_behaves_like 'InetContainer'
describe 'Validations' do
subject { Fabricate.build :ip_block }

View File

@@ -0,0 +1,28 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe UserIp do
describe 'Scopes' do
describe '.by_latest_used' do
let!(:user) { Fabricate :user, sign_up_ip: '192.168.0.1', created_at: 15.days.ago }
let!(:other_user) { Fabricate :user, sign_up_ip: '10.0.10.0', created_at: 10.days.ago }
it 'returns records ordered by most recent usage' do
expect(described_class.by_latest_used)
.to eq([other_user.ips.last, user.ips.last])
end
end
describe '.contained_by' do
let!(:user) { Fabricate :user, sign_up_ip: '192.168.0.1' }
let!(:other_user) { Fabricate :user, sign_up_ip: '10.0.10.0' }
it 'returns records ordered by rank' do
expect(described_class.contained_by('192.168.0.0/24'))
.to include(user.ips.last)
.and not_include(other_user.ips.last)
end
end
end
end

View File

@@ -0,0 +1,44 @@
# frozen_string_literal: true
RSpec.shared_examples 'InetContainer' do
describe 'Scopes' do
describe '.containing' do
let!(:contained) { Fabricate factory_name, ip: '192.168.0.0/24' }
let!(:uncontained) { Fabricate factory_name, ip: '10.0.0.0/16' }
it 'returns records containing the value' do
expect(described_class.containing('192.168.0.1'))
.to include(contained)
.and not_include(uncontained)
end
end
describe '.contained_by' do
let!(:contained) { Fabricate factory_name, ip: '192.168.0.1' }
let!(:uncontained) { Fabricate factory_name, ip: '10.0.10.0' }
it 'returns records contained by the value' do
expect(described_class.contained_by('192.168.0.0/24'))
.to include(contained)
.and not_include(uncontained)
end
end
describe '.overlapping_with' do
let!(:contained) { Fabricate factory_name, ip: '192.168.0.0/16' }
let!(:contained_also) { Fabricate factory_name, ip: '192.168.0.1' }
let!(:uncontained) { Fabricate factory_name, ip: '10.0.10.0' }
it 'returns records containing or contained by the value' do
expect(described_class.overlapping_with('192.168.0.0/24'))
.to include(contained)
.and include(contained_also)
.and not_include(uncontained)
end
end
end
def factory_name
described_class.name.underscore.to_sym
end
end