Merge remote-tracking branch 'parent/main' into kb_migration
This commit is contained in:
commit
32cfd20257
54 changed files with 1045 additions and 138 deletions
7
spec/fabricators/software_update_fabricator.rb
Normal file
7
spec/fabricators/software_update_fabricator.rb
Normal file
|
@ -0,0 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Fabricator(:software_update) do
|
||||
version '99.99.99'
|
||||
urgent false
|
||||
type 'patch'
|
||||
end
|
23
spec/features/admin/software_updates_spec.rb
Normal file
23
spec/features/admin/software_updates_spec.rb
Normal file
|
@ -0,0 +1,23 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe 'finding software updates through the admin interface' do
|
||||
before do
|
||||
Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: true, release_notes: 'https://github.com/mastodon/mastodon/releases/v99')
|
||||
|
||||
sign_in Fabricate(:user, role: UserRole.find_by(name: 'Owner')), scope: :user
|
||||
end
|
||||
|
||||
it 'shows a link to the software updates page, which links to release notes' do
|
||||
visit settings_profile_path
|
||||
click_on I18n.t('admin.critical_update_pending')
|
||||
|
||||
expect(page).to have_title(I18n.t('admin.software_updates.title'))
|
||||
|
||||
expect(page).to have_content('99.99.99')
|
||||
|
||||
click_on I18n.t('admin.software_updates.release_notes')
|
||||
expect(page).to have_current_path('https://github.com/mastodon/mastodon/releases/v99', url: true)
|
||||
end
|
||||
end
|
133
spec/lib/admin/system_check/software_version_check_spec.rb
Normal file
133
spec/lib/admin/system_check/software_version_check_spec.rb
Normal file
|
@ -0,0 +1,133 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Admin::SystemCheck::SoftwareVersionCheck do
|
||||
include RoutingHelper
|
||||
|
||||
subject(:check) { described_class.new(user) }
|
||||
|
||||
let(:user) { Fabricate(:user) }
|
||||
|
||||
describe 'skip?' do
|
||||
context 'when user cannot view devops' do
|
||||
before { allow(user).to receive(:can?).with(:view_devops).and_return(false) }
|
||||
|
||||
it 'returns true' do
|
||||
expect(check.skip?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when user can view devops' do
|
||||
before { allow(user).to receive(:can?).with(:view_devops).and_return(true) }
|
||||
|
||||
it 'returns false' do
|
||||
expect(check.skip?).to be false
|
||||
end
|
||||
|
||||
context 'when checks are disabled' do
|
||||
around do |example|
|
||||
ClimateControl.modify UPDATE_CHECK_URL: '' do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(check.skip?).to be true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'pass?' do
|
||||
context 'when there is no known update' do
|
||||
it 'returns true' do
|
||||
expect(check.pass?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a non-urgent major release' do
|
||||
before do
|
||||
Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: false)
|
||||
end
|
||||
|
||||
it 'returns true' do
|
||||
expect(check.pass?).to be true
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is an urgent major release' do
|
||||
before do
|
||||
Fabricate(:software_update, version: '99.99.99', type: 'major', urgent: true)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(check.pass?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is an urgent minor release' do
|
||||
before do
|
||||
Fabricate(:software_update, version: '99.99.99', type: 'minor', urgent: true)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(check.pass?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is an urgent patch release' do
|
||||
before do
|
||||
Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: true)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(check.pass?).to be false
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is a non-urgent patch release' do
|
||||
before do
|
||||
Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: false)
|
||||
end
|
||||
|
||||
it 'returns false' do
|
||||
expect(check.pass?).to be false
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe 'message' do
|
||||
context 'when there is a non-urgent patch release pending' do
|
||||
before do
|
||||
Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: false)
|
||||
end
|
||||
|
||||
it 'sends class name symbol to message instance' do
|
||||
allow(Admin::SystemCheck::Message).to receive(:new)
|
||||
.with(:software_version_patch_check, anything, anything)
|
||||
|
||||
check.message
|
||||
|
||||
expect(Admin::SystemCheck::Message).to have_received(:new)
|
||||
.with(:software_version_patch_check, nil, admin_software_updates_path)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when there is an urgent patch release pending' do
|
||||
before do
|
||||
Fabricate(:software_update, version: '99.99.99', type: 'patch', urgent: true)
|
||||
end
|
||||
|
||||
it 'sends class name symbol to message instance' do
|
||||
allow(Admin::SystemCheck::Message).to receive(:new)
|
||||
.with(:software_version_critical_check, anything, anything, anything)
|
||||
|
||||
check.message
|
||||
|
||||
expect(Admin::SystemCheck::Message).to have_received(:new)
|
||||
.with(:software_version_critical_check, nil, admin_software_updates_path, true)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -85,4 +85,46 @@ RSpec.describe AdminMailer do
|
|||
expect(mail.body.encoded).to match 'The following items need a review before they can be displayed publicly'
|
||||
end
|
||||
end
|
||||
|
||||
describe '.new_software_updates' do
|
||||
let(:recipient) { Fabricate(:account, username: 'Bob') }
|
||||
let(:mail) { described_class.with(recipient: recipient).new_software_updates }
|
||||
|
||||
before do
|
||||
recipient.user.update(locale: :en)
|
||||
end
|
||||
|
||||
it 'renders the headers' do
|
||||
expect(mail.subject).to eq('New Mastodon versions are available for cb6e6126.ngrok.io!')
|
||||
expect(mail.to).to eq [recipient.user_email]
|
||||
expect(mail.from).to eq ['notifications@localhost']
|
||||
end
|
||||
|
||||
it 'renders the body' do
|
||||
expect(mail.body.encoded).to match 'New Mastodon versions have been released, you may want to update!'
|
||||
end
|
||||
end
|
||||
|
||||
describe '.new_critical_software_updates' do
|
||||
let(:recipient) { Fabricate(:account, username: 'Bob') }
|
||||
let(:mail) { described_class.with(recipient: recipient).new_critical_software_updates }
|
||||
|
||||
before do
|
||||
recipient.user.update(locale: :en)
|
||||
end
|
||||
|
||||
it 'renders the headers', :aggregate_failures do
|
||||
expect(mail.subject).to eq('Critical Mastodon updates are available for cb6e6126.ngrok.io!')
|
||||
expect(mail.to).to eq [recipient.user_email]
|
||||
expect(mail.from).to eq ['notifications@localhost']
|
||||
|
||||
expect(mail['Importance'].value).to eq 'high'
|
||||
expect(mail['Priority'].value).to eq 'urgent'
|
||||
expect(mail['X-Priority'].value).to eq '1'
|
||||
end
|
||||
|
||||
it 'renders the body' do
|
||||
expect(mail.body.encoded).to match 'New critical versions of Mastodon have been released, you may want to update as soon as possible!'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
87
spec/models/software_update_spec.rb
Normal file
87
spec/models/software_update_spec.rb
Normal file
|
@ -0,0 +1,87 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe SoftwareUpdate do
|
||||
describe '.pending_to_a' do
|
||||
before do
|
||||
allow(Mastodon::Version).to receive(:gem_version).and_return(Gem::Version.new(mastodon_version))
|
||||
|
||||
Fabricate(:software_update, version: '3.4.42', type: 'patch', urgent: true)
|
||||
Fabricate(:software_update, version: '3.5.0', type: 'minor', urgent: false)
|
||||
Fabricate(:software_update, version: '4.2.0', type: 'major', urgent: false)
|
||||
end
|
||||
|
||||
context 'when the Mastodon version is an outdated release' do
|
||||
let(:mastodon_version) { '3.4.0' }
|
||||
|
||||
it 'returns the expected versions' do
|
||||
expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('3.4.42', '3.5.0', '4.2.0')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the Mastodon version is more recent than anything last returned by the server' do
|
||||
let(:mastodon_version) { '5.0.0' }
|
||||
|
||||
it 'returns the expected versions' do
|
||||
expect(described_class.pending_to_a.pluck(:version)).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the Mastodon version is an outdated nightly' do
|
||||
let(:mastodon_version) { '4.3.0-nightly.2023-09-10' }
|
||||
|
||||
before do
|
||||
Fabricate(:software_update, version: '4.3.0-nightly.2023-09-12', type: 'major', urgent: true)
|
||||
end
|
||||
|
||||
it 'returns the expected versions' do
|
||||
expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-nightly.2023-09-12')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the Mastodon version is a very outdated nightly' do
|
||||
let(:mastodon_version) { '4.2.0-nightly.2023-07-10' }
|
||||
|
||||
it 'returns the expected versions' do
|
||||
expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.2.0')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the Mastodon version is an outdated dev version' do
|
||||
let(:mastodon_version) { '4.3.0-0.dev.0' }
|
||||
|
||||
before do
|
||||
Fabricate(:software_update, version: '4.3.0-0.dev.2', type: 'major', urgent: true)
|
||||
end
|
||||
|
||||
it 'returns the expected versions' do
|
||||
expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-0.dev.2')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the Mastodon version is an outdated beta version' do
|
||||
let(:mastodon_version) { '4.3.0-beta1' }
|
||||
|
||||
before do
|
||||
Fabricate(:software_update, version: '4.3.0-beta2', type: 'major', urgent: true)
|
||||
end
|
||||
|
||||
it 'returns the expected versions' do
|
||||
expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-beta2')
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the Mastodon version is an outdated beta version and there is a rc' do
|
||||
let(:mastodon_version) { '4.3.0-beta1' }
|
||||
|
||||
before do
|
||||
Fabricate(:software_update, version: '4.3.0-rc1', type: 'major', urgent: true)
|
||||
end
|
||||
|
||||
it 'returns the expected versions' do
|
||||
expect(described_class.pending_to_a.pluck(:version)).to contain_exactly('4.3.0-rc1')
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
25
spec/policies/software_update_policy_spec.rb
Normal file
25
spec/policies/software_update_policy_spec.rb
Normal file
|
@ -0,0 +1,25 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
require 'pundit/rspec'
|
||||
|
||||
RSpec.describe SoftwareUpdatePolicy do
|
||||
subject { described_class }
|
||||
|
||||
let(:admin) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')).account }
|
||||
let(:john) { Fabricate(:account) }
|
||||
|
||||
permissions :index? do
|
||||
context 'when owner' do
|
||||
it 'permits' do
|
||||
expect(subject).to permit(admin, SoftwareUpdate)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when not owner' do
|
||||
it 'denies' do
|
||||
expect(subject).to_not permit(john, SoftwareUpdate)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
158
spec/services/software_update_check_service_spec.rb
Normal file
158
spec/services/software_update_check_service_spec.rb
Normal file
|
@ -0,0 +1,158 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
RSpec.describe SoftwareUpdateCheckService, type: :service do
|
||||
subject { described_class.new }
|
||||
|
||||
shared_examples 'when the feature is enabled' do
|
||||
let(:full_update_check_url) { "#{update_check_url}?version=#{Mastodon::Version.to_s.split('+')[0]}" }
|
||||
|
||||
let(:devops_role) { Fabricate(:user_role, name: 'DevOps', permissions: UserRole::FLAGS[:view_devops]) }
|
||||
let(:owner_user) { Fabricate(:user, role: UserRole.find_by(name: 'Owner')) }
|
||||
let(:old_devops_user) { Fabricate(:user) }
|
||||
let(:none_user) { Fabricate(:user, role: devops_role) }
|
||||
let(:patch_user) { Fabricate(:user, role: devops_role) }
|
||||
let(:critical_user) { Fabricate(:user, role: devops_role) }
|
||||
|
||||
around do |example|
|
||||
queue_adapter = ActiveJob::Base.queue_adapter
|
||||
ActiveJob::Base.queue_adapter = :test
|
||||
|
||||
example.run
|
||||
|
||||
ActiveJob::Base.queue_adapter = queue_adapter
|
||||
end
|
||||
|
||||
before do
|
||||
Fabricate(:software_update, version: '3.5.0', type: 'major', urgent: false)
|
||||
Fabricate(:software_update, version: '42.13.12', type: 'major', urgent: false)
|
||||
|
||||
owner_user.settings.update('notification_emails.software_updates': 'all')
|
||||
owner_user.save!
|
||||
|
||||
old_devops_user.settings.update('notification_emails.software_updates': 'all')
|
||||
old_devops_user.save!
|
||||
|
||||
none_user.settings.update('notification_emails.software_updates': 'none')
|
||||
none_user.save!
|
||||
|
||||
patch_user.settings.update('notification_emails.software_updates': 'patch')
|
||||
patch_user.save!
|
||||
|
||||
critical_user.settings.update('notification_emails.software_updates': 'critical')
|
||||
critical_user.save!
|
||||
end
|
||||
|
||||
context 'when the update server errors out' do
|
||||
before do
|
||||
stub_request(:get, full_update_check_url).to_return(status: 404)
|
||||
end
|
||||
|
||||
it 'deletes outdated update records but keeps valid update records' do
|
||||
expect { subject.call }.to change { SoftwareUpdate.pluck(:version).sort }.from(['3.5.0', '42.13.12']).to(['42.13.12'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'when the server returns new versions' do
|
||||
let(:server_json) do
|
||||
{
|
||||
updatesAvailable: [
|
||||
{
|
||||
version: '4.2.1',
|
||||
urgent: false,
|
||||
type: 'patch',
|
||||
releaseNotes: 'https://github.com/mastodon/mastodon/releases/v4.2.1',
|
||||
},
|
||||
{
|
||||
version: '4.3.0',
|
||||
urgent: false,
|
||||
type: 'minor',
|
||||
releaseNotes: 'https://github.com/mastodon/mastodon/releases/v4.3.0',
|
||||
},
|
||||
{
|
||||
version: '5.0.0',
|
||||
urgent: false,
|
||||
type: 'minor',
|
||||
releaseNotes: 'https://github.com/mastodon/mastodon/releases/v5.0.0',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
before do
|
||||
stub_request(:get, full_update_check_url).to_return(body: Oj.dump(server_json))
|
||||
end
|
||||
|
||||
it 'updates the list of known updates' do
|
||||
expect { subject.call }.to change { SoftwareUpdate.pluck(:version).sort }.from(['3.5.0', '42.13.12']).to(['4.2.1', '4.3.0', '5.0.0'])
|
||||
end
|
||||
|
||||
context 'when no update is urgent' do
|
||||
it 'sends e-mail notifications according to settings', :aggregate_failures do
|
||||
expect { subject.call }.to have_enqueued_mail(AdminMailer, :new_software_updates)
|
||||
.with(hash_including(params: { recipient: owner_user.account })).once
|
||||
.and(have_enqueued_mail(AdminMailer, :new_software_updates).with(hash_including(params: { recipient: patch_user.account })).once)
|
||||
.and(have_enqueued_mail.at_most(2))
|
||||
end
|
||||
end
|
||||
|
||||
context 'when an update is urgent' do
|
||||
let(:server_json) do
|
||||
{
|
||||
updatesAvailable: [
|
||||
{
|
||||
version: '5.0.0',
|
||||
urgent: true,
|
||||
type: 'minor',
|
||||
releaseNotes: 'https://github.com/mastodon/mastodon/releases/v5.0.0',
|
||||
},
|
||||
],
|
||||
}
|
||||
end
|
||||
|
||||
it 'sends e-mail notifications according to settings', :aggregate_failures do
|
||||
expect { subject.call }.to have_enqueued_mail(AdminMailer, :new_critical_software_updates)
|
||||
.with(hash_including(params: { recipient: owner_user.account })).once
|
||||
.and(have_enqueued_mail(AdminMailer, :new_critical_software_updates).with(hash_including(params: { recipient: patch_user.account })).once)
|
||||
.and(have_enqueued_mail(AdminMailer, :new_critical_software_updates).with(hash_including(params: { recipient: critical_user.account })).once)
|
||||
.and(have_enqueued_mail.at_most(3))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context 'when update checking is disabled' do
|
||||
around do |example|
|
||||
ClimateControl.modify UPDATE_CHECK_URL: '' do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
before do
|
||||
Fabricate(:software_update, version: '3.5.0', type: 'major', urgent: false)
|
||||
end
|
||||
|
||||
it 'deletes outdated update records' do
|
||||
expect { subject.call }.to change(SoftwareUpdate, :count).from(1).to(0)
|
||||
end
|
||||
end
|
||||
|
||||
context 'when using the default update checking API' do
|
||||
let(:update_check_url) { 'https://api.joinmastodon.org/update-check' }
|
||||
|
||||
it_behaves_like 'when the feature is enabled'
|
||||
end
|
||||
|
||||
context 'when using a custom update check URL' do
|
||||
let(:update_check_url) { 'https://api.example.com/update_check' }
|
||||
|
||||
around do |example|
|
||||
ClimateControl.modify UPDATE_CHECK_URL: 'https://api.example.com/update_check' do
|
||||
example.run
|
||||
end
|
||||
end
|
||||
|
||||
it_behaves_like 'when the feature is enabled'
|
||||
end
|
||||
end
|
|
@ -0,0 +1,20 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'rails_helper'
|
||||
|
||||
describe Scheduler::SoftwareUpdateCheckScheduler do
|
||||
subject { described_class.new }
|
||||
|
||||
describe 'perform' do
|
||||
let(:service_double) { instance_double(SoftwareUpdateCheckService, call: nil) }
|
||||
|
||||
before do
|
||||
allow(SoftwareUpdateCheckService).to receive(:new).and_return(service_double)
|
||||
end
|
||||
|
||||
it 'calls SoftwareUpdateCheckService' do
|
||||
subject.perform
|
||||
expect(service_double).to have_received(:call)
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Add table
Add a link
Reference in a new issue