DEV: Isolate multisite specs (#13634)

Mixing multisite and standard specs can lead to issues (e.g. when using `fab!`)
Disabled the (upcoming https://github.com/discourse/rubocop-discourse/pull/11) rubocop rule for two files that have thoroughly tangled both types of specs.
This commit is contained in:
Jarek Radosz 2021-07-07 18:57:42 +02:00 committed by GitHub
parent 14a13dc192
commit 48b92d8897
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
21 changed files with 429 additions and 314 deletions

View File

@ -0,0 +1,29 @@
# frozen_string_literal: true
require "rails_helper"
require "s3_helper"
require "s3_inventory"
require "file_store/s3_store"
describe "S3Inventory", type: :multisite do
let(:client) { Aws::S3::Client.new(stub_responses: true) }
let(:helper) { S3Helper.new(SiteSetting.Upload.s3_upload_bucket.downcase, "", client: client) }
let(:inventory) { S3Inventory.new(helper, :upload) }
let(:csv_filename) { "#{Rails.root}/spec/fixtures/csv/s3_inventory.csv" }
it "can create per-site files" do
freeze_time
setup_s3
SiteSetting.enable_s3_inventory = true
inventory.stubs(:files).returns([{ key: "Key", filename: "#{csv_filename}.gz" }])
inventory.stubs(:cleanup!)
files = inventory.prepare_for_all_sites
db1 = files["default"].read
db2 = files["second"].read
expect(db1.lines.count).to eq(3)
expect(db2.lines.count).to eq(1)
files.values.each { |f| f.close; f.unlink }
end
end

View File

@ -175,19 +175,6 @@ describe "S3Inventory" do
expect(Discourse.stats.get("missing_s3_uploads")).to eq(2)
end
it "can create per-site files", type: :multisite do
freeze_time
inventory.stubs(:files).returns([{ key: "Key", filename: "#{csv_filename}.gz" }])
files = inventory.prepare_for_all_sites
db1 = files["default"].read
db2 = files["second"].read
expect(db1.lines.count).to eq(3)
expect(db2.lines.count).to eq(1)
files.values.each { |f| f.close; f.unlink }
end
context "s3 inventory configuration" do
let(:bucket_name) { "s3-upload-bucket" }
let(:subfolder_path) { "subfolder" }

View File

@ -0,0 +1,30 @@
# frozen_string_literal: true
require "rails_helper"
describe SiteSettingExtension, type: :multisite do
before do
MessageBus.off
end
after do
MessageBus.on
end
let(:provider_local) do
SiteSettings::LocalProcessProvider.new
end
let(:settings) do
new_settings(provider_local)
end
it "has no db cross talk" do
settings.setting(:hello, 1)
settings.hello = 100
test_multisite_connection("second") do
expect(settings.hello).to eq(1)
end
end
end

View File

@ -162,17 +162,6 @@ describe SiteSettingExtension do
end
end
describe "multisite" do
it "has no db cross talk", type: :multisite do
settings.setting(:hello, 1)
settings.hello = 100
test_multisite_connection("second") do
expect(settings.hello).to eq(1)
end
end
end
describe "DiscourseEvent" do
before do
settings.setting(:test_setting, 1)

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
require "rails_helper"
describe SiteSettings::LocalProcessProvider, type: :multisite do
def expect_same_setting(actual, expected)
expect(actual.name).to eq(expected.name)
expect(actual.value).to eq(expected.value)
expect(actual.data_type).to eq(expected.data_type)
end
let(:provider) { described_class.new }
def setting(name, value, data_type)
described_class::Setting.new(name, data_type).tap { |s| s.value = value }
end
it "loads the correct settings" do
test_multisite_connection("default") { provider.save("test", "bla-default", 2) }
test_multisite_connection("second") { provider.save("test", "bla-second", 2) }
test_multisite_connection("default") do
expect_same_setting(provider.find("test"), setting("test", "bla-default", 2))
end
test_multisite_connection("second") do
expect_same_setting(provider.find("test"), setting("test", "bla-second", 2))
end
end
it "returns the correct site name" do
test_multisite_connection("second") do
expect(provider.current_site).to eq("second")
end
end
end

View File

@ -61,25 +61,4 @@ describe SiteSettings::LocalProcessProvider do
it "returns the correct site name" do
expect(provider.current_site).to eq("default")
end
describe "multisite", type: :multisite do
it "loads the correct settings" do
test_multisite_connection("default") { provider.save("test", "bla-default", 2) }
test_multisite_connection("second") { provider.save("test", "bla-second", 2) }
test_multisite_connection("default") do
expect_same_setting(provider.find("test"), setting("test", "bla-default", 2))
end
test_multisite_connection("second") do
expect_same_setting(provider.find("test"), setting("test", "bla-second", 2))
end
end
it "returns the correct site name" do
test_multisite_connection("second") do
expect(provider.current_site).to eq("second")
end
end
end
end

View File

@ -49,7 +49,6 @@ describe 'multisite', type: :multisite do
end
it "should hit correct site elsewise" do
headers = {
"HTTP_HOST" => "test2.localhost",
"REQUEST_METHOD" => "GET",
@ -61,5 +60,4 @@ describe 'multisite', type: :multisite do
expect(code).to eq(200)
expect(body.join).to eq("test2.localhost")
end
end

View File

@ -0,0 +1,18 @@
# frozen_string_literal: true
require "rails_helper"
require_relative "shared_context_for_backup_restore"
describe BackupRestore::BackupFileHandler, type: :multisite do
include_context "shared stuff"
it "works with old backup file format" do
test_multisite_connection("second") do
expect_decompress_and_clean_up_to_work(
backup_filename: "backup_till_v1.5.tar.gz",
require_metadata_file: true,
require_uploads: true
)
end
end
end

View File

@ -6,68 +6,6 @@ require_relative 'shared_context_for_backup_restore'
describe BackupRestore::BackupFileHandler do
include_context "shared stuff"
def expect_decompress_and_clean_up_to_work(backup_filename:, expected_dump_filename: "dump.sql",
require_metadata_file:, require_uploads:, expected_upload_paths: nil,
location: nil)
freeze_time(DateTime.parse('2019-12-24 14:31:48'))
source_file = File.join(Rails.root, "spec/fixtures/backups", backup_filename)
target_directory = BackupRestore::LocalBackupStore.base_directory
target_file = File.join(target_directory, backup_filename)
FileUtils.copy_file(source_file, target_file)
Dir.mktmpdir do |root_directory|
current_db = RailsMultisite::ConnectionManagement.current_db
file_handler = BackupRestore::BackupFileHandler.new(
logger, backup_filename, current_db,
root_tmp_directory: root_directory,
location: location
)
tmp_directory, db_dump_path = file_handler.decompress
expected_tmp_path = File.join(root_directory, "tmp/restores", current_db, "2019-12-24-143148")
expect(tmp_directory).to eq(expected_tmp_path)
expect(db_dump_path).to eq(File.join(expected_tmp_path, expected_dump_filename))
expect(Dir.exist?(tmp_directory)).to eq(true)
expect(File.exist?(db_dump_path)).to eq(true)
expect(File.exist?(File.join(tmp_directory, "meta.json"))).to eq(require_metadata_file)
if require_uploads
expected_upload_paths ||= ["uploads/default/original/3X/b/d/bd269860bb508aebcb6f08fe7289d5f117830383.png"]
expected_upload_paths.each do |upload_path|
absolute_upload_path = File.join(tmp_directory, upload_path)
expect(File.exist?(absolute_upload_path)).to eq(true), "expected file #{upload_path} does not exist"
yield(absolute_upload_path) if block_given?
end
else
expect(Dir.exist?(File.join(tmp_directory, "uploads"))).to eq(false)
end
file_handler.clean_up
expect(Dir.exist?(tmp_directory)).to eq(false)
end
ensure
FileUtils.rm(target_file)
# We don't want to delete the directory unless it is empty, otherwise this could be annoying
# when tests run for the "default" database in a development environment.
FileUtils.rmdir(target_directory) rescue nil
end
it "works with old backup file format", type: :multisite do
test_multisite_connection("second") do
expect_decompress_and_clean_up_to_work(
backup_filename: "backup_till_v1.5.tar.gz",
require_metadata_file: true,
require_uploads: true
)
end
end
it "works with current backup file format" do
expect_decompress_and_clean_up_to_work(
backup_filename: "backup_since_v1.6.tar.gz",

View File

@ -0,0 +1,22 @@
# frozen_string_literal: true
require "rails_helper"
require_relative "shared_context_for_backup_restore"
describe BackupRestore::DatabaseRestorer, type: :multisite do
include_context "shared stuff"
let(:current_db) { RailsMultisite::ConnectionManagement.current_db }
subject { BackupRestore::DatabaseRestorer.new(logger, current_db) }
describe "#restore" do
context "database connection" do
it "reconnects to the correct database" do
RailsMultisite::ConnectionManagement.establish_connection(db: "second")
execute_stubbed_restore
expect(RailsMultisite::ConnectionManagement.current_db).to eq("second")
end
end
end
end

View File

@ -7,56 +7,9 @@ describe BackupRestore::DatabaseRestorer do
include_context "shared stuff"
let(:current_db) { RailsMultisite::ConnectionManagement.current_db }
subject { BackupRestore::DatabaseRestorer.new(logger, current_db) }
def expect_create_readonly_functions
Migration::BaseDropper.expects(:create_readonly_function).at_least_once
end
def expect_table_move
BackupRestore.expects(:move_tables_between_schemas).with("public", "backup").once
end
def expect_psql(output_lines: ["output from psql"], exit_status: 0, stub_thread: false)
status = mock("psql status")
status.expects(:exitstatus).returns(exit_status).once
Process.expects(:last_status).returns(status).once
if stub_thread
thread = mock("thread")
thread.stubs(:join)
Thread.stubs(:new).returns(thread)
end
output_lines << nil
psql_io = mock("psql")
psql_io.expects(:readline).returns(*output_lines).times(output_lines.size)
IO.expects(:popen).yields(psql_io).once
end
def expect_db_migrate
Discourse::Utils.expects(:execute_command).with do |env, *command, **options|
env["SKIP_POST_DEPLOYMENT_MIGRATIONS"] == "0" &&
env["SKIP_OPTIMIZE_ICONS"] == "1" &&
env["DISABLE_TRANSLATION_OVERRIDES"] == "1" &&
command == ["rake", "db:migrate"] &&
options[:chdir] == Rails.root
end.once
end
def expect_db_reconnect
RailsMultisite::ConnectionManagement.expects(:establish_connection).once
end
def execute_stubbed_restore(stub_readonly_functions: true, stub_psql: true, stub_migrate: true,
dump_file_path: "foo.sql")
expect_table_move
expect_create_readonly_functions if stub_readonly_functions
expect_psql if stub_psql
expect_db_migrate if stub_migrate
subject.restore(dump_file_path)
end
describe "#restore" do
it "executes everything in the correct order" do
restore = sequence("restore")
@ -173,12 +126,6 @@ describe BackupRestore::DatabaseRestorer do
end
context "database connection" do
it 'reconnects to the correct database', type: :multisite do
RailsMultisite::ConnectionManagement.establish_connection(db: 'second')
execute_stubbed_restore
expect(RailsMultisite::ConnectionManagement.current_db).to eq('second')
end
it 'it is not erroring for non-multisite' do
expect { execute_stubbed_restore }.not_to raise_error
end

View File

@ -1,9 +1,117 @@
# frozen_string_literal: true
#
shared_context "shared stuff" do
let!(:logger) do
Class.new do
def log(message, ex = nil); end
end.new
end
def expect_create_readonly_functions
Migration::BaseDropper.expects(:create_readonly_function).at_least_once
end
def expect_table_move
BackupRestore.expects(:move_tables_between_schemas).with("public", "backup").once
end
def expect_psql(output_lines: ["output from psql"], exit_status: 0, stub_thread: false)
status = mock("psql status")
status.expects(:exitstatus).returns(exit_status).once
Process.expects(:last_status).returns(status).once
if stub_thread
thread = mock("thread")
thread.stubs(:join)
Thread.stubs(:new).returns(thread)
end
output_lines << nil
psql_io = mock("psql")
psql_io.expects(:readline).returns(*output_lines).times(output_lines.size)
IO.expects(:popen).yields(psql_io).once
end
def expect_db_migrate
Discourse::Utils.expects(:execute_command).with do |env, *command, **options|
env["SKIP_POST_DEPLOYMENT_MIGRATIONS"] == "0" &&
env["SKIP_OPTIMIZE_ICONS"] == "1" &&
env["DISABLE_TRANSLATION_OVERRIDES"] == "1" &&
command == ["rake", "db:migrate"] &&
options[:chdir] == Rails.root
end.once
end
def expect_db_reconnect
RailsMultisite::ConnectionManagement.expects(:establish_connection).once
end
def execute_stubbed_restore(
stub_readonly_functions: true,
stub_psql: true,
stub_migrate: true,
dump_file_path: "foo.sql"
)
expect_table_move
expect_create_readonly_functions if stub_readonly_functions
expect_psql if stub_psql
expect_db_migrate if stub_migrate
subject.restore(dump_file_path)
end
def expect_decompress_and_clean_up_to_work(
backup_filename:,
expected_dump_filename: "dump.sql",
require_metadata_file:,
require_uploads:,
expected_upload_paths: nil,
location: nil
)
freeze_time(DateTime.parse("2019-12-24 14:31:48"))
source_file = File.join(Rails.root, "spec/fixtures/backups", backup_filename)
target_directory = BackupRestore::LocalBackupStore.base_directory
target_file = File.join(target_directory, backup_filename)
FileUtils.copy_file(source_file, target_file)
Dir.mktmpdir do |root_directory|
current_db = RailsMultisite::ConnectionManagement.current_db
file_handler = BackupRestore::BackupFileHandler.new(
logger, backup_filename, current_db,
root_tmp_directory: root_directory,
location: location
)
tmp_directory, db_dump_path = file_handler.decompress
expected_tmp_path = File.join(root_directory, "tmp/restores", current_db, "2019-12-24-143148")
expect(tmp_directory).to eq(expected_tmp_path)
expect(db_dump_path).to eq(File.join(expected_tmp_path, expected_dump_filename))
expect(Dir.exist?(tmp_directory)).to eq(true)
expect(File.exist?(db_dump_path)).to eq(true)
expect(File.exist?(File.join(tmp_directory, "meta.json"))).to eq(require_metadata_file)
if require_uploads
expected_upload_paths ||= ["uploads/default/original/3X/b/d/bd269860bb508aebcb6f08fe7289d5f117830383.png"]
expected_upload_paths.each do |upload_path|
absolute_upload_path = File.join(tmp_directory, upload_path)
expect(File.exist?(absolute_upload_path)).to eq(true), "expected file #{upload_path} does not exist"
yield(absolute_upload_path) if block_given?
end
else
expect(Dir.exist?(File.join(tmp_directory, "uploads"))).to eq(false)
end
file_handler.clean_up
expect(Dir.exist?(tmp_directory)).to eq(false)
end
ensure
FileUtils.rm(target_file)
# We don't want to delete the directory unless it is empty, otherwise this could be annoying
# when tests run for the "default" database in a development environment.
FileUtils.rmdir(target_directory) rescue nil
end
end

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true
# rubocop:disable Discourse/OnlyTopLevelMultisiteSpecs
shared_context "backups" do
before { create_backups }

View File

@ -0,0 +1,40 @@
# frozen_string_literal: true
require "rails_helper"
require_relative "shared_context_for_backup_restore"
describe BackupRestore::SystemInterface, type: :multisite do
include_context "shared stuff"
subject { BackupRestore::SystemInterface.new(logger) }
describe "#flush_redis" do
it "removes only keys from the current site in a multisite" do
test_multisite_connection("default") do
Discourse.redis.set("foo", "default-foo")
Discourse.redis.set("bar", "default-bar")
expect(Discourse.redis.get("foo")).to eq("default-foo")
expect(Discourse.redis.get("bar")).to eq("default-bar")
end
test_multisite_connection("second") do
Discourse.redis.set("foo", "second-foo")
Discourse.redis.set("bar", "second-bar")
expect(Discourse.redis.get("foo")).to eq("second-foo")
expect(Discourse.redis.get("bar")).to eq("second-bar")
subject.flush_redis
expect(Discourse.redis.get("foo")).to be_nil
expect(Discourse.redis.get("bar")).to be_nil
end
test_multisite_connection("default") do
expect(Discourse.redis.get("foo")).to eq("default-foo")
expect(Discourse.redis.get("bar")).to eq("default-bar")
end
end
end
end

View File

@ -168,45 +168,17 @@ describe BackupRestore::SystemInterface do
subject.wait_for_sidekiq
end
end
end
describe "flush_redis" do
context "Sidekiq" do
after { Sidekiq.unpause! }
describe "#flush_redis" do
context "Sidekiq" do
after { Sidekiq.unpause! }
it "doesn't unpause Sidekiq" do
Sidekiq.pause!
subject.flush_redis
it "doesn't unpause Sidekiq" do
Sidekiq.pause!
subject.flush_redis
expect(Sidekiq.paused?).to eq(true)
end
end
it "removes only keys from the current site in a multisite", type: :multisite do
test_multisite_connection("default") do
Discourse.redis.set("foo", "default-foo")
Discourse.redis.set("bar", "default-bar")
expect(Discourse.redis.get("foo")).to eq("default-foo")
expect(Discourse.redis.get("bar")).to eq("default-bar")
end
test_multisite_connection("second") do
Discourse.redis.set("foo", "second-foo")
Discourse.redis.set("bar", "second-bar")
expect(Discourse.redis.get("foo")).to eq("second-foo")
expect(Discourse.redis.get("bar")).to eq("second-bar")
subject.flush_redis
expect(Discourse.redis.get("foo")).to be_nil
expect(Discourse.redis.get("bar")).to be_nil
end
test_multisite_connection("default") do
expect(Discourse.redis.get("foo")).to eq("default-foo")
expect(Discourse.redis.get("bar")).to eq("default-bar")
end
expect(Sidekiq.paused?).to eq(true)
end
end
end

View File

@ -1,4 +1,5 @@
# frozen_string_literal: true
# rubocop:disable Discourse/OnlyTopLevelMultisiteSpecs
require 'rails_helper'
require_relative 'shared_context_for_backup_restore'

View File

@ -0,0 +1,77 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe "Pausing/Unpausing Sidekiq", type: :multisite do
describe "#pause!, #unpause! and #paused?" do
it "can pause and unpause" do
Sidekiq.pause!
expect(Sidekiq.paused?).to eq(true)
test_multisite_connection("second") do
expect(Sidekiq.paused?).to eq(false)
end
Sidekiq.unpause!
expect(Sidekiq.paused?).to eq(false)
test_multisite_connection("second") do
Sidekiq.pause!("test")
expect(Sidekiq.paused?).to eq(true)
end
expect(Sidekiq.paused_dbs).to eq(["second"])
Sidekiq.unpause_all!
RailsMultisite::ConnectionManagement.each_connection do
expect(Sidekiq.paused?).to eq(false)
end
end
end
end
RSpec.describe Sidekiq::Pausable, type: :multisite do
after do
Sidekiq.unpause_all!
end
describe "when sidekiq is paused" do
let(:middleware) { Sidekiq::Pausable.new }
def call_middleware(db = RailsMultisite::ConnectionManagement::DEFAULT)
middleware.call(Jobs::PostAlert.new, {
"args" => [{ "current_site_id" => db }]
}, "critical") do
yield
end
end
it "should delay the job" do
Sidekiq.pause!
called = false
called2 = false
call_middleware { called = true }
expect(called).to eq(false)
test_multisite_connection("second") do
call_middleware("second") { called2 = true }
expect(called2).to eq(true)
end
Sidekiq.unpause!
call_middleware { called = true }
expect(called).to eq(true)
test_multisite_connection("second") do
Sidekiq.pause!
call_middleware("second") { called2 = false }
expect(called2).to eq(true)
end
end
end
end

View File

@ -1,37 +1,6 @@
# frozen_string_literal: true
require 'rails_helper'
RSpec.describe "Pausing/Unpausing Sidekiq", type: :multisite do
describe '#pause!, #unpause! and #paused?' do
it "can pause and unpause" do
Sidekiq.pause!
expect(Sidekiq.paused?).to eq(true)
test_multisite_connection('second') do
expect(Sidekiq.paused?).to eq(false)
end
Sidekiq.unpause!
expect(Sidekiq.paused?).to eq(false)
test_multisite_connection('second') do
Sidekiq.pause!("test")
expect(Sidekiq.paused?).to eq(true)
end
expect(Sidekiq.paused_dbs).to eq(["second"])
Sidekiq.unpause_all!
RailsMultisite::ConnectionManagement.each_connection do
expect(Sidekiq.paused?).to eq(false)
end
end
end
end
require "rails_helper"
RSpec.describe Sidekiq::Pausable do
after do
@ -54,42 +23,4 @@ RSpec.describe Sidekiq::Pausable do
jobs = Sidekiq::ScheduledSet.new
expect(jobs.size).to eq(0)
end
describe 'when sidekiq is paused', type: :multisite do
let(:middleware) { Sidekiq::Pausable.new }
def call_middleware(db = RailsMultisite::ConnectionManagement::DEFAULT)
middleware.call(Jobs::PostAlert.new, {
"args" => [{ "current_site_id" => db }]
}, "critical") do
yield
end
end
it 'should delay the job' do
Sidekiq.pause!
called = false
called2 = false
call_middleware { called = true }
expect(called).to eq(false)
test_multisite_connection('second') do
call_middleware('second') { called2 = true }
expect(called2).to eq(true)
end
Sidekiq.unpause!
call_middleware { called = true }
expect(called).to eq(true)
test_multisite_connection('second') do
Sidekiq.pause!
call_middleware('second') { called2 = false }
expect(called2).to eq(true)
end
end
end
end

View File

@ -1,33 +1,22 @@
# frozen_string_literal: true
require 'rails_helper'
require "rails_helper"
describe UploadsController do
describe UploadsController, type: [:multisite, :request] do
let!(:user) { Fabricate(:user) }
let(:upload) { Fabricate(:upload_s3) }
describe "#show_short" do
describe "s3 store" do
let(:upload) { Fabricate(:upload_s3) }
before do
setup_s3
SiteSetting.secure_media = true
upload.update(secure: true)
end
before do
setup_s3
end
it "redirects to the signed_url_for_path with the multisite DB name in the url" do
sign_in(user)
freeze_time
get upload.short_path
context "when upload is secure and secure media enabled" do
before do
SiteSetting.secure_media = true
upload.update(secure: true)
end
context "when running on a multisite connection", type: :multisite do
it "redirects to the signed_url_for_path with the multisite DB name in the url" do
sign_in(user)
freeze_time
get upload.short_path
expect(response.body).to include(RailsMultisite::ConnectionManagement.current_db)
end
end
end
end
expect(response.body).to include(RailsMultisite::ConnectionManagement.current_db)
end
end

View File

@ -0,0 +1,43 @@
# frozen_string_literal: true
require "rails_helper"
RSpec.describe InlineUploads, type: :multisite do
before do
set_cdn_url "https://awesome.com"
end
describe ".process" do
describe "s3 uploads" do
let(:upload) { Fabricate(:upload_s3) }
let(:upload2) { Fabricate(:upload_s3) }
let(:upload3) { Fabricate(:upload) }
before do
upload3
setup_s3
SiteSetting.s3_cdn_url = "https://s3.cdn.com"
end
it "should correct image URLs in multisite" do
md = <<~MD
https:#{upload2.url} https:#{upload2.url}
#{URI.join(SiteSetting.s3_cdn_url, URI.parse(upload2.url).path).to_s}
<img src="#{upload.url}" alt="some image">
<img src="#{URI.join(SiteSetting.s3_cdn_url, URI.parse(upload2.url).path).to_s}" alt="some image">
<img src="#{upload3.url}">
MD
expect(InlineUploads.process(md)).to eq(<<~MD)
#{Discourse.base_url}#{upload2.short_path} #{Discourse.base_url}#{upload2.short_path}
#{Discourse.base_url}#{upload2.short_path}
![some image](#{upload.short_url})
![some image](#{upload2.short_url})
![](#{upload3.short_url})
MD
end
end
end
end

View File

@ -692,26 +692,6 @@ RSpec.describe InlineUploads do
[some reference]: #{Discourse.base_url}#{upload.short_path}
MD
end
it "should correct image URLs in multisite", type: :multisite do
md = <<~MD
https:#{upload2.url} https:#{upload2.url}
#{URI.join(SiteSetting.s3_cdn_url, URI.parse(upload2.url).path).to_s}
<img src="#{upload.url}" alt="some image">
<img src="#{URI.join(SiteSetting.s3_cdn_url, URI.parse(upload2.url).path).to_s}" alt="some image">
<img src="#{upload3.url}">
MD
expect(InlineUploads.process(md)).to eq(<<~MD)
#{Discourse.base_url}#{upload2.short_path} #{Discourse.base_url}#{upload2.short_path}
#{Discourse.base_url}#{upload2.short_path}
![some image](#{upload.short_url})
![some image](#{upload2.short_url})
![](#{upload3.short_url})
MD
end
end
end