Skip to content

Commit

Permalink
Add specs for Instance model scopes and add with_domain_follows s…
Browse files Browse the repository at this point in the history
…cope (mastodon#28767)
  • Loading branch information
mjankowski committed Jan 25, 2024
1 parent 4cdf62e commit 42ab855
Show file tree
Hide file tree
Showing 3 changed files with 123 additions and 1 deletion.
6 changes: 5 additions & 1 deletion app/controllers/admin/export_domain_blocks_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def import
next
end

@warning_domains = Instance.where(domain: @domain_blocks.map(&:domain)).where('EXISTS (SELECT 1 FROM follows JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id WHERE accounts.domain = instances.domain)').pluck(:domain)
@warning_domains = instances_from_imported_blocks.pluck(:domain)
rescue ActionController::ParameterMissing
flash.now[:alert] = I18n.t('admin.export_domain_blocks.no_file')
set_dummy_import!
Expand All @@ -58,6 +58,10 @@ def import

private

def instances_from_imported_blocks
Instance.with_domain_follows(@domain_blocks.map(&:domain))
end

def export_filename
'domain_blocks.csv'
end
Expand Down
14 changes: 14 additions & 0 deletions app/models/instance.rb
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,25 @@ class Instance < ApplicationRecord
scope :matches_domain, ->(value) { where(arel_table[:domain].matches("%#{value}%")) }
scope :domain_starts_with, ->(value) { where(arel_table[:domain].matches("#{sanitize_sql_like(value)}%", false, true)) }
scope :by_domain_and_subdomains, ->(domain) { where("reverse('.' || domain) LIKE reverse(?)", "%.#{domain}") }
scope :with_domain_follows, ->(domains) { where(domain: domains).where(domain_account_follows) }

def self.refresh
Scenic.database.refresh_materialized_view(table_name, concurrently: true, cascade: false)
end

def self.domain_account_follows
Arel.sql(
<<~SQL.squish
EXISTS (
SELECT 1
FROM follows
JOIN accounts ON follows.account_id = accounts.id OR follows.target_account_id = accounts.id
WHERE accounts.domain = instances.domain
)
SQL
)
end

def readonly?
true
end
Expand Down
104 changes: 104 additions & 0 deletions spec/models/instance_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
# frozen_string_literal: true

require 'rails_helper'

RSpec.describe Instance do
describe 'Scopes' do
before { described_class.refresh }

describe '#searchable' do
let(:expected_domain) { 'host.example' }
let(:blocked_domain) { 'other.example' }

before do
Fabricate :account, domain: expected_domain
Fabricate :account, domain: blocked_domain
Fabricate :domain_block, domain: blocked_domain
end

it 'returns records not domain blocked' do
results = described_class.searchable.pluck(:domain)

expect(results)
.to include(expected_domain)
.and not_include(blocked_domain)
end
end

describe '#matches_domain' do
let(:host_domain) { 'host.example.com' }
let(:host_under_domain) { 'host_under.example.com' }
let(:other_domain) { 'other.example' }

before do
Fabricate :account, domain: host_domain
Fabricate :account, domain: host_under_domain
Fabricate :account, domain: other_domain
end

it 'returns matching records' do
expect(described_class.matches_domain('host.exa').pluck(:domain))
.to include(host_domain)
.and not_include(other_domain)

expect(described_class.matches_domain('ple.com').pluck(:domain))
.to include(host_domain)
.and not_include(other_domain)

expect(described_class.matches_domain('example').pluck(:domain))
.to include(host_domain)
.and include(other_domain)

expect(described_class.matches_domain('host_').pluck(:domain)) # Preserve SQL wildcards
.to include(host_domain)
.and include(host_under_domain)
.and not_include(other_domain)
end
end

describe '#by_domain_and_subdomains' do
let(:exact_match_domain) { 'example.com' }
let(:subdomain_domain) { 'foo.example.com' }
let(:partial_domain) { 'grexample.com' }

before do
Fabricate(:account, domain: exact_match_domain)
Fabricate(:account, domain: subdomain_domain)
Fabricate(:account, domain: partial_domain)
end

it 'returns matching instances' do
results = described_class.by_domain_and_subdomains('example.com').pluck(:domain)

expect(results)
.to include(exact_match_domain)
.and include(subdomain_domain)
.and not_include(partial_domain)
end
end

describe '#with_domain_follows' do
let(:example_domain) { 'example.host' }
let(:other_domain) { 'other.host' }
let(:none_domain) { 'none.host' }

before do
example_account = Fabricate(:account, domain: example_domain)
other_account = Fabricate(:account, domain: other_domain)
Fabricate(:account, domain: none_domain)

Fabricate :follow, account: example_account
Fabricate :follow, target_account: other_account
end

it 'returns instances with domain accounts that have follows' do
results = described_class.with_domain_follows(['example.host', 'other.host', 'none.host']).pluck(:domain)

expect(results)
.to include(example_domain)
.and include(other_domain)
.and not_include(none_domain)
end
end
end
end

0 comments on commit 42ab855

Please sign in to comment.