{{ note.body }}
-{{ note.body }}
some content
'; + factory({ + slots: { + default: dummyContent, + }, + }); + + const content = wrapper.find('.timeline-entry-inner :first-child'); + + expect(content.html()).toBe(dummyContent); + }); +}); diff --git a/spec/lib/gitlab/auth/user_auth_finders_spec.rb b/spec/lib/gitlab/auth/user_auth_finders_spec.rb index 5d3fbba264f..4e4c8b215c2 100644 --- a/spec/lib/gitlab/auth/user_auth_finders_spec.rb +++ b/spec/lib/gitlab/auth/user_auth_finders_spec.rb @@ -279,5 +279,20 @@ describe Gitlab::Auth::UserAuthFinders do expect { validate_access_token!(scopes: [:sudo]) }.to raise_error(Gitlab::Auth::InsufficientScopeError) end end + + context 'with impersonation token' do + let(:personal_access_token) { create(:personal_access_token, :impersonation, user: user) } + + context 'when impersonation is disabled' do + before do + stub_config_setting(impersonation_enabled: false) + allow_any_instance_of(described_class).to receive(:access_token).and_return(personal_access_token) + end + + it 'returns Gitlab::Auth::ImpersonationDisabled' do + expect { validate_access_token! }.to raise_error(Gitlab::Auth::ImpersonationDisabled) + end + end + end end end diff --git a/spec/lib/gitlab/ci/config/entry/attributable_spec.rb b/spec/lib/gitlab/config/entry/attributable_spec.rb similarity index 87% rename from spec/lib/gitlab/ci/config/entry/attributable_spec.rb rename to spec/lib/gitlab/config/entry/attributable_spec.rb index b028b771375..abb4fff3ad7 100644 --- a/spec/lib/gitlab/ci/config/entry/attributable_spec.rb +++ b/spec/lib/gitlab/config/entry/attributable_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Attributable do +describe Gitlab::Config::Entry::Attributable do let(:node) do Class.new do - include Gitlab::Ci::Config::Entry::Attributable + include Gitlab::Config::Entry::Attributable end end @@ -48,7 +48,7 @@ describe Gitlab::Ci::Config::Entry::Attributable do it 'raises an error' do expectation = expect do Class.new(String) do - include Gitlab::Ci::Config::Entry::Attributable + include Gitlab::Config::Entry::Attributable attributes :length end diff --git a/spec/lib/gitlab/ci/config/entry/boolean_spec.rb b/spec/lib/gitlab/config/entry/boolean_spec.rb similarity index 93% rename from spec/lib/gitlab/ci/config/entry/boolean_spec.rb rename to spec/lib/gitlab/config/entry/boolean_spec.rb index 5f067cad93c..1b7a3f850ec 100644 --- a/spec/lib/gitlab/ci/config/entry/boolean_spec.rb +++ b/spec/lib/gitlab/config/entry/boolean_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Boolean do +describe Gitlab::Config::Entry::Boolean do let(:entry) { described_class.new(config) } describe 'validations' do diff --git a/spec/lib/gitlab/ci/config/entry/configurable_spec.rb b/spec/lib/gitlab/config/entry/configurable_spec.rb similarity index 82% rename from spec/lib/gitlab/ci/config/entry/configurable_spec.rb rename to spec/lib/gitlab/config/entry/configurable_spec.rb index 088d4b472da..85a7cf1d241 100644 --- a/spec/lib/gitlab/ci/config/entry/configurable_spec.rb +++ b/spec/lib/gitlab/config/entry/configurable_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Configurable do +describe Gitlab::Config::Entry::Configurable do let(:entry) do - Class.new(Gitlab::Ci::Config::Entry::Node) do - include Gitlab::Ci::Config::Entry::Configurable + Class.new(Gitlab::Config::Entry::Node) do + include Gitlab::Config::Entry::Configurable end end @@ -39,7 +39,7 @@ describe Gitlab::Ci::Config::Entry::Configurable do it 'creates a node factory' do expect(entry.nodes[:object]) - .to be_an_instance_of Gitlab::Ci::Config::Entry::Factory + .to be_an_instance_of Gitlab::Config::Entry::Factory end it 'returns a duplicated factory object' do diff --git a/spec/lib/gitlab/ci/config/entry/factory_spec.rb b/spec/lib/gitlab/config/entry/factory_spec.rb similarity index 86% rename from spec/lib/gitlab/ci/config/entry/factory_spec.rb rename to spec/lib/gitlab/config/entry/factory_spec.rb index 8dd48e4efae..c29d17eaee3 100644 --- a/spec/lib/gitlab/ci/config/entry/factory_spec.rb +++ b/spec/lib/gitlab/config/entry/factory_spec.rb @@ -1,9 +1,17 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Factory do +describe Gitlab::Config::Entry::Factory do describe '#create!' do + class Script < Gitlab::Config::Entry::Node + include Gitlab::Config::Entry::Validatable + + validations do + validates :config, array_of_strings: true + end + end + + let(:entry) { Script } let(:factory) { described_class.new(entry) } - let(:entry) { Gitlab::Ci::Config::Entry::Script } context 'when setting a concrete value' do it 'creates entry with valid value' do @@ -54,7 +62,7 @@ describe Gitlab::Ci::Config::Entry::Factory do context 'when not setting a value' do it 'raises error' do expect { factory.create! }.to raise_error( - Gitlab::Ci::Config::Entry::Factory::InvalidFactory + Gitlab::Config::Entry::Factory::InvalidFactory ) end end diff --git a/spec/lib/gitlab/ci/config/entry/simplifiable_spec.rb b/spec/lib/gitlab/config/entry/simplifiable_spec.rb similarity index 97% rename from spec/lib/gitlab/ci/config/entry/simplifiable_spec.rb rename to spec/lib/gitlab/config/entry/simplifiable_spec.rb index 395062207a3..bc8387ada67 100644 --- a/spec/lib/gitlab/ci/config/entry/simplifiable_spec.rb +++ b/spec/lib/gitlab/config/entry/simplifiable_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Simplifiable do +describe Gitlab::Config::Entry::Simplifiable do describe '.strategy' do let(:entry) do Class.new(described_class) do diff --git a/spec/lib/gitlab/ci/config/entry/undefined_spec.rb b/spec/lib/gitlab/config/entry/undefined_spec.rb similarity index 93% rename from spec/lib/gitlab/ci/config/entry/undefined_spec.rb rename to spec/lib/gitlab/config/entry/undefined_spec.rb index fdf48d84192..48f9d276c95 100644 --- a/spec/lib/gitlab/ci/config/entry/undefined_spec.rb +++ b/spec/lib/gitlab/config/entry/undefined_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Undefined do +describe Gitlab::Config::Entry::Undefined do let(:entry) { described_class.new } describe '#leaf?' do diff --git a/spec/lib/gitlab/ci/config/entry/unspecified_spec.rb b/spec/lib/gitlab/config/entry/unspecified_spec.rb similarity index 92% rename from spec/lib/gitlab/ci/config/entry/unspecified_spec.rb rename to spec/lib/gitlab/config/entry/unspecified_spec.rb index 66f88fa35b6..64421824a12 100644 --- a/spec/lib/gitlab/ci/config/entry/unspecified_spec.rb +++ b/spec/lib/gitlab/config/entry/unspecified_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Unspecified do +describe Gitlab::Config::Entry::Unspecified do let(:unspecified) { described_class.new(entry) } let(:entry) { spy('Entry') } diff --git a/spec/lib/gitlab/ci/config/entry/validatable_spec.rb b/spec/lib/gitlab/config/entry/validatable_spec.rb similarity index 84% rename from spec/lib/gitlab/ci/config/entry/validatable_spec.rb rename to spec/lib/gitlab/config/entry/validatable_spec.rb index ae2a7a51ba6..5a8f9766d23 100644 --- a/spec/lib/gitlab/ci/config/entry/validatable_spec.rb +++ b/spec/lib/gitlab/config/entry/validatable_spec.rb @@ -1,9 +1,9 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Validatable do +describe Gitlab::Config::Entry::Validatable do let(:entry) do - Class.new(Gitlab::Ci::Config::Entry::Node) do - include Gitlab::Ci::Config::Entry::Validatable + Class.new(Gitlab::Config::Entry::Node) do + include Gitlab::Config::Entry::Validatable end end @@ -20,7 +20,7 @@ describe Gitlab::Ci::Config::Entry::Validatable do it 'returns validator' do expect(entry.validator.superclass) - .to be Gitlab::Ci::Config::Entry::Validator + .to be Gitlab::Config::Entry::Validator end it 'returns only one validator to mitigate leaks' do diff --git a/spec/lib/gitlab/ci/config/entry/validator_spec.rb b/spec/lib/gitlab/config/entry/validator_spec.rb similarity index 96% rename from spec/lib/gitlab/ci/config/entry/validator_spec.rb rename to spec/lib/gitlab/config/entry/validator_spec.rb index 172b6b47a4f..efa16c4265c 100644 --- a/spec/lib/gitlab/ci/config/entry/validator_spec.rb +++ b/spec/lib/gitlab/config/entry/validator_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Entry::Validator do +describe Gitlab::Config::Entry::Validator do let(:validator) { Class.new(described_class) } let(:validator_instance) { validator.new(node) } let(:node) { spy('node') } diff --git a/spec/lib/gitlab/ci/config/loader_spec.rb b/spec/lib/gitlab/config/loader/yaml_spec.rb similarity index 84% rename from spec/lib/gitlab/ci/config/loader_spec.rb rename to spec/lib/gitlab/config/loader/yaml_spec.rb index 590fc8904c1..44c9a3896a8 100644 --- a/spec/lib/gitlab/ci/config/loader_spec.rb +++ b/spec/lib/gitlab/config/loader/yaml_spec.rb @@ -1,6 +1,6 @@ require 'spec_helper' -describe Gitlab::Ci::Config::Loader do +describe Gitlab::Config::Loader::Yaml do let(:loader) { described_class.new(yml) } context 'when yaml syntax is correct' do @@ -31,7 +31,7 @@ describe Gitlab::Ci::Config::Loader do describe '#load!' do it 'raises error' do expect { loader.load! }.to raise_error( - Gitlab::Ci::Config::Loader::FormatError, + Gitlab::Config::Loader::FormatError, 'Invalid configuration format' ) end @@ -43,7 +43,7 @@ describe Gitlab::Ci::Config::Loader do describe '#initialize' do it 'raises FormatError' do - expect { loader }.to raise_error(Gitlab::Ci::Config::Loader::FormatError, 'Unknown alias: bad_alias') + expect { loader }.to raise_error(Gitlab::Config::Loader::FormatError, 'Unknown alias: bad_alias') end end end diff --git a/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb b/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb new file mode 100644 index 00000000000..f518bb3dc3e --- /dev/null +++ b/spec/lib/gitlab/database/count/exact_count_strategy_spec.rb @@ -0,0 +1,34 @@ +require 'spec_helper' + +describe Gitlab::Database::Count::ExactCountStrategy do + before do + create_list(:project, 3) + create(:identity) + end + + let(:models) { [Project, Identity] } + + subject { described_class.new(models).count } + + describe '#count' do + it 'counts all models' do + expect(models).to all(receive(:count).and_call_original) + + expect(subject).to eq({ Project => 3, Identity => 1 }) + end + end + + describe '.enabled?' do + it 'is enabled for PostgreSQL' do + allow(Gitlab::Database).to receive(:postgresql?).and_return(true) + + expect(described_class.enabled?).to be_truthy + end + + it 'is enabled for MySQL' do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + + expect(described_class.enabled?).to be_truthy + end + end +end diff --git a/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb new file mode 100644 index 00000000000..b44e8c5a110 --- /dev/null +++ b/spec/lib/gitlab/database/count/reltuples_count_strategy_spec.rb @@ -0,0 +1,48 @@ +require 'spec_helper' + +describe Gitlab::Database::Count::ReltuplesCountStrategy do + before do + create_list(:project, 3) + create(:identity) + end + + let(:models) { [Project, Identity] } + subject { described_class.new(models).count } + + describe '#count', :postgresql do + context 'when reltuples is up to date' do + before do + ActiveRecord::Base.connection.execute('ANALYZE projects') + ActiveRecord::Base.connection.execute('ANALYZE identities') + end + + it 'uses statistics to do the count' do + models.each { |model| expect(model).not_to receive(:count) } + + expect(subject).to eq({ Project => 3, Identity => 1 }) + end + end + + context 'insufficient permissions' do + it 'returns an empty hash' do + allow(ActiveRecord::Base).to receive(:transaction).and_raise(PG::InsufficientPrivilege) + + expect(subject).to eq({}) + end + end + end + + describe '.enabled?' do + it 'is enabled for PostgreSQL' do + allow(Gitlab::Database).to receive(:postgresql?).and_return(true) + + expect(described_class.enabled?).to be_truthy + end + + it 'is disabled for MySQL' do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + + expect(described_class.enabled?).to be_falsey + end + end +end diff --git a/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb new file mode 100644 index 00000000000..203f9344a41 --- /dev/null +++ b/spec/lib/gitlab/database/count/tablesample_count_strategy_spec.rb @@ -0,0 +1,65 @@ +require 'spec_helper' + +describe Gitlab::Database::Count::TablesampleCountStrategy do + before do + create_list(:project, 3) + create(:identity) + end + + let(:models) { [Project, Identity] } + let(:strategy) { described_class.new(models) } + + subject { strategy.count } + + describe '#count', :postgresql do + let(:estimates) { { Project => threshold + 1, Identity => threshold - 1 } } + let(:threshold) { Gitlab::Database::Count::TablesampleCountStrategy::EXACT_COUNT_THRESHOLD } + + before do + allow(strategy).to receive(:size_estimates).with(check_statistics: false).and_return(estimates) + end + + context 'for tables with an estimated small size' do + it 'performs an exact count' do + expect(Identity).to receive(:count).and_call_original + + expect(subject).to include({ Identity => 1 }) + end + end + + context 'for tables with an estimated large size' do + it 'performs a tablesample count' do + expect(Project).not_to receive(:count) + + result = subject + expect(result[Project]).to eq(3) + end + end + + context 'insufficient permissions' do + it 'returns an empty hash' do + allow(strategy).to receive(:size_estimates).and_raise(PG::InsufficientPrivilege) + + expect(subject).to eq({}) + end + end + end + + describe '.enabled?' do + before do + stub_feature_flags(tablesample_counts: true) + end + + it 'is enabled for PostgreSQL' do + allow(Gitlab::Database).to receive(:postgresql?).and_return(true) + + expect(described_class.enabled?).to be_truthy + end + + it 'is disabled for MySQL' do + allow(Gitlab::Database).to receive(:postgresql?).and_return(false) + + expect(described_class.enabled?).to be_falsey + end + end +end diff --git a/spec/lib/gitlab/database/count_spec.rb b/spec/lib/gitlab/database/count_spec.rb index 407d9470785..1d096b8fa7c 100644 --- a/spec/lib/gitlab/database/count_spec.rb +++ b/spec/lib/gitlab/database/count_spec.rb @@ -8,63 +8,51 @@ describe Gitlab::Database::Count do let(:models) { [Project, Identity] } - describe '.approximate_counts' do - context 'with MySQL' do - context 'when reltuples have not been updated' do - it 'counts all models the normal way' do - expect(Gitlab::Database).to receive(:postgresql?).and_return(false) + context '.approximate_counts' do + context 'selecting strategies' do + let(:strategies) { [double('s1', enabled?: true), double('s2', enabled?: false)] } - expect(Project).to receive(:count).and_call_original - expect(Identity).to receive(:count).and_call_original + it 'uses only enabled strategies' do + expect(strategies[0]).to receive(:new).and_return(double('strategy1', count: {})) + expect(strategies[1]).not_to receive(:new) - expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) - end + described_class.approximate_counts(models, strategies: strategies) end end - context 'with PostgreSQL', :postgresql do - describe 'when reltuples have not been updated' do - it 'counts all models the normal way' do - expect(described_class).to receive(:reltuples_from_recently_updated).with(%w(projects identities)).and_return({}) + context 'fallbacks' do + subject { described_class.approximate_counts(models, strategies: strategies) } - expect(Project).to receive(:count).and_call_original - expect(Identity).to receive(:count).and_call_original - expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) - end + let(:strategies) do + [ + double('s1', enabled?: true, new: first_strategy), + double('s2', enabled?: true, new: second_strategy) + ] end - describe 'no permission' do - it 'falls back to standard query' do - allow(described_class).to receive(:postgresql_estimate_query).and_raise(PG::InsufficientPrivilege) + let(:first_strategy) { double('first strategy', count: {}) } + let(:second_strategy) { double('second strategy', count: {}) } - expect(Project).to receive(:count).and_call_original - expect(Identity).to receive(:count).and_call_original - expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) - end + it 'gets results from first strategy' do + expect(strategies[0]).to receive(:new).with(models).and_return(first_strategy) + expect(first_strategy).to receive(:count) + + subject end - describe 'when some reltuples have been updated' do - it 'counts projects in the fast way' do - expect(described_class).to receive(:reltuples_from_recently_updated).with(%w(projects identities)).and_return({ 'projects' => 3 }) + it 'gets more results from second strategy if some counts are missing' do + expect(first_strategy).to receive(:count).and_return({ Project => 3 }) + expect(strategies[1]).to receive(:new).with([Identity]).and_return(second_strategy) + expect(second_strategy).to receive(:count).and_return({ Identity => 1 }) - expect(Project).not_to receive(:count).and_call_original - expect(Identity).to receive(:count).and_call_original - expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) - end + expect(subject).to eq({ Project => 3, Identity => 1 }) end - describe 'when all reltuples have been updated' do - before do - ActiveRecord::Base.connection.execute('ANALYZE projects') - ActiveRecord::Base.connection.execute('ANALYZE identities') - end + it 'does not get more results as soon as all counts are present' do + expect(first_strategy).to receive(:count).and_return({ Project => 3, Identity => 1 }) + expect(strategies[1]).not_to receive(:new) - it 'counts models with the standard way' do - expect(Project).not_to receive(:count) - expect(Identity).not_to receive(:count) - - expect(described_class.approximate_counts(models)).to eq({ Project => 3, Identity => 1 }) - end + subject end end end diff --git a/spec/lib/gitlab/import_export/all_models.yml b/spec/lib/gitlab/import_export/all_models.yml index 1d184375a52..31ab11bbf8d 100644 --- a/spec/lib/gitlab/import_export/all_models.yml +++ b/spec/lib/gitlab/import_export/all_models.yml @@ -121,6 +121,8 @@ pipelines: - artifacts - pipeline_schedule - merge_requests +- deployments +- environments pipeline_variables: - pipeline stages: @@ -245,6 +247,7 @@ project: - protected_branches - protected_tags - project_members +- project_repository - users - requesters - deploy_keys_projects diff --git a/spec/migrations/clean_up_for_members_spec.rb b/spec/migrations/clean_up_for_members_spec.rb index 0258860d169..7876536cb3e 100644 --- a/spec/migrations/clean_up_for_members_spec.rb +++ b/spec/migrations/clean_up_for_members_spec.rb @@ -3,6 +3,7 @@ require Rails.root.join('db', 'migrate', '20171216111734_clean_up_for_members.rb describe CleanUpForMembers, :migration do let(:migration) { described_class.new } + let(:groups) { table(:namespaces) } let!(:group_member) { create_group_member } let!(:unbinded_group_member) { create_group_member } let!(:invited_group_member) { create_group_member(true) } @@ -25,7 +26,7 @@ describe CleanUpForMembers, :migration do end def create_group_member(invited = false) - fill_member(GroupMember.new(group: create_group), invited) + fill_member(GroupMember.new(source_id: create_group.id, source_type: 'Namespace'), invited) end def create_project_member(invited = false) @@ -54,7 +55,7 @@ describe CleanUpForMembers, :migration do def create_group name = FFaker::Lorem.characters(10) - Group.create(name: name, path: name.downcase.gsub(/\s/, '_')) + groups.create!(type: 'Group', name: name, path: name.downcase.gsub(/\s/, '_')) end def create_project diff --git a/spec/migrations/delete_inconsistent_internal_id_records_spec.rb b/spec/migrations/delete_inconsistent_internal_id_records_spec.rb index 4af51217031..8c55daf0d37 100644 --- a/spec/migrations/delete_inconsistent_internal_id_records_spec.rb +++ b/spec/migrations/delete_inconsistent_internal_id_records_spec.rb @@ -94,17 +94,18 @@ describe DeleteInconsistentInternalIdRecords, :migration do end context 'for milestones (by group)' do - # milestones (by group) is a little different than all of the other models - let!(:group1) { create(:group) } - let!(:group2) { create(:group) } - let!(:group3) { create(:group) } + # milestones (by group) is a little different than most of the other models + let(:groups) { table(:namespaces) } + let(:group1) { groups.create(name: 'Group 1', type: 'Group', path: 'group_1') } + let(:group2) { groups.create(name: 'Group 2', type: 'Group', path: 'group_2') } + let(:group3) { groups.create(name: 'Group 2', type: 'Group', path: 'group_3') } let(:internal_id_query) { ->(group) { InternalId.where(usage: InternalId.usages['milestones'], namespace: group) } } before do - 3.times { create(:milestone, group: group1) } - 3.times { create(:milestone, group: group2) } - 3.times { create(:milestone, group: group3) } + 3.times { create(:milestone, group_id: group1.id) } + 3.times { create(:milestone, group_id: group2.id) } + 3.times { create(:milestone, group_id: group3.id) } internal_id_query.call(group1).first.tap do |iid| iid.last_value = iid.last_value - 2 diff --git a/spec/models/environment_status_spec.rb b/spec/models/environment_status_spec.rb index 90f7e4a4590..9da16dea929 100644 --- a/spec/models/environment_status_spec.rb +++ b/spec/models/environment_status_spec.rb @@ -92,16 +92,12 @@ describe EnvironmentStatus do end describe '.build_environments_status' do - subject { described_class.send(:build_environments_status, merge_request, user, sha) } + subject { described_class.send(:build_environments_status, merge_request, user, pipeline) } let!(:build) { create(:ci_build, :deploy_to_production, pipeline: pipeline) } let(:environment) { build.deployment.environment } let(:user) { project.owner } - before do - build.deployment&.update!(sha: sha) - end - context 'when environment is created on a forked project' do let(:project) { create(:project, :repository) } let(:forked) { fork_project(project, user, repository: true) } @@ -160,6 +156,39 @@ describe EnvironmentStatus do expect(subject.count).to eq(0) end end + + context 'when multiple deployments with the same SHA in different environments' do + let(:pipeline2) { create(:ci_pipeline, sha: sha, project: project) } + let!(:build2) { create(:ci_build, :start_review_app, pipeline: pipeline2) } + + it 'returns deployments related to the head pipeline' do + expect(subject.count).to eq(1) + expect(subject[0].environment).to eq(environment) + expect(subject[0].merge_request).to eq(merge_request) + expect(subject[0].sha).to eq(sha) + end + end + + context 'when multiple deployments in the same pipeline for the same environments' do + let!(:build2) { create(:ci_build, :deploy_to_production, pipeline: pipeline) } + + it 'returns unique entries' do + expect(subject.count).to eq(1) + expect(subject[0].environment).to eq(environment) + expect(subject[0].merge_request).to eq(merge_request) + expect(subject[0].sha).to eq(sha) + end + end + + context 'when environment is stopped' do + before do + environment.stop! + end + + it 'does not return environment status' do + expect(subject.count).to eq(0) + end + end end end end diff --git a/spec/models/pool_repository_spec.rb b/spec/models/pool_repository_spec.rb index 6c904710fb5..541e78507e5 100644 --- a/spec/models/pool_repository_spec.rb +++ b/spec/models/pool_repository_spec.rb @@ -1,3 +1,5 @@ +# frozen_string_literal: true + require 'spec_helper' describe PoolRepository do diff --git a/spec/models/project_repository_spec.rb b/spec/models/project_repository_spec.rb new file mode 100644 index 00000000000..c966447fedc --- /dev/null +++ b/spec/models/project_repository_spec.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe ProjectRepository do + describe 'associations' do + it { is_expected.to belong_to(:shard) } + it { is_expected.to belong_to(:project) } + end + + describe '.find_project' do + it 'finds project by disk path' do + project = create(:project) + project.track_project_repository + + expect(described_class.find_project(project.disk_path)).to eq(project) + end + + it 'returns nil when it does not find the project' do + expect(described_class.find_project('@@unexisting/path/to/project')).to be_nil + end + end +end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index e98c69e636a..af5b0939ca2 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -54,6 +54,7 @@ describe Project do it { is_expected.to have_one(:gitlab_issue_tracker_service) } it { is_expected.to have_one(:external_wiki_service) } it { is_expected.to have_one(:project_feature) } + it { is_expected.to have_one(:project_repository) } it { is_expected.to have_one(:statistics).class_name('ProjectStatistics') } it { is_expected.to have_one(:import_data).class_name('ProjectImportData') } it { is_expected.to have_one(:last_event).class_name('Event') } @@ -1618,6 +1619,30 @@ describe Project do end end + describe '#track_project_repository' do + let(:project) { create(:project, :repository) } + + it 'creates a project_repository' do + project.track_project_repository + + expect(project.reload.project_repository).to be_present + expect(project.project_repository.disk_path).to eq(project.disk_path) + expect(project.project_repository.shard_name).to eq(project.repository_storage) + end + + it 'updates the project_repository' do + project.track_project_repository + + allow(project).to receive(:disk_path).and_return('@fancy/new/path') + + expect do + project.track_project_repository + end.not_to change(ProjectRepository, :count) + + expect(project.reload.project_repository.disk_path).to eq(project.disk_path) + end + end + describe '#create_repository' do let(:project) { create(:project, :repository) } let(:shell) { Gitlab::Shell.new } diff --git a/spec/models/remote_mirror_spec.rb b/spec/models/remote_mirror_spec.rb index da61a5f2771..b12ca79847c 100644 --- a/spec/models/remote_mirror_spec.rb +++ b/spec/models/remote_mirror_spec.rb @@ -174,7 +174,15 @@ describe RemoteMirror do end context 'with remote mirroring enabled' do + it 'defaults to disabling only protected branches' do + expect(remote_mirror.only_protected_branches?).to be_falsey + end + context 'with only protected branches enabled' do + before do + remote_mirror.only_protected_branches = true + end + context 'when it did not update in the last minute' do it 'schedules a RepositoryUpdateRemoteMirrorWorker to run now' do expect(RepositoryUpdateRemoteMirrorWorker).to receive(:perform_async).with(remote_mirror.id, Time.now) diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index 82ff2a002e0..3682e21ca40 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -236,7 +236,8 @@ describe Todo do create(:todo, target: create(:merge_request)) - expect(described_class.for_target(todo.target)).to eq([todo]) + expect(described_class.for_type(Issue.name).for_target(todo.target)) + .to contain_exactly(todo) end end diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb index cca449e9e56..2c40e266f5f 100644 --- a/spec/requests/api/helpers_spec.rb +++ b/spec/requests/api/helpers_spec.rb @@ -206,6 +206,19 @@ describe API::Helpers do expect { current_user }.to raise_error Gitlab::Auth::ExpiredError end + + context 'when impersonation is disabled' do + let(:personal_access_token) { create(:personal_access_token, :impersonation, user: user) } + + before do + stub_config_setting(impersonation_enabled: false) + env[Gitlab::Auth::UserAuthFinders::PRIVATE_TOKEN_HEADER] = personal_access_token.token + end + + it 'does not allow impersonation tokens' do + expect { current_user }.to raise_error Gitlab::Auth::ImpersonationDisabled + end + end end end diff --git a/spec/requests/api/namespaces_spec.rb b/spec/requests/api/namespaces_spec.rb index e2000ab42e8..145356c4df5 100644 --- a/spec/requests/api/namespaces_spec.rb +++ b/spec/requests/api/namespaces_spec.rb @@ -3,7 +3,7 @@ require 'spec_helper' describe API::Namespaces do let(:admin) { create(:admin) } let(:user) { create(:user) } - let!(:group1) { create(:group) } + let!(:group1) { create(:group, name: 'group.one') } let!(:group2) { create(:group, :nested) } describe "GET /namespaces" do diff --git a/spec/services/ci/archive_trace_service_spec.rb b/spec/services/ci/archive_trace_service_spec.rb new file mode 100644 index 00000000000..8e9cb65f3bc --- /dev/null +++ b/spec/services/ci/archive_trace_service_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe Ci::ArchiveTraceService, '#execute' do + subject { described_class.new.execute(job) } + + context 'when job is finished' do + let(:job) { create(:ci_build, :success, :trace_live) } + + it 'creates an archived trace' do + expect { subject }.not_to raise_error + + expect(job.reload.job_artifacts_trace).to be_exist + end + + context 'when trace is already archived' do + let!(:job) { create(:ci_build, :success, :trace_artifact) } + + it 'ignores an exception' do + expect { subject }.not_to raise_error + end + + it 'does not create an archived trace' do + expect { subject }.not_to change { Ci::JobArtifact.trace.count } + end + end + end + + context 'when job is running' do + let(:job) { create(:ci_build, :running, :trace_live) } + + it 'increments Prometheus counter, sends crash report to Sentry and ignore an error for continuing to archive' do + expect(Gitlab::Sentry) + .to receive(:track_exception) + .with(::Gitlab::Ci::Trace::ArchiveError, + issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/51502', + extra: { job_id: job.id } ).once + + expect(Rails.logger) + .to receive(:error) + .with("Failed to archive trace. id: #{job.id} message: Job is not finished yet") + .and_call_original + + expect(Gitlab::Metrics) + .to receive(:counter) + .with(:job_trace_archive_failed_total, "Counter of failed attempts of trace archiving") + .and_call_original + + expect { subject }.not_to raise_error + end + end +end diff --git a/spec/services/ci/create_pipeline_service_spec.rb b/spec/services/ci/create_pipeline_service_spec.rb index 4d9c5aabbda..a4582d1bc64 100644 --- a/spec/services/ci/create_pipeline_service_spec.rb +++ b/spec/services/ci/create_pipeline_service_spec.rb @@ -657,4 +657,37 @@ describe Ci::CreatePipelineService do end end end + + describe '#execute!' do + subject { service.execute!(*args) } + + let(:service) { described_class.new(project, user, ref: ref_name) } + let(:args) { [:push] } + + context 'when user has a permission to create a pipeline' do + let(:user) { create(:user) } + + before do + project.add_developer(user) + end + + it 'does not raise an error' do + expect { subject }.not_to raise_error + end + + it 'creates a pipeline' do + expect { subject }.to change { Ci::Pipeline.count }.by(1) + end + end + + context 'when user does not have a permission to create a pipeline' do + let(:user) { create(:user) } + + it 'raises an error' do + expect { subject } + .to raise_error(described_class::CreateError) + .with_message('Insufficient permissions to create a new pipeline') + end + end + end end diff --git a/spec/services/files/multi_service_spec.rb b/spec/services/files/multi_service_spec.rb index 5f3c8e82715..84c48d63c64 100644 --- a/spec/services/files/multi_service_spec.rb +++ b/spec/services/files/multi_service_spec.rb @@ -122,26 +122,47 @@ describe Files::MultiService do let(:action) { 'move' } let(:new_file_path) { 'files/ruby/new_popen.rb' } + let(:result) { subject.execute } + let(:blob) { repository.blob_at_branch(branch_name, new_file_path) } + context 'when original file has been updated' do before do update_file(original_file_path) end it 'rejects the commit' do - results = subject.execute - - expect(results[:status]).to eq(:error) - expect(results[:message]).to match(original_file_path) + expect(result[:status]).to eq(:error) + expect(result[:message]).to match(original_file_path) end end - context 'when original file have not been updated' do + context 'when original file has not been updated' do it 'moves the file' do - results = subject.execute - blob = project.repository.blob_at_branch(branch_name, new_file_path) - - expect(results[:status]).to eq(:success) + expect(result[:status]).to eq(:success) expect(blob).to be_present + expect(blob.data).to eq(file_content) + end + + context 'when content is nil' do + let(:file_content) { nil } + + it 'moves the existing content untouched' do + original_content = repository.blob_at_branch(branch_name, original_file_path).data + + expect(result[:status]).to eq(:success) + expect(blob).to be_present + expect(blob.data).to eq(original_content) + end + end + + context 'when content is an empty string' do + let(:file_content) { '' } + + it 'moves the file and empties it' do + expect(result[:status]).to eq(:success) + expect(blob).not_to be_nil + expect(blob.data).to eq('') + end end end end diff --git a/spec/support/helpers/features/sorting_helpers.rb b/spec/support/helpers/features/sorting_helpers.rb index ad0053ec5cf..a1ae428586e 100644 --- a/spec/support/helpers/features/sorting_helpers.rb +++ b/spec/support/helpers/features/sorting_helpers.rb @@ -13,7 +13,7 @@ module Spec module Features module SortingHelpers def sort_by(value) - find('button.dropdown-toggle').click + find('.filter-dropdown-container button.dropdown-menu-toggle').click page.within('.content ul.dropdown-menu.dropdown-menu-right li') do click_link(value) diff --git a/spec/support/helpers/sorting_helper.rb b/spec/support/helpers/sorting_helper.rb index 9496a94d8f4..e505a6b7258 100644 --- a/spec/support/helpers/sorting_helper.rb +++ b/spec/support/helpers/sorting_helper.rb @@ -10,7 +10,7 @@ # module SortingHelper def sorting_by(value) - find('button.dropdown-toggle').click + find('.filter-dropdown-container button.dropdown-menu-toggle').click page.within('.content ul.dropdown-menu.dropdown-menu-right li') do click_link value end diff --git a/spec/workers/archive_trace_worker_spec.rb b/spec/workers/archive_trace_worker_spec.rb index b768588c6e1..7244ad4f199 100644 --- a/spec/workers/archive_trace_worker_spec.rb +++ b/spec/workers/archive_trace_worker_spec.rb @@ -5,10 +5,11 @@ describe ArchiveTraceWorker do subject { described_class.new.perform(job&.id) } context 'when job is found' do - let(:job) { create(:ci_build) } + let(:job) { create(:ci_build, :trace_live) } it 'executes service' do - expect_any_instance_of(Gitlab::Ci::Trace).to receive(:archive!) + expect_any_instance_of(Ci::ArchiveTraceService) + .to receive(:execute).with(job) subject end @@ -18,7 +19,8 @@ describe ArchiveTraceWorker do let(:job) { nil } it 'does not execute service' do - expect_any_instance_of(Gitlab::Ci::Trace).not_to receive(:archive!) + expect_any_instance_of(Ci::ArchiveTraceService) + .not_to receive(:execute) subject end diff --git a/spec/workers/ci/archive_traces_cron_worker_spec.rb b/spec/workers/ci/archive_traces_cron_worker_spec.rb index 23f5dda298a..478fb7d2c0f 100644 --- a/spec/workers/ci/archive_traces_cron_worker_spec.rb +++ b/spec/workers/ci/archive_traces_cron_worker_spec.rb @@ -30,6 +30,13 @@ describe Ci::ArchiveTracesCronWorker do it_behaves_like 'archives trace' + it 'executes service' do + expect_any_instance_of(Ci::ArchiveTraceService) + .to receive(:execute).with(build) + + subject + end + context 'when a trace had already been archived' do let!(:build) { create(:ci_build, :success, :trace_live, :trace_artifact) } let!(:build2) { create(:ci_build, :success, :trace_live) } @@ -46,11 +53,12 @@ describe Ci::ArchiveTracesCronWorker do let!(:build) { create(:ci_build, :success, :trace_live) } before do + allow(Gitlab::Sentry).to receive(:track_exception) allow_any_instance_of(Gitlab::Ci::Trace).to receive(:archive!).and_raise('Unexpected error') end it 'puts a log' do - expect(Rails.logger).to receive(:error).with("Failed to archive stale live trace. id: #{build.id} message: Unexpected error") + expect(Rails.logger).to receive(:error).with("Failed to archive trace. id: #{build.id} message: Unexpected error") subject end diff --git a/spec/workers/pipeline_schedule_worker_spec.rb b/spec/workers/pipeline_schedule_worker_spec.rb index a2fe4734d47..c5a60e9855b 100644 --- a/spec/workers/pipeline_schedule_worker_spec.rb +++ b/spec/workers/pipeline_schedule_worker_spec.rb @@ -56,17 +56,89 @@ describe PipelineScheduleWorker do expect { subject }.not_to change { project.pipelines.count } end end + + context 'when gitlab-ci.yml is corrupted' do + before do + stub_ci_pipeline_yaml_file(YAML.dump(rspec: { variables: 'rspec' } )) + end + + it 'creates a failed pipeline with the reason' do + expect { subject }.to change { project.pipelines.count }.by(1) + expect(Ci::Pipeline.last).to be_config_error + expect(Ci::Pipeline.last.yaml_errors).not_to be_nil + end + end end context 'when the schedule is not runnable by the user' do - it 'deactivates the schedule' do - subject - - expect(pipeline_schedule.reload.active).to be_falsy + before do + expect(Gitlab::Sentry) + .to receive(:track_exception) + .with(Ci::CreatePipelineService::CreateError, + issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/41231', + extra: { schedule_id: pipeline_schedule.id } ).once end - it 'does not schedule a pipeline' do + it 'does not deactivate the schedule' do + subject + + expect(pipeline_schedule.reload.active).to be_truthy + end + + it 'increments Prometheus counter' do + expect(Gitlab::Metrics) + .to receive(:counter) + .with(:pipeline_schedule_creation_failed_total, "Counter of failed attempts of pipeline schedule creation") + .and_call_original + + subject + end + + it 'logging a pipeline error' do + expect(Rails.logger) + .to receive(:error) + .with(a_string_matching("Insufficient permissions to create a new pipeline")) + .and_call_original + + subject + end + + it 'does not create a pipeline' do expect { subject }.not_to change { project.pipelines.count } end + + it 'does not raise an exception' do + expect { subject }.not_to raise_error + end + end + + context 'when .gitlab-ci.yml is missing in the project' do + before do + stub_ci_pipeline_yaml_file(nil) + project.add_maintainer(user) + + expect(Gitlab::Sentry) + .to receive(:track_exception) + .with(Ci::CreatePipelineService::CreateError, + issue_url: 'https://gitlab.com/gitlab-org/gitlab-ce/issues/41231', + extra: { schedule_id: pipeline_schedule.id } ).once + end + + it 'logging a pipeline error' do + expect(Rails.logger) + .to receive(:error) + .with(a_string_matching("Missing .gitlab-ci.yml file")) + .and_call_original + + subject + end + + it 'does not create a pipeline' do + expect { subject }.not_to change { project.pipelines.count } + end + + it 'does not raise an exception' do + expect { subject }.not_to raise_error + end end end diff --git a/vendor/jupyter/values.yaml b/vendor/jupyter/values.yaml index 24136a7aca5..781d6e3042f 100644 --- a/vendor/jupyter/values.yaml +++ b/vendor/jupyter/values.yaml @@ -22,3 +22,4 @@ ingress: enabled: true annotations: kubernetes.io/ingress.class: "nginx" + kubernetes.io/tls-acme: "true" diff --git a/yarn.lock b/yarn.lock index 6d5ff62715b..623e38815aa 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1933,10 +1933,10 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -codesandbox-api@^0.0.18: - version "0.0.18" - resolved "https://registry.yarnpkg.com/codesandbox-api/-/codesandbox-api-0.0.18.tgz#56b96b37533f80d20c21861e5e477d3557e613ca" - integrity sha512-DHLR8QQpMplNDF9GDbV8EevwmF9mlMBQwiWB8bZBnP2NQQbklthqjpBwNjah8qlDgfD7vQNNcwT8uIZ24WZb7Q== +codesandbox-api@^0.0.20: + version "0.0.20" + resolved "https://registry.yarnpkg.com/codesandbox-api/-/codesandbox-api-0.0.20.tgz#174bcd76c9f31521175c6bceabc37da6b1fbc30b" + integrity sha512-jhxZzAmjCKBZad8QWMeueiQVFE87igK6F2DBOEVFFJO6jgTXT8qjuzGYepr+B8bjgo/icN7bc/2xmEMBA63s2w== codesandbox-import-util-types@^1.2.11: version "1.2.11" @@ -7261,12 +7261,12 @@ slugify@^1.3.1: resolved "https://registry.yarnpkg.com/slugify/-/slugify-1.3.1.tgz#f572127e8535329fbc6c1edb74ab856b61ad7de2" integrity sha512-6BwyhjF5tG5P8s+0DPNyJmBSBePG6iMyhjvIW5zGdA3tFik9PtK+yNkZgTeiroCRGZYgkHftFA62tGVK1EI9Kw== -smooshpack@^0.0.48: - version "0.0.48" - resolved "https://registry.yarnpkg.com/smooshpack/-/smooshpack-0.0.48.tgz#6fbeaaf59226a1fe500f56aa17185eed377d2823" - integrity sha512-BmaIr6fK6w7WBCI4V7tcZIg78WeE6X56zrhGSNk5vXavT1bQPXH+brZFq6Hwi833upY/yusG2FMVkf7TZsVv/w== +smooshpack@^0.0.53: + version "0.0.53" + resolved "https://registry.yarnpkg.com/smooshpack/-/smooshpack-0.0.53.tgz#aa397ca43619912e8ac0aa32012846ff4feaa5e8" + integrity sha512-FVXvKvZOz5+Tk/zUJ/wxM+ftu1yZtFEmeQl4chCqbzK/reU0L/OdDiYpx+/27Jt2VX09j08oIzwoyQ5fHH4+WQ== dependencies: - codesandbox-api "^0.0.18" + codesandbox-api "^0.0.20" codesandbox-import-utils "^1.2.3" lodash.isequal "^4.5.0"