diff options
Diffstat (limited to 'spec/models/project_spec.rb')
-rw-r--r-- | spec/models/project_spec.rb | 598 |
1 files changed, 348 insertions, 250 deletions
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index dfc8919e19d..e9bb01f4b23 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -14,6 +14,8 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do it_behaves_like 'having unique enum values' + it_behaves_like 'ensures runners_token is prefixed', :project + describe 'associations' do it { is_expected.to belong_to(:group) } it { is_expected.to belong_to(:namespace) } @@ -25,6 +27,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do it { is_expected.to have_many(:merge_requests) } it { is_expected.to have_many(:merge_request_metrics).class_name('MergeRequest::Metrics') } it { is_expected.to have_many(:issues) } + it { is_expected.to have_many(:work_items) } it { is_expected.to have_many(:incident_management_issuable_escalation_statuses).through(:issues).inverse_of(:project).class_name('IncidentManagement::IssuableEscalationStatus') } it { is_expected.to have_many(:milestones) } it { is_expected.to have_many(:project_members).dependent(:delete_all) } @@ -40,7 +43,9 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do it { is_expected.to have_many(:protected_branches) } it { is_expected.to have_many(:exported_protected_branches) } it { is_expected.to have_one(:wiki_repository).class_name('Projects::WikiRepository').inverse_of(:project) } + it { is_expected.to have_one(:design_management_repository).class_name('DesignManagement::Repository').inverse_of(:project) } it { is_expected.to have_one(:slack_integration) } + it { is_expected.to have_one(:catalog_resource) } it { is_expected.to have_one(:microsoft_teams_integration) } it { is_expected.to have_one(:mattermost_integration) } it { is_expected.to have_one(:hangouts_chat_integration) } @@ -50,6 +55,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do it { is_expected.to have_one(:packagist_integration) } it { is_expected.to have_one(:pushover_integration) } it { is_expected.to have_one(:apple_app_store_integration) } + it { is_expected.to have_one(:google_play_integration) } it { is_expected.to have_one(:asana_integration) } it { is_expected.to have_many(:boards) } it { is_expected.to have_one(:campfire_integration) } @@ -88,6 +94,8 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do it { is_expected.to have_one(:alerting_setting).class_name('Alerting::ProjectAlertingSetting') } it { is_expected.to have_one(:mock_ci_integration) } it { is_expected.to have_one(:mock_monitoring_integration) } + it { is_expected.to have_one(:service_desk_custom_email_verification).class_name('ServiceDesk::CustomEmailVerification') } + it { is_expected.to have_one(:container_registry_data_repair_detail).class_name('ContainerRegistry::DataRepairDetail') } it { is_expected.to have_many(:commit_statuses) } it { is_expected.to have_many(:ci_pipelines) } it { is_expected.to have_many(:ci_refs) } @@ -135,7 +143,9 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do it { is_expected.to have_many(:reviews).inverse_of(:project) } it { is_expected.to have_many(:packages).class_name('Packages::Package') } it { is_expected.to have_many(:package_files).class_name('Packages::PackageFile') } + it { is_expected.to have_many(:rpm_repository_files).class_name('Packages::Rpm::RepositoryFile').inverse_of(:project).dependent(:destroy) } it { is_expected.to have_many(:debian_distributions).class_name('Packages::Debian::ProjectDistribution').dependent(:destroy) } + it { is_expected.to have_many(:npm_metadata_caches).class_name('Packages::Npm::MetadataCache') } it { is_expected.to have_one(:packages_cleanup_policy).class_name('Packages::Cleanup::Policy').inverse_of(:project) } it { is_expected.to have_many(:pipeline_artifacts).dependent(:restrict_with_error) } it { is_expected.to have_many(:terraform_states).class_name('Terraform::State').inverse_of(:project) } @@ -780,7 +790,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do let(:new_project) do build(:project, - name: project_pending_deletion.name, + path: project_pending_deletion.path, namespace: project_pending_deletion.namespace) end @@ -874,46 +884,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end - describe '#has_packages?' do - let_it_be(:project) { create(:project, :public) } - - subject { project.has_packages?(package_type) } - - shared_examples 'returning true examples' do - let!(:package) { create("#{package_type}_package", project: project) } - - it { is_expected.to be true } - end - - shared_examples 'returning false examples' do - it { is_expected.to be false } - end - - context 'with maven packages' do - it_behaves_like 'returning true examples' do - let(:package_type) { :maven } - end - end - - context 'with npm packages' do - it_behaves_like 'returning true examples' do - let(:package_type) { :npm } - end - end - - context 'with conan packages' do - it_behaves_like 'returning true examples' do - let(:package_type) { :conan } - end - end - - context 'with no package type' do - it_behaves_like 'returning false examples' do - let(:package_type) { nil } - end - end - end - describe '#ci_pipelines' do let_it_be(:project) { create(:project) } @@ -939,6 +909,17 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe '#commit_notes' do + let_it_be(:project) { create(:project) } + + it "returns project's commit notes" do + note_1 = create(:note_on_commit, project: project, commit_id: 'commit_id_1') + note_2 = create(:note_on_commit, project: project, commit_id: 'commit_id_2') + + expect(project.commit_notes).to match_array([note_1, note_2]) + end + end + describe '#personal_namespace_holder?' do let_it_be(:group) { create(:group) } let_it_be(:namespace_user) { create(:user) } @@ -1115,7 +1096,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do 'restrict_user_defined_variables' => '', 'runner_token_expiration_interval' => '', 'separated_caches' => 'ci_', - 'opt_in_jwt' => 'ci_', 'allow_fork_pipelines_to_run_in_parent_project' => 'ci_', 'inbound_job_token_scope_enabled' => 'ci_', 'job_token_scope_enabled' => 'ci_outbound_' @@ -1151,7 +1131,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end describe '#ci_inbound_job_token_scope_enabled?' do - it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_' do + it_behaves_like 'a ci_cd_settings predicate method', prefix: 'ci_', default: true do let(:delegated_method) { :inbound_job_token_scope_enabled? } end end @@ -1380,6 +1360,60 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe '#to_reference_base' do + using RSpec::Parameterized::TableSyntax + + let_it_be(:user) { create(:user) } + let_it_be(:user_namespace) { user.namespace } + + let_it_be(:parent) { create(:group) } + let_it_be(:group) { create(:group, parent: parent) } + let_it_be(:another_group) { create(:group) } + + let_it_be(:project1) { create(:project, namespace: group) } + let_it_be(:project_namespace) { project1.project_namespace } + + # different project same group + let_it_be(:project2) { create(:project, namespace: group) } + let_it_be(:project_namespace2) { project2.project_namespace } + + # different project from different group + let_it_be(:project3) { create(:project) } + let_it_be(:project_namespace3) { project3.project_namespace } + + # testing references with namespace being: group, project namespace and user namespace + where(:project, :full, :from, :result) do + ref(:project1) | false | nil | nil + ref(:project1) | true | nil | lazy { project.full_path } + ref(:project1) | false | ref(:group) | lazy { project.path } + ref(:project1) | true | ref(:group) | lazy { project.full_path } + ref(:project1) | false | ref(:parent) | lazy { project.full_path } + ref(:project1) | true | ref(:parent) | lazy { project.full_path } + ref(:project1) | false | ref(:project1) | nil + ref(:project1) | true | ref(:project1) | lazy { project.full_path } + ref(:project1) | false | ref(:project_namespace) | nil + ref(:project1) | true | ref(:project_namespace) | lazy { project.full_path } + ref(:project1) | false | ref(:project2) | lazy { project.path } + ref(:project1) | true | ref(:project2) | lazy { project.full_path } + ref(:project1) | false | ref(:project_namespace2) | lazy { project.path } + ref(:project1) | true | ref(:project_namespace2) | lazy { project.full_path } + ref(:project1) | false | ref(:another_group) | lazy { project.full_path } + ref(:project1) | true | ref(:another_group) | lazy { project.full_path } + ref(:project1) | false | ref(:project3) | lazy { project.full_path } + ref(:project1) | true | ref(:project3) | lazy { project.full_path } + ref(:project1) | false | ref(:project_namespace3) | lazy { project.full_path } + ref(:project1) | true | ref(:project_namespace3) | lazy { project.full_path } + ref(:project1) | false | ref(:user_namespace) | lazy { project.full_path } + ref(:project1) | true | ref(:user_namespace) | lazy { project.full_path } + end + + with_them do + it 'returns correct path' do + expect(project.to_reference_base(from, full: full)).to eq(result) + end + end + end + describe '#merge_method' do where(:ff, :rebase, :method) do true | true | :ff @@ -1619,7 +1653,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do context 'with namespace' do before do @group = create(:group, name: 'gitlab') - @project = create(:project, name: 'gitlabhq', namespace: @group) + @project = create(:project, path: 'gitlabhq', namespace: @group) end it { expect(@project.to_param).to eq('gitlabhq') } @@ -2213,8 +2247,8 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do subject(:project) { build(:project, :private, namespace: namespace, service_desk_enabled: true) } before do - allow(Gitlab::IncomingEmail).to receive(:enabled?).and_return(true) - allow(Gitlab::IncomingEmail).to receive(:supports_wildcard?).and_return(true) + allow(Gitlab::Email::IncomingEmail).to receive(:enabled?).and_return(true) + allow(Gitlab::Email::IncomingEmail).to receive(:supports_wildcard?).and_return(true) end it 'is enabled' do @@ -2254,7 +2288,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do context 'when service_desk_email is disabled' do before do - allow(::Gitlab::ServiceDeskEmail).to receive(:enabled?).and_return(false) + allow(::Gitlab::Email::ServiceDeskEmail).to receive(:enabled?).and_return(false) end it_behaves_like 'with incoming email address' @@ -2263,7 +2297,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do context 'when service_desk_email is enabled' do before do config = double(enabled: true, address: 'foo+%{key}@bar.com') - allow(::Gitlab::ServiceDeskEmail).to receive(:config).and_return(config) + allow(::Gitlab::Email::ServiceDeskEmail).to receive(:config).and_return(config) end context 'when project_key is set' do @@ -2666,7 +2700,11 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end describe '#pages_url', feature_category: :pages do + let(:group_name) { 'group' } + let(:project_name) { 'project' } + let(:group) { create(:group, name: group_name) } + let(:nested_group) { create(:group, parent: group) } let(:project_path) { project_name.downcase } let(:project) do @@ -2689,101 +2727,168 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do .and_return(['http://example.com', port].compact.join(':')) end - context 'group page' do - let(:group_name) { 'Group' } - let(:project_name) { 'group.example.com' } - - it { is_expected.to eq("http://group.example.com") } + context 'when not using pages_unique_domain' do + subject { project.pages_url(with_unique_domain: false) } - context 'mixed case path' do - let(:project_path) { 'Group.example.com' } + context 'when pages_unique_domain feature flag is disabled' do + before do + stub_feature_flags(pages_unique_domain: false) + end - it { is_expected.to eq("http://group.example.com") } + it { is_expected.to eq('http://group.example.com/project') } end - end - context 'project page' do - let(:group_name) { 'Group' } - let(:project_name) { 'Project' } + context 'when pages_unique_domain feature flag is enabled' do + before do + stub_feature_flags(pages_unique_domain: true) - it { is_expected.to eq("http://group.example.com/project") } + project.project_setting.update!( + pages_unique_domain_enabled: pages_unique_domain_enabled, + pages_unique_domain: 'unique-domain' + ) + end - context 'mixed case path' do - let(:project_path) { 'Project' } + context 'when pages_unique_domain_enabled is false' do + let(:pages_unique_domain_enabled) { false } + + it { is_expected.to eq('http://group.example.com/project') } + end - it { is_expected.to eq("http://group.example.com/Project") } + context 'when pages_unique_domain_enabled is true' do + let(:pages_unique_domain_enabled) { true } + + it { is_expected.to eq('http://group.example.com/project') } + end end end - context 'when there is an explicit port' do - let(:port) { 3000 } + context 'when using pages_unique_domain' do + subject { project.pages_url(with_unique_domain: true) } - context 'when not in dev mode' do + context 'when pages_unique_domain feature flag is disabled' do before do - stub_rails_env('production') + stub_feature_flags(pages_unique_domain: false) end - context 'group page' do - let(:group_name) { 'Group' } - let(:project_name) { 'group.example.com' } - - it { is_expected.to eq('http://group.example.com:3000/group.example.com') } + it { is_expected.to eq('http://group.example.com/project') } + end - context 'mixed case path' do - let(:project_path) { 'Group.example.com' } + context 'when pages_unique_domain feature flag is enabled' do + before do + stub_feature_flags(pages_unique_domain: true) - it { is_expected.to eq('http://group.example.com:3000/Group.example.com') } - end + project.project_setting.update!( + pages_unique_domain_enabled: pages_unique_domain_enabled, + pages_unique_domain: 'unique-domain' + ) end - context 'project page' do - let(:group_name) { 'Group' } - let(:project_name) { 'Project' } + context 'when pages_unique_domain_enabled is false' do + let(:pages_unique_domain_enabled) { false } - it { is_expected.to eq("http://group.example.com:3000/project") } + it { is_expected.to eq('http://group.example.com/project') } + end - context 'mixed case path' do - let(:project_path) { 'Project' } + context 'when pages_unique_domain_enabled is true' do + let(:pages_unique_domain_enabled) { true } - it { is_expected.to eq("http://group.example.com:3000/Project") } - end + it { is_expected.to eq('http://unique-domain.example.com') } end end + end - context 'when in dev mode' do - before do - stub_rails_env('development') - end + context 'with nested group' do + let(:project) { create(:project, namespace: nested_group, name: project_name) } + let(:expected_url) { "http://group.example.com/#{nested_group.path}/#{project.path}" } - context 'group page' do - let(:group_name) { 'Group' } - let(:project_name) { 'group.example.com' } + context 'group page' do + let(:project_name) { 'group.example.com' } - it { is_expected.to eq('http://group.example.com:3000') } + it { is_expected.to eq(expected_url) } + end + + context 'project page' do + let(:project_name) { 'Project' } - context 'mixed case path' do - let(:project_path) { 'Group.example.com' } + it { is_expected.to eq(expected_url) } + end + end - it { is_expected.to eq('http://group.example.com:3000') } - end - end + context 'when the project matches its namespace url' do + let(:project_name) { 'group.example.com' } + + it { is_expected.to eq('http://group.example.com') } - context 'project page' do - let(:group_name) { 'Group' } - let(:project_name) { 'Project' } + context 'with different group name capitalization' do + let(:group_name) { 'Group' } - it { is_expected.to eq("http://group.example.com:3000/project") } + it { is_expected.to eq("http://group.example.com") } + end + + context 'with different project path capitalization' do + let(:project_path) { 'Group.example.com' } + + it { is_expected.to eq("http://group.example.com") } + end + + context 'with different project name capitalization' do + let(:project_name) { 'Project' } - context 'mixed case path' do - let(:project_path) { 'Project' } + it { is_expected.to eq("http://group.example.com/project") } + end + + context 'when there is an explicit port' do + let(:port) { 3000 } - it { is_expected.to eq("http://group.example.com:3000/Project") } + context 'when not in dev mode' do + before do + stub_rails_env('production') end + + it { is_expected.to eq('http://group.example.com:3000/group.example.com') } + end + + context 'when in dev mode' do + before do + stub_rails_env('development') + end + + it { is_expected.to eq('http://group.example.com:3000') } end end end end + describe '#pages_unique_url', feature_category: :pages do + let(:project_settings) { create(:project_setting, pages_unique_domain: 'unique-domain') } + let(:project) { build(:project, project_setting: project_settings) } + let(:domain) { 'example.com' } + + before do + allow(Settings.pages).to receive(:host).and_return(domain) + allow(Gitlab.config.pages).to receive(:url).and_return("http://#{domain}") + end + + it 'returns the pages unique url' do + expect(project.pages_unique_url).to eq('http://unique-domain.example.com') + end + end + + describe '#pages_unique_host', feature_category: :pages do + let(:project_settings) { create(:project_setting, pages_unique_domain: 'unique-domain') } + let(:project) { build(:project, project_setting: project_settings) } + let(:domain) { 'example.com' } + + before do + allow(Settings.pages).to receive(:host).and_return(domain) + allow(Gitlab.config.pages).to receive(:url).and_return("http://#{domain}") + end + + it 'returns the pages unique url' do + expect(project.pages_unique_host).to eq('unique-domain.example.com') + end + end + describe '#pages_namespace_url', feature_category: :pages do let(:group) { create(:group, name: group_name) } let(:project) { create(:project, namespace: group, name: project_name) } @@ -3030,6 +3135,12 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do expect(project.create_repository).to eq(false) expect(project.errors).not_to be_empty end + + it 'passes through default branch' do + expect(project.repository).to receive(:create_repository).with('pineapple') + + expect(project.create_repository(default_branch: 'pineapple')).to eq(true) + end end context 'using a forked repository' do @@ -3565,6 +3676,44 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe '#beautified_import_status_name' do + context 'when import not finished' do + it 'returns the right beautified import status' do + project = create(:project, :import_started) + + expect(project.beautified_import_status_name).to eq('started') + end + end + + context 'when import is finished' do + context 'when import is partially completed' do + it 'returns partially completed' do + project = create(:project) + + create(:import_state, project: project, status: 'finished', checksums: { + 'fetched' => { 'labels' => 10 }, + 'imported' => { 'labels' => 9 } + }) + + expect(project.beautified_import_status_name).to eq('partially completed') + end + end + + context 'when import is fully completed' do + it 'returns completed' do + project = create(:project) + + create(:import_state, project: project, status: 'finished', checksums: { + 'fetched' => { 'labels' => 10 }, + 'imported' => { 'labels' => 10 } + }) + + expect(project.beautified_import_status_name).to eq('completed') + end + end + end + end + describe '#add_import_job' do let(:import_jid) { '123' } @@ -3843,21 +3992,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end describe '#ancestors' do - context 'with linear_project_ancestors feature flag enabled' do - before do - stub_feature_flags(linear_project_ancestors: true) - end - - include_examples '#ancestors' - end - - context 'with linear_project_ancestors feature flag disabled' do - before do - stub_feature_flags(linear_project_ancestors: false) - end - - include_examples '#ancestors' - end + include_examples '#ancestors' end describe '#ancestors_upto' do @@ -4643,52 +4778,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end - describe '#pages_url' do - let(:group) { create(:group, name: 'Group') } - let(:nested_group) { create(:group, parent: group) } - let(:domain) { 'Example.com' } - - subject { project.pages_url } - - before do - allow(Settings.pages).to receive(:host).and_return(domain) - allow(Gitlab.config.pages).to receive(:url).and_return('http://example.com') - end - - context 'top-level group' do - let(:project) { create(:project, namespace: group, name: project_name) } - - context 'group page' do - let(:project_name) { 'group.example.com' } - - it { is_expected.to eq("http://group.example.com") } - end - - context 'project page' do - let(:project_name) { 'Project' } - - it { is_expected.to eq("http://group.example.com/project") } - end - end - - context 'nested group' do - let(:project) { create(:project, namespace: nested_group, name: project_name) } - let(:expected_url) { "http://group.example.com/#{nested_group.path}/#{project.path}" } - - context 'group page' do - let(:project_name) { 'group.example.com' } - - it { is_expected.to eq(expected_url) } - end - - context 'project page' do - let(:project_name) { 'Project' } - - it { is_expected.to eq(expected_url) } - end - end - end - describe '#lfs_http_url_to_repo' do let(:project) { create(:project) } @@ -5740,8 +5829,19 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do let_it_be(:project) { create(:project) } it 'exposes API v4 URL' do - expect(project.api_variables.first[:key]).to eq 'CI_API_V4_URL' - expect(project.api_variables.first[:value]).to include '/api/v4' + v4_variable = project.api_variables.find { |variable| variable[:key] == "CI_API_V4_URL" } + + expect(v4_variable).not_to be_nil + expect(v4_variable[:key]).to eq 'CI_API_V4_URL' + expect(v4_variable[:value]).to end_with '/api/v4' + end + + it 'exposes API GraphQL URL' do + graphql_variable = project.api_variables.find { |variable| variable[:key] == "CI_API_GRAPHQL_URL" } + + expect(graphql_variable).not_to be_nil + expect(graphql_variable[:key]).to eq 'CI_API_GRAPHQL_URL' + expect(graphql_variable[:value]).to end_with '/api/graphql' end it 'contains a URL variable for every supported API version' do @@ -5756,7 +5856,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end expect(project.api_variables.map { |variable| variable[:key] }) - .to contain_exactly(*required_variables) + .to include(*required_variables) end end @@ -5854,7 +5954,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do expect(project).to receive(:after_create_default_branch) expect(project).to receive(:refresh_markdown_cache!) expect(InternalId).to receive(:flush_records!).with(project: project) - expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:repository_size]) + expect(ProjectCacheWorker).to receive(:perform_async).with(project.id, [], [:repository_size, :wiki_size]) expect(DetectRepositoryLanguagesWorker).to receive(:perform_async).with(project.id) expect(AuthorizedProjectUpdate::ProjectRecalculateWorker).to receive(:perform_async).with(project.id) expect(project).to receive(:set_full_path) @@ -6049,7 +6149,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do it 'executes hooks which were backed off and are no longer backed off' do project = create(:project) hook = create(:project_hook, project: project, push_events: true) - WebHook::FAILURE_THRESHOLD.succ.times { hook.backoff! } + WebHooks::AutoDisabling::FAILURE_THRESHOLD.succ.times { hook.backoff! } expect_any_instance_of(ProjectHook).to receive(:async_execute).once @@ -6701,6 +6801,19 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe '.pending_data_repair_analysis' do + it 'returns projects that are not in ContainerRegistry::DataRepairDetail' do + project_1 = create(:project) + project_2 = create(:project) + + expect(described_class.pending_data_repair_analysis).to match_array([project_1, project_2]) + + create(:container_registry_data_repair_detail, project: project_1) + + expect(described_class.pending_data_repair_analysis).to match_array([project_2]) + end + end + describe '.deployments' do subject { project.deployments } @@ -7305,20 +7418,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end - describe 'with integrations and chat names' do - subject { create(:project) } - - let(:integration) { create(:integration, project: subject) } - - before do - create_list(:chat_name, 5, integration: integration) - end - - it 'does not remove chat names on removal' do - expect { subject.destroy! }.not_to change { ChatName.count } - end - end - describe 'with_issues_or_mrs_available_for_user' do before do Project.delete_all @@ -7380,6 +7479,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do context 'when feature flag `group_protected_branches` enabled' do before do stub_feature_flags(group_protected_branches: true) + stub_feature_flags(allow_protected_branches_for_group: true) end it 'return all protected branches' do @@ -7390,6 +7490,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do context 'when feature flag `group_protected_branches` disabled' do before do stub_feature_flags(group_protected_branches: false) + stub_feature_flags(allow_protected_branches_for_group: false) end it 'return only project-level protected branches' do @@ -7463,24 +7564,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end - describe '#self_monitoring?' do - let_it_be(:project) { create(:project) } - - subject { project.self_monitoring? } - - context 'when the project is instance self-monitoring' do - before do - stub_application_setting(self_monitoring_project_id: project.id) - end - - it { is_expected.to be true } - end - - context 'when the project is not self-monitoring' do - it { is_expected.to be false } - end - end - describe '#add_export_job' do let_it_be(:user) { create(:user) } let_it_be(:project) { create(:project) } @@ -7578,48 +7661,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end - describe '#has_packages?' do - let(:project) { create(:project, :public) } - - subject { project.has_packages?(package_type) } - - shared_examples 'has_package' do - context 'package of package_type exists' do - let!(:package) { create("#{package_type}_package", project: project) } - - it { is_expected.to be true } - end - - context 'package of package_type does not exist' do - it { is_expected.to be false } - end - end - - context 'with maven packages' do - it_behaves_like 'has_package' do - let(:package_type) { :maven } - end - end - - context 'with npm packages' do - it_behaves_like 'has_package' do - let(:package_type) { :npm } - end - end - - context 'with conan packages' do - it_behaves_like 'has_package' do - let(:package_type) { :conan } - end - end - - context 'calling has_package? with nil' do - let(:package_type) { nil } - - it { is_expected.to be false } - end - end - describe 'with Debian Distributions' do subject { create(:project) } @@ -7740,6 +7781,29 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe '#has_namespaced_npm_packages?' do + let_it_be(:namespace) { create(:namespace, path: 'test') } + let_it_be(:project) { create(:project, :public, namespace: namespace) } + + subject { project.has_namespaced_npm_packages? } + + context 'with scope of the namespace path' do + let_it_be(:package) { create(:npm_package, project: project, name: "@#{namespace.path}/foo") } + + it { is_expected.to be true } + end + + context 'without scope of the namespace path' do + let_it_be(:package) { create(:npm_package, project: project, name: "@someotherscope/foo") } + + it { is_expected.to be false } + end + + context 'without packages' do + it { is_expected.to be false } + end + end + describe '#package_already_taken?' do let_it_be(:namespace) { create(:namespace, path: 'test') } let_it_be(:project) { create(:project, :public, namespace: namespace) } @@ -7913,7 +7977,7 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do describe '#activity_path' do it 'returns the project activity_path' do - expected_path = "/#{project.namespace.path}/#{project.name}/activity" + expected_path = "/#{project.full_path}/activity" expect(project.activity_path).to eq(expected_path) end @@ -7952,8 +8016,8 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do using RSpec::Parameterized::TableSyntax where(:topic_list, :expected_result) do - ['topicA', 'topicB'] | %w[topicA topicB] # rubocop:disable Style/WordArray, Lint/BinaryOperatorWithIdenticalOperands - ['topicB', 'topicA'] | %w[topicB topicA] # rubocop:disable Style/WordArray, Lint/BinaryOperatorWithIdenticalOperands + ['topicA', 'topicB'] | %w[topicA topicB] # rubocop:disable Style/WordArray + ['topicB', 'topicA'] | %w[topicB topicA] # rubocop:disable Style/WordArray [' topicC ', ' topicD '] | %w[topicC topicD] ['topicE', 'topicF', 'topicE'] | %w[topicE topicF] # rubocop:disable Style/WordArray ['topicE ', 'topicF', ' topicE'] | %w[topicE topicF] @@ -8028,7 +8092,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do using RSpec::Parameterized::TableSyntax - # rubocop:disable Lint/BinaryOperatorWithIdenticalOperands where(:initial_visibility, :new_visibility, :new_topic_list, :expected_count_changes) do ref(:private) | nil | 't2, t3' | [0, 0, 0] ref(:internal) | nil | 't2, t3' | [-1, 0, 1] @@ -8052,7 +8115,6 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do ref(:public) | ref(:internal) | 't2, t3' | [-1, 0, 1] ref(:public) | ref(:private) | 't2, t3' | [-1, -1, 0] end - # rubocop:enable Lint/BinaryOperatorWithIdenticalOperands with_them do it 'increments or decrements counters of topics' do @@ -8519,6 +8581,16 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe '#content_editor_on_issues_feature_flag_enabled?' do + let_it_be(:group_project) { create(:project, :in_subgroup) } + + it_behaves_like 'checks parent group feature flag' do + let(:feature_flag_method) { :content_editor_on_issues_feature_flag_enabled? } + let(:feature_flag) { :content_editor_on_issues } + let(:subject_project) { group_project } + end + end + describe '#work_items_mvc_feature_flag_enabled?' do let_it_be(:group_project) { create(:project, :in_subgroup) } @@ -8842,6 +8914,32 @@ RSpec.describe Project, factory_default: :keep, feature_category: :projects do end end + describe 'deprecated project attributes' do + where(:project_attr, :project_method, :project_feature_attr) do + :wiki_enabled | :wiki_enabled? | :wiki_access_level + :builds_enabled | :builds_enabled? | :builds_access_level + :merge_requests_enabled | :merge_requests_enabled? | :merge_requests_access_level + :issues_enabled | :issues_enabled? | :issues_access_level + :snippets_enabled | :snippets_enabled? | :snippets_access_level + end + + with_them do + it 'delegates the attributes to project feature' do + project = Project.new(project_attr => false) + + expect(project.public_send(project_method)).to eq(false) + expect(project.project_feature.public_send(project_feature_attr)).to eq(ProjectFeature::DISABLED) + end + + it 'sets the default value' do + project = Project.new + + expect(project.public_send(project_method)).to eq(true) + expect(project.project_feature.public_send(project_feature_attr)).to eq(ProjectFeature::ENABLED) + end + end + end + private def finish_job(export_job) |