debian-mirror-gitlab/spec/models/project_statistics_spec.rb

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

536 lines
17 KiB
Ruby
Raw Normal View History

2019-07-07 11:18:12 +05:30
# frozen_string_literal: true
2019-12-04 20:38:33 +05:30
require 'spec_helper'
2017-08-17 22:00:37 +05:30
2020-07-28 23:09:34 +05:30
RSpec.describe ProjectStatistics do
2017-09-10 17:25:29 +05:30
let(:project) { create :project }
2017-08-17 22:00:37 +05:30
let(:statistics) { project.statistics }
describe 'associations' do
it { is_expected.to belong_to(:project) }
it { is_expected.to belong_to(:namespace) }
end
2019-09-04 21:01:54 +05:30
describe 'scopes' do
describe '.for_project_ids' do
it 'returns only requested projects' do
stats = create_list(:project_statistics, 3)
project_ids = stats[0..1].map { |s| s.project_id }
expected_ids = stats[0..1].map { |s| s.id }
requested_stats = described_class.for_project_ids(project_ids).pluck(:id)
expect(requested_stats).to eq(expected_ids)
end
end
end
2017-08-17 22:00:37 +05:30
describe 'statistics columns' do
2022-08-27 11:52:29 +05:30
it "supports bigint values" do
expect do
statistics.update!(
commit_count: 3.gigabytes,
repository_size: 3.gigabytes,
wiki_size: 3.gigabytes,
lfs_objects_size: 3.gigabytes,
build_artifacts_size: 3.gigabytes,
snippets_size: 3.gigabytes,
pipeline_artifacts_size: 3.gigabytes,
uploads_size: 3.gigabytes,
container_registry_size: 3.gigabytes
)
end.not_to raise_error
2017-08-17 22:00:37 +05:30
end
end
2022-07-23 23:45:48 +05:30
describe 'namespace relatable columns' do
it 'treats the correct columns as namespace relatable' do
expect(described_class::NAMESPACE_RELATABLE_COLUMNS).to match_array %i[
repository_size
wiki_size
lfs_objects_size
uploads_size
container_registry_size
]
end
end
2017-08-17 22:00:37 +05:30
describe '#total_repository_size' do
it "sums repository and LFS object size" do
statistics.repository_size = 2
2019-09-04 21:01:54 +05:30
statistics.wiki_size = 6
2017-08-17 22:00:37 +05:30
statistics.lfs_objects_size = 3
statistics.build_artifacts_size = 4
2020-07-28 23:09:34 +05:30
statistics.snippets_size = 5
2021-01-29 00:20:46 +05:30
statistics.uploads_size = 3
2022-07-16 23:28:13 +05:30
statistics.container_registry_size = 8
2017-08-17 22:00:37 +05:30
expect(statistics.total_repository_size).to eq 5
end
end
2019-09-04 21:01:54 +05:30
describe '#wiki_size' do
2020-07-28 23:09:34 +05:30
it 'is initialized with not null value' do
expect(statistics.attributes['wiki_size']).to be_zero
expect(statistics.wiki_size).to be_zero
end
it 'coerces any nil value to 0' do
statistics.update!(wiki_size: nil)
expect(statistics.attributes['wiki_size']).to be_nil
2019-09-04 21:01:54 +05:30
expect(statistics.wiki_size).to eq 0
end
end
2020-07-28 23:09:34 +05:30
describe '#snippets_size' do
it 'is initialized with not null value' do
expect(statistics.attributes['snippets_size']).to be_zero
expect(statistics.snippets_size).to be_zero
end
it 'coerces any nil value to 0' do
statistics.update!(snippets_size: nil)
expect(statistics.attributes['snippets_size']).to be_nil
expect(statistics.snippets_size).to eq 0
end
end
2017-08-17 22:00:37 +05:30
describe '#refresh!' do
2022-11-25 23:54:43 +05:30
subject(:refresh_statistics) { statistics.refresh! }
2017-08-17 22:00:37 +05:30
before do
allow(statistics).to receive(:update_commit_count)
allow(statistics).to receive(:update_repository_size)
2019-09-04 21:01:54 +05:30
allow(statistics).to receive(:update_wiki_size)
2017-08-17 22:00:37 +05:30
allow(statistics).to receive(:update_lfs_objects_size)
2020-07-28 23:09:34 +05:30
allow(statistics).to receive(:update_snippets_size)
2017-08-17 22:00:37 +05:30
allow(statistics).to receive(:update_storage_size)
2021-01-29 00:20:46 +05:30
allow(statistics).to receive(:update_uploads_size)
2022-07-16 23:28:13 +05:30
allow(statistics).to receive(:update_container_registry_size)
2017-08-17 22:00:37 +05:30
end
context "without arguments" do
before do
2022-11-25 23:54:43 +05:30
refresh_statistics
2017-08-17 22:00:37 +05:30
end
it "sums all counters" do
expect(statistics).to have_received(:update_commit_count)
expect(statistics).to have_received(:update_repository_size)
2019-09-04 21:01:54 +05:30
expect(statistics).to have_received(:update_wiki_size)
2017-08-17 22:00:37 +05:30
expect(statistics).to have_received(:update_lfs_objects_size)
2020-07-28 23:09:34 +05:30
expect(statistics).to have_received(:update_snippets_size)
2021-01-29 00:20:46 +05:30
expect(statistics).to have_received(:update_uploads_size)
2022-07-16 23:28:13 +05:30
expect(statistics).to have_received(:update_container_registry_size)
2017-08-17 22:00:37 +05:30
end
end
context "when passing an only: argument" do
before do
statistics.refresh! only: [:lfs_objects_size]
end
it "only updates the given columns" do
expect(statistics).to have_received(:update_lfs_objects_size)
expect(statistics).not_to have_received(:update_commit_count)
expect(statistics).not_to have_received(:update_repository_size)
2019-09-04 21:01:54 +05:30
expect(statistics).not_to have_received(:update_wiki_size)
2020-07-28 23:09:34 +05:30
expect(statistics).not_to have_received(:update_snippets_size)
2021-01-29 00:20:46 +05:30
expect(statistics).not_to have_received(:update_uploads_size)
2022-07-16 23:28:13 +05:30
expect(statistics).not_to have_received(:update_container_registry_size)
2019-09-04 21:01:54 +05:30
end
end
context 'without repositories' do
it 'does not crash' do
expect(project.repository.exists?).to be_falsey
expect(project.wiki.repository.exists?).to be_falsey
2022-11-25 23:54:43 +05:30
refresh_statistics
2019-09-04 21:01:54 +05:30
expect(statistics).to have_received(:update_commit_count)
expect(statistics).to have_received(:update_repository_size)
expect(statistics).to have_received(:update_wiki_size)
2020-07-28 23:09:34 +05:30
expect(statistics).to have_received(:update_snippets_size)
2021-01-29 00:20:46 +05:30
expect(statistics).to have_received(:update_uploads_size)
2022-07-16 23:28:13 +05:30
expect(statistics).to have_received(:update_container_registry_size)
2019-09-04 21:01:54 +05:30
expect(statistics.repository_size).to eq(0)
expect(statistics.commit_count).to eq(0)
expect(statistics.wiki_size).to eq(0)
2020-07-28 23:09:34 +05:30
expect(statistics.snippets_size).to eq(0)
2021-01-29 00:20:46 +05:30
expect(statistics.uploads_size).to eq(0)
2022-07-16 23:28:13 +05:30
expect(statistics.container_registry_size).to eq(0)
2019-09-04 21:01:54 +05:30
end
end
context 'with deleted repositories' do
let(:project) { create(:project, :repository, :wiki_repo) }
before do
2022-11-25 23:54:43 +05:30
project.repository.remove
project.wiki.repository.remove
2019-09-04 21:01:54 +05:30
end
it 'does not crash' do
2022-11-25 23:54:43 +05:30
refresh_statistics
2019-09-04 21:01:54 +05:30
expect(statistics).to have_received(:update_commit_count)
expect(statistics).to have_received(:update_repository_size)
expect(statistics).to have_received(:update_wiki_size)
2020-07-28 23:09:34 +05:30
expect(statistics).to have_received(:update_snippets_size)
2021-01-29 00:20:46 +05:30
expect(statistics).to have_received(:update_uploads_size)
2022-07-16 23:28:13 +05:30
expect(statistics).to have_received(:update_container_registry_size)
2019-09-04 21:01:54 +05:30
expect(statistics.repository_size).to eq(0)
expect(statistics.commit_count).to eq(0)
expect(statistics.wiki_size).to eq(0)
2020-07-28 23:09:34 +05:30
expect(statistics.snippets_size).to eq(0)
2021-01-29 00:20:46 +05:30
expect(statistics.uploads_size).to eq(0)
2022-07-16 23:28:13 +05:30
expect(statistics.container_registry_size).to eq(0)
2017-08-17 22:00:37 +05:30
end
end
2019-09-30 21:07:59 +05:30
context 'when the column is namespace relatable' do
let(:namespace) { create(:group) }
let(:project) { create(:project, namespace: namespace) }
2019-10-12 21:52:04 +05:30
context 'when arguments are passed' do
2019-09-30 21:07:59 +05:30
it 'schedules the aggregation worker' do
expect(Namespaces::ScheduleAggregationWorker)
.to receive(:perform_async)
statistics.refresh!(only: [:lfs_objects_size])
end
end
context 'when no argument is passed' do
it 'schedules the aggregation worker' do
expect(Namespaces::ScheduleAggregationWorker)
.to receive(:perform_async)
2022-11-25 23:54:43 +05:30
refresh_statistics
2019-09-30 21:07:59 +05:30
end
end
end
context 'when the column is not namespace relatable' do
it 'does not schedules an aggregation worker' do
expect(Namespaces::ScheduleAggregationWorker)
.not_to receive(:perform_async)
statistics.refresh!(only: [:commit_count])
end
end
2021-01-03 14:25:43 +05:30
context 'when the database is read-only' do
it 'does nothing' do
allow(Gitlab::Database).to receive(:read_only?) { true }
expect(statistics).not_to receive(:update_commit_count)
expect(statistics).not_to receive(:update_repository_size)
expect(statistics).not_to receive(:update_wiki_size)
expect(statistics).not_to receive(:update_lfs_objects_size)
expect(statistics).not_to receive(:update_snippets_size)
2021-01-29 00:20:46 +05:30
expect(statistics).not_to receive(:update_uploads_size)
2022-07-16 23:28:13 +05:30
expect(statistics).not_to receive(:update_container_registry_size)
2021-01-03 14:25:43 +05:30
expect(statistics).not_to receive(:save!)
expect(Namespaces::ScheduleAggregationWorker)
.not_to receive(:perform_async)
2022-11-25 23:54:43 +05:30
refresh_statistics
2021-01-03 14:25:43 +05:30
end
end
2022-11-25 23:54:43 +05:30
it_behaves_like 'obtaining lease to update database' do
let(:model) { statistics }
end
2017-08-17 22:00:37 +05:30
end
describe '#update_commit_count' do
before do
allow(project.repository).to receive(:commit_count).and_return(23)
statistics.update_commit_count
end
it "stores the number of commits in the repository" do
expect(statistics.commit_count).to eq 23
end
end
describe '#update_repository_size' do
before do
allow(project.repository).to receive(:size).and_return(12)
statistics.update_repository_size
end
it "stores the size of the repository" do
expect(statistics.repository_size).to eq 12.megabytes
end
end
2019-09-04 21:01:54 +05:30
describe '#update_wiki_size' do
before do
allow(project.wiki.repository).to receive(:size).and_return(34)
statistics.update_wiki_size
end
it "stores the size of the wiki" do
expect(statistics.wiki_size).to eq 34.megabytes
end
end
2020-07-28 23:09:34 +05:30
describe '#update_snippets_size' do
before do
create_list(:project_snippet, 2, project: project)
SnippetStatistics.update_all(repository_size: 10)
end
it 'stores the size of snippets' do
# Snippet not associated with the project
snippet = create(:project_snippet)
snippet.statistics.update!(repository_size: 40)
statistics.update_snippets_size
expect(statistics.update_snippets_size).to eq 20
end
context 'when not all snippets has statistics' do
it 'stores the size of snippets with statistics' do
SnippetStatistics.last.delete
statistics.update_snippets_size
expect(statistics.update_snippets_size).to eq 10
end
end
end
2017-08-17 22:00:37 +05:30
describe '#update_lfs_objects_size' do
let!(:lfs_object1) { create(:lfs_object, size: 23.megabytes) }
let!(:lfs_object2) { create(:lfs_object, size: 34.megabytes) }
2021-11-18 22:05:49 +05:30
let!(:lfs_object3) { create(:lfs_object, size: 34.megabytes) }
2017-08-17 22:00:37 +05:30
let!(:lfs_objects_project1) { create(:lfs_objects_project, project: project, lfs_object: lfs_object1) }
let!(:lfs_objects_project2) { create(:lfs_objects_project, project: project, lfs_object: lfs_object2) }
2021-11-18 22:05:49 +05:30
let!(:lfs_objects_project3) { create(:lfs_objects_project, project: project, lfs_object: lfs_object3) }
2017-08-17 22:00:37 +05:30
before do
statistics.update_lfs_objects_size
end
it "stores the size of related LFS objects" do
2021-11-18 22:05:49 +05:30
expect(statistics.lfs_objects_size).to eq 91.megabytes
2017-08-17 22:00:37 +05:30
end
end
2021-01-29 00:20:46 +05:30
describe '#update_uploads_size' do
let!(:upload1) { create(:upload, model: project, size: 1.megabyte) }
let!(:upload2) { create(:upload, model: project, size: 2.megabytes) }
it 'stores the size of related uploaded files' do
expect(statistics.update_uploads_size).to eq(3.megabytes)
end
end
2022-07-16 23:28:13 +05:30
describe '#update_container_registry_size' do
subject(:update_container_registry_size) { statistics.update_container_registry_size }
it 'stores the project container registry repositories size' do
allow(project).to receive(:container_repositories_size).and_return(10)
update_container_registry_size
expect(statistics.container_registry_size).to eq(10)
end
it 'handles nil values for the repositories size' do
allow(project).to receive(:container_repositories_size).and_return(nil)
update_container_registry_size
expect(statistics.container_registry_size).to eq(0)
end
end
2017-08-17 22:00:37 +05:30
describe '#update_storage_size' do
2022-07-16 23:28:13 +05:30
it "sums the relevant storage counters" do
2017-08-17 22:00:37 +05:30
statistics.update!(
repository_size: 2,
2019-09-04 21:01:54 +05:30
wiki_size: 4,
2020-07-28 23:09:34 +05:30
lfs_objects_size: 3,
2020-11-24 15:15:51 +05:30
snippets_size: 2,
2021-01-29 00:20:46 +05:30
pipeline_artifacts_size: 3,
2021-12-11 22:18:48 +05:30
build_artifacts_size: 3,
packages_size: 6,
2021-01-29 00:20:46 +05:30
uploads_size: 5
2019-09-04 21:01:54 +05:30
)
statistics.reload
2021-12-11 22:18:48 +05:30
expect(statistics.storage_size).to eq 28
2019-09-04 21:01:54 +05:30
end
2022-07-16 23:28:13 +05:30
it 'excludes the container_registry_size' do
statistics.update!(
repository_size: 2,
uploads_size: 5,
container_registry_size: 10
)
statistics.reload
expect(statistics.storage_size).to eq 7
end
2019-09-04 21:01:54 +05:30
it 'works during wiki_size backfill' do
statistics.update!(
repository_size: 2,
wiki_size: nil,
2017-09-10 17:25:29 +05:30
lfs_objects_size: 3
2017-08-17 22:00:37 +05:30
)
statistics.reload
expect(statistics.storage_size).to eq 5
end
2020-07-28 23:09:34 +05:30
context 'when nullable columns are nil' do
it 'does not raise any error' do
expect do
statistics.update!(
repository_size: 2,
wiki_size: nil,
lfs_objects_size: 3,
snippets_size: nil
)
end.not_to raise_error
expect(statistics.storage_size).to eq 5
end
end
2017-08-17 22:00:37 +05:30
end
2018-10-15 14:42:47 +05:30
2022-10-11 01:57:18 +05:30
describe '#refresh_storage_size!' do
2022-11-25 23:54:43 +05:30
subject(:refresh_storage_size) { statistics.refresh_storage_size! }
2022-10-11 01:57:18 +05:30
it 'recalculates storage size from its components and save it' do
statistics.update_columns(
repository_size: 2,
wiki_size: 4,
lfs_objects_size: 3,
snippets_size: 2,
pipeline_artifacts_size: 3,
build_artifacts_size: 3,
packages_size: 6,
uploads_size: 5,
storage_size: 0
)
2022-11-25 23:54:43 +05:30
expect { refresh_storage_size }.to change { statistics.reload.storage_size }.from(0).to(28)
end
context 'when nullable columns are nil' do
before do
statistics.update_columns(
repository_size: 2,
wiki_size: nil,
storage_size: 0
)
end
it 'does not raise any error' do
expect { refresh_storage_size }.not_to raise_error
end
it 'recalculates storage size from its components' do
expect { refresh_storage_size }.to change { statistics.reload.storage_size }.from(0).to(2)
end
end
it_behaves_like 'obtaining lease to update database' do
let(:model) { statistics }
2022-10-11 01:57:18 +05:30
end
end
2018-10-15 14:42:47 +05:30
describe '.increment_statistic' do
2019-07-31 22:56:46 +05:30
shared_examples 'a statistic that increases storage_size' do
it 'increases the statistic by that amount' do
2021-01-03 14:25:43 +05:30
expect { described_class.increment_statistic(project, stat, 13) }
2019-07-31 22:56:46 +05:30
.to change { statistics.reload.send(stat) || 0 }
.by(13)
end
it 'increases also storage size by that amount' do
2021-01-03 14:25:43 +05:30
expect { described_class.increment_statistic(project, stat, 20) }
2020-10-24 23:57:45 +05:30
.to change { statistics.reload.storage_size }
.by(20)
2019-07-31 22:56:46 +05:30
end
2023-03-04 22:38:38 +05:30
it 'schedules a namespace aggregation worker' do
expect(Namespaces::ScheduleAggregationWorker).to receive(:perform_async)
.with(statistics.project.namespace.id)
described_class.increment_statistic(project, stat, 20)
end
2018-10-15 14:42:47 +05:30
end
2021-01-03 14:25:43 +05:30
shared_examples 'a statistic that increases storage_size asynchronously' do
it 'stores the increment temporarily in Redis', :clean_gitlab_redis_shared_state do
described_class.increment_statistic(project, stat, 13)
Gitlab::Redis::SharedState.with do |redis|
2023-03-04 22:38:38 +05:30
key = statistics.counter(stat).key
increment = redis.get(key)
2021-01-03 14:25:43 +05:30
expect(increment.to_i).to eq(13)
end
end
2022-10-11 01:57:18 +05:30
it 'schedules a worker to update the statistic and storage_size async', :sidekiq_inline do
2021-01-03 14:25:43 +05:30
expect(FlushCounterIncrementsWorker)
.to receive(:perform_in)
2023-03-04 22:38:38 +05:30
.with(Gitlab::Counters::BufferedCounter::WORKER_DELAY, described_class.name, statistics.id, stat)
2022-10-11 01:57:18 +05:30
.and_call_original
2021-01-03 14:25:43 +05:30
2022-10-11 01:57:18 +05:30
expect { described_class.increment_statistic(project, stat, 20) }
.to change { statistics.reload.send(stat) }.by(20)
.and change { statistics.reload.send(:storage_size) }.by(20)
2021-01-03 14:25:43 +05:30
end
end
2019-07-31 22:56:46 +05:30
context 'when adjusting :build_artifacts_size' do
let(:stat) { :build_artifacts_size }
2021-01-03 14:25:43 +05:30
it_behaves_like 'a statistic that increases storage_size asynchronously'
2019-07-31 22:56:46 +05:30
end
2020-11-24 15:15:51 +05:30
context 'when adjusting :pipeline_artifacts_size' do
let(:stat) { :pipeline_artifacts_size }
it_behaves_like 'a statistic that increases storage_size'
end
2019-07-31 22:56:46 +05:30
context 'when adjusting :packages_size' do
let(:stat) { :packages_size }
2023-03-04 22:38:38 +05:30
it_behaves_like 'a statistic that increases storage_size asynchronously'
2018-11-18 11:00:15 +05:30
end
2018-10-15 14:42:47 +05:30
context 'when the amount is 0' do
it 'does not execute a query' do
project
2023-03-04 22:38:38 +05:30
expect { described_class.increment_statistic(project, :build_artifacts_size, 0) }
2018-10-15 14:42:47 +05:30
.not_to exceed_query_limit(0)
end
end
context 'when using an invalid column' do
it 'raises an error' do
2023-03-04 22:38:38 +05:30
expect { described_class.increment_statistic(project, :id, 13) }
2018-10-15 14:42:47 +05:30
.to raise_error(ArgumentError, "Cannot increment attribute: id")
end
end
end
2017-08-17 22:00:37 +05:30
end