diff options
Diffstat (limited to 'spec/models/project_spec.rb')
-rw-r--r-- | spec/models/project_spec.rb | 412 |
1 files changed, 342 insertions, 70 deletions
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 4e38bf7d3e3..2fe50f8c48a 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -7,6 +7,7 @@ RSpec.describe Project, factory_default: :keep do include GitHelpers include ExternalAuthorizationServiceHelpers include ReloadHelpers + include StubGitlabCalls using RSpec::Parameterized::TableSyntax let_it_be(:namespace) { create_default(:namespace).freeze } @@ -379,6 +380,7 @@ RSpec.describe Project, factory_default: :keep do it { is_expected.to validate_presence_of(:name) } it { is_expected.to validate_uniqueness_of(:name).scoped_to(:namespace_id) } it { is_expected.to validate_length_of(:name).is_at_most(255) } + it { is_expected.not_to allow_value('colon:in:path').for(:path) } # This is to validate that a specially crafted name cannot bypass a pattern match. See !72555 it { is_expected.to validate_presence_of(:path) } it { is_expected.to validate_length_of(:path).is_at_most(255) } it { is_expected.to validate_length_of(:description).is_at_most(2000) } @@ -1298,7 +1300,7 @@ RSpec.describe Project, factory_default: :keep do end end - describe '#default_owner' do + describe '#first_owner' do let_it_be(:owner) { create(:user) } let_it_be(:namespace) { create(:namespace, owner: owner) } @@ -1306,7 +1308,7 @@ RSpec.describe Project, factory_default: :keep do let(:project) { build(:project, namespace: namespace) } it 'is the namespace owner' do - expect(project.default_owner).to eq(owner) + expect(project.first_owner).to eq(owner) end end @@ -1315,9 +1317,9 @@ RSpec.describe Project, factory_default: :keep do let(:project) { build(:project, group: group, namespace: namespace) } it 'is the group owner' do - allow(group).to receive(:default_owner).and_return(Object.new) + allow(group).to receive(:first_owner).and_return(Object.new) - expect(project.default_owner).to eq(group.default_owner) + expect(project.first_owner).to eq(group.first_owner) end end end @@ -1358,51 +1360,51 @@ RSpec.describe Project, factory_default: :keep do project.reload.has_external_issue_tracker end - it 'is false when external issue tracker service is not active' do - create(:service, project: project, category: 'issue_tracker', active: false) + it 'is false when external issue tracker integration is not active' do + create(:integration, project: project, category: 'issue_tracker', active: false) is_expected.to eq(false) end - it 'is false when other service is active' do - create(:service, project: project, category: 'not_issue_tracker', active: true) + it 'is false when other integration is active' do + create(:integration, project: project, category: 'not_issue_tracker', active: true) is_expected.to eq(false) end - context 'when there is an active external issue tracker service' do - let!(:service) do - create(:service, project: project, type: 'JiraService', category: 'issue_tracker', active: true) + context 'when there is an active external issue tracker integration' do + let!(:integration) do + create(:integration, project: project, type: 'JiraService', category: 'issue_tracker', active: true) end specify { is_expected.to eq(true) } - it 'becomes false when external issue tracker service is destroyed' do + it 'becomes false when external issue tracker integration is destroyed' do expect do - Integration.find(service.id).delete + Integration.find(integration.id).delete end.to change { subject }.to(false) end - it 'becomes false when external issue tracker service becomes inactive' do + it 'becomes false when external issue tracker integration becomes inactive' do expect do - service.update_column(:active, false) + integration.update_column(:active, false) end.to change { subject }.to(false) end - context 'when there are two active external issue tracker services' do - let_it_be(:second_service) do - create(:service, project: project, type: 'CustomIssueTracker', category: 'issue_tracker', active: true) + context 'when there are two active external issue tracker integrations' do + let_it_be(:second_integration) do + create(:integration, project: project, type: 'CustomIssueTracker', category: 'issue_tracker', active: true) end - it 'does not become false when external issue tracker service is destroyed' do + it 'does not become false when external issue tracker integration is destroyed' do expect do - Integration.find(service.id).delete + Integration.find(integration.id).delete end.not_to change { subject } end - it 'does not become false when external issue tracker service becomes inactive' do + it 'does not become false when external issue tracker integration becomes inactive' do expect do - service.update_column(:active, false) + integration.update_column(:active, false) end.not_to change { subject } end end @@ -1454,13 +1456,13 @@ RSpec.describe Project, factory_default: :keep do specify { expect(has_external_wiki).to eq(true) } - it 'becomes false if the external wiki service is destroyed' do + it 'becomes false if the external wiki integration is destroyed' do expect do Integration.find(integration.id).delete end.to change { has_external_wiki }.to(false) end - it 'becomes false if the external wiki service becomes inactive' do + it 'becomes false if the external wiki integration becomes inactive' do expect do integration.update_column(:active, false) end.to change { has_external_wiki }.to(false) @@ -4580,11 +4582,25 @@ RSpec.describe Project, factory_default: :keep do include ProjectHelpers let_it_be(:group) { create(:group) } + let_it_be_with_reload(:project) { create(:project, namespace: group) } - let!(:project) { create(:project, project_level, namespace: group ) } let(:user) { create_user_from_membership(project, membership) } - context 'reporter level access' do + subject { described_class.filter_by_feature_visibility(feature, user) } + + shared_examples 'filter respects visibility' do + it 'respects visibility' do + enable_admin_mode!(user) if admin_mode + project.update!(visibility_level: Gitlab::VisibilityLevel.level_value(project_level.to_s)) + update_feature_access_level(project, feature_access_level) + + expected_objects = expected_count == 1 ? [project] : [] + + expect(subject).to eq(expected_objects) + end + end + + context 'with reporter level access' do let(:feature) { MergeRequest } where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do @@ -4592,20 +4608,11 @@ RSpec.describe Project, factory_default: :keep do end with_them do - it "respects visibility" do - enable_admin_mode!(user) if admin_mode - update_feature_access_level(project, feature_access_level) - - expected_objects = expected_count == 1 ? [project] : [] - - expect( - described_class.filter_by_feature_visibility(feature, user) - ).to eq(expected_objects) - end + it_behaves_like 'filter respects visibility' end end - context 'issues' do + context 'with feature issues' do let(:feature) { Issue } where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do @@ -4613,20 +4620,11 @@ RSpec.describe Project, factory_default: :keep do end with_them do - it "respects visibility" do - enable_admin_mode!(user) if admin_mode - update_feature_access_level(project, feature_access_level) - - expected_objects = expected_count == 1 ? [project] : [] - - expect( - described_class.filter_by_feature_visibility(feature, user) - ).to eq(expected_objects) - end + it_behaves_like 'filter respects visibility' end end - context 'wiki' do + context 'with feature wiki' do let(:feature) { :wiki } where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do @@ -4634,20 +4632,11 @@ RSpec.describe Project, factory_default: :keep do end with_them do - it "respects visibility" do - enable_admin_mode!(user) if admin_mode - update_feature_access_level(project, feature_access_level) - - expected_objects = expected_count == 1 ? [project] : [] - - expect( - described_class.filter_by_feature_visibility(feature, user) - ).to eq(expected_objects) - end + it_behaves_like 'filter respects visibility' end end - context 'code' do + context 'with feature code' do let(:feature) { :repository } where(:project_level, :feature_access_level, :membership, :admin_mode, :expected_count) do @@ -4655,16 +4644,7 @@ RSpec.describe Project, factory_default: :keep do end with_them do - it "respects visibility" do - enable_admin_mode!(user) if admin_mode - update_feature_access_level(project, feature_access_level) - - expected_objects = expected_count == 1 ? [project] : [] - - expect( - described_class.filter_by_feature_visibility(feature, user) - ).to eq(expected_objects) - end + it_behaves_like 'filter respects visibility' end end end @@ -6835,7 +6815,7 @@ RSpec.describe Project, factory_default: :keep do describe 'with integrations and chat names' do subject { create(:project) } - let(:integration) { create(:service, project: subject) } + let(:integration) { create(:integration, project: subject) } before do create_list(:chat_name, 5, integration: integration) @@ -7476,6 +7456,258 @@ RSpec.describe Project, factory_default: :keep do end end + describe '#enforced_runner_token_expiration_interval and #effective_runner_token_expiration_interval' do + shared_examples 'no enforced expiration interval' do + it { expect(subject.enforced_runner_token_expiration_interval).to be_nil } + end + + shared_examples 'enforced expiration interval' do |enforced_interval:| + it { expect(subject.enforced_runner_token_expiration_interval).to eq(enforced_interval) } + end + + shared_examples 'no effective expiration interval' do + it { expect(subject.effective_runner_token_expiration_interval).to be_nil } + end + + shared_examples 'effective expiration interval' do |effective_interval:| + it { expect(subject.effective_runner_token_expiration_interval).to eq(effective_interval) } + end + + context 'when there is no interval' do + let_it_be(:project) { create(:project) } + + subject { project } + + it_behaves_like 'no enforced expiration interval' + it_behaves_like 'no effective expiration interval' + end + + context 'when there is a project interval' do + let_it_be(:project) { create(:project, runner_token_expiration_interval: 3.days.to_i) } + + subject { project } + + it_behaves_like 'no enforced expiration interval' + it_behaves_like 'effective expiration interval', effective_interval: 3.days + end + + # runner_token_expiration_interval should not affect the expiration interval, only + # project_runner_token_expiration_interval should. + context 'when there is a site-wide enforced shared interval' do + before do + stub_application_setting(runner_token_expiration_interval: 5.days.to_i) + end + + let_it_be(:project) { create(:project) } + + subject { project } + + it_behaves_like 'no enforced expiration interval' + it_behaves_like 'no effective expiration interval' + end + + # group_runner_token_expiration_interval should not affect the expiration interval, only + # project_runner_token_expiration_interval should. + context 'when there is a site-wide enforced group interval' do + before do + stub_application_setting(group_runner_token_expiration_interval: 5.days.to_i) + end + + let_it_be(:project) { create(:project) } + + subject { project } + + it_behaves_like 'no enforced expiration interval' + it_behaves_like 'no effective expiration interval' + end + + context 'when there is a site-wide enforced project interval' do + before do + stub_application_setting(project_runner_token_expiration_interval: 5.days.to_i) + end + + let_it_be(:project) { create(:project) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 5.days + it_behaves_like 'effective expiration interval', effective_interval: 5.days + end + + # runner_token_expiration_interval should not affect the expiration interval, only + # project_runner_token_expiration_interval should. + context 'when there is a group-enforced group interval' do + let_it_be(:group_settings) { create(:namespace_settings, runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:group) { create(:group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group) } + + subject { project } + + it_behaves_like 'no enforced expiration interval' + it_behaves_like 'no effective expiration interval' + end + + # subgroup_runner_token_expiration_interval should not affect the expiration interval, only + # project_runner_token_expiration_interval should. + context 'when there is a group-enforced subgroup interval' do + let_it_be(:group_settings) { create(:namespace_settings, subgroup_runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:group) { create(:group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group) } + + subject { project } + + it_behaves_like 'no enforced expiration interval' + it_behaves_like 'no effective expiration interval' + end + + context 'when there is an owner group-enforced project interval' do + let_it_be(:group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:group) { create(:group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 4.days + it_behaves_like 'effective expiration interval', effective_interval: 4.days + end + + context 'when there is a grandparent group-enforced interval' do + let_it_be(:grandparent_group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 3.days.to_i) } + let_it_be(:grandparent_group) { create(:group, namespace_settings: grandparent_group_settings) } + let_it_be(:parent_group_settings) { create(:namespace_settings) } + let_it_be(:parent_group) { create(:group, parent: grandparent_group, namespace_settings: parent_group_settings) } + let_it_be(:group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:group) { create(:group, parent: parent_group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 3.days + it_behaves_like 'effective expiration interval', effective_interval: 3.days + end + + context 'when there is a parent group-enforced interval overridden by group-enforced interval' do + let_it_be(:parent_group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 5.days.to_i) } + let_it_be(:parent_group) { create(:group, namespace_settings: parent_group_settings) } + let_it_be(:group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:group) { create(:group, parent: parent_group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 4.days + it_behaves_like 'effective expiration interval', effective_interval: 4.days + end + + context 'when site-wide enforced interval overrides project interval' do + before do + stub_application_setting(project_runner_token_expiration_interval: 3.days.to_i) + end + + let_it_be(:project) { create(:project, runner_token_expiration_interval: 4.days.to_i) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 3.days + it_behaves_like 'effective expiration interval', effective_interval: 3.days + end + + context 'when project interval overrides site-wide enforced interval' do + before do + stub_application_setting(project_runner_token_expiration_interval: 5.days.to_i) + end + + let_it_be(:project) { create(:project, runner_token_expiration_interval: 4.days.to_i) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 5.days + it_behaves_like 'effective expiration interval', effective_interval: 4.days + + it 'has human-readable expiration intervals' do + expect(subject.enforced_runner_token_expiration_interval_human_readable).to eq('5d') + expect(subject.effective_runner_token_expiration_interval_human_readable).to eq('4d') + end + end + + context 'when site-wide enforced interval overrides group-enforced interval' do + before do + stub_application_setting(project_runner_token_expiration_interval: 3.days.to_i) + end + + let_it_be(:group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:group) { create(:group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 3.days + it_behaves_like 'effective expiration interval', effective_interval: 3.days + end + + context 'when group-enforced interval overrides site-wide enforced interval' do + before do + stub_application_setting(project_runner_token_expiration_interval: 5.days.to_i) + end + + let_it_be(:group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:group) { create(:group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 4.days + it_behaves_like 'effective expiration interval', effective_interval: 4.days + end + + context 'when group-enforced interval overrides project interval' do + let_it_be(:group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 3.days.to_i) } + let_it_be(:group) { create(:group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group, runner_token_expiration_interval: 4.days.to_i) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 3.days + it_behaves_like 'effective expiration interval', effective_interval: 3.days + end + + context 'when project interval overrides group-enforced interval' do + let_it_be(:group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 5.days.to_i) } + let_it_be(:group) { create(:group, namespace_settings: group_settings) } + let_it_be(:project) { create(:project, group: group, runner_token_expiration_interval: 4.days.to_i) } + + subject { project } + + it_behaves_like 'enforced expiration interval', enforced_interval: 5.days + it_behaves_like 'effective expiration interval', effective_interval: 4.days + end + + # Unrelated groups should not affect the expiration interval. + context 'when there is an enforced project interval in an unrelated group' do + let_it_be(:unrelated_group_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:unrelated_group) { create(:group, namespace_settings: unrelated_group_settings) } + let_it_be(:project) { create(:project) } + + subject { project } + + it_behaves_like 'no enforced expiration interval' + it_behaves_like 'no effective expiration interval' + end + + # Subgroups should not affect the parent group expiration interval. + context 'when there is an enforced project interval in a subgroup' do + let_it_be(:group) { create(:group) } + let_it_be(:subgroup_settings) { create(:namespace_settings, project_runner_token_expiration_interval: 4.days.to_i) } + let_it_be(:subgroup) { create(:group, parent: group, namespace_settings: subgroup_settings) } + let_it_be(:project) { create(:project, group: group) } + + subject { project } + + it_behaves_like 'no enforced expiration interval' + it_behaves_like 'no effective expiration interval' + end + end + it_behaves_like 'it has loose foreign keys' do let(:factory_name) { :project } end @@ -7551,6 +7783,46 @@ RSpec.describe Project, factory_default: :keep do end end + describe '#context_commits_enabled?' do + let_it_be(:project) { create(:project) } + + subject(:result) { project.context_commits_enabled? } + + context 'when context_commits feature flag is enabled' do + before do + stub_feature_flags(context_commits: true) + end + + it { is_expected.to be_truthy } + end + + context 'when context_commits feature flag is disabled' do + before do + stub_feature_flags(context_commits: false) + end + + it { is_expected.to be_falsey } + end + + context 'when context_commits feature flag is enabled on this project' do + before do + stub_feature_flags(context_commits: project) + end + + it { is_expected.to be_truthy } + end + + context 'when context_commits feature flag is enabled on another project' do + let(:another_project) { create(:project) } + + before do + stub_feature_flags(context_commits: another_project) + end + + it { is_expected.to be_falsey } + end + end + private def finish_job(export_job) |