diff options
Diffstat (limited to 'spec/models')
31 files changed, 907 insertions, 237 deletions
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb index 129b2f92683..e128fe8a4b7 100644 --- a/spec/models/active_session_spec.rb +++ b/spec/models/active_session_spec.rb @@ -7,7 +7,10 @@ RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do end end - let(:session) { double(:session, id: '6919a6f1bb119dd7396fadc38fd18d0d') } + let(:session) do + double(:session, { id: '6919a6f1bb119dd7396fadc38fd18d0d', + '[]': {} }) + end let(:request) do double(:request, { diff --git a/spec/models/appearance_spec.rb b/spec/models/appearance_spec.rb index 28d482adebf..3e95aa2b5dd 100644 --- a/spec/models/appearance_spec.rb +++ b/spec/models/appearance_spec.rb @@ -78,4 +78,22 @@ describe Appearance do it { is_expected.to allow_value(hex).for(:message_font_color) } it { is_expected.not_to allow_value('000').for(:message_font_color) } end + + describe 'email_header_and_footer_enabled' do + context 'default email_header_and_footer_enabled flag value' do + it 'returns email_header_and_footer_enabled as true' do + appearance = build(:appearance) + + expect(appearance.email_header_and_footer_enabled?).to eq(false) + end + end + + context 'when setting email_header_and_footer_enabled flag value' do + it 'returns email_header_and_footer_enabled as true' do + appearance = build(:appearance, email_header_and_footer_enabled: true) + + expect(appearance.email_header_and_footer_enabled?).to eq(true) + end + end + end end diff --git a/spec/models/board_group_recent_visit_spec.rb b/spec/models/board_group_recent_visit_spec.rb index 59ad4e5417e..558be61824f 100644 --- a/spec/models/board_group_recent_visit_spec.rb +++ b/spec/models/board_group_recent_visit_spec.rb @@ -50,15 +50,25 @@ describe BoardGroupRecentVisit do end describe '#latest' do - it 'returns the most recent visited' do - board2 = create(:board, group: group) - board3 = create(:board, group: group) + def create_visit(time) + create :board_group_recent_visit, group: group, user: user, updated_at: time + end - create :board_group_recent_visit, group: board.group, board: board, user: user, updated_at: 7.days.ago - create :board_group_recent_visit, group: board2.group, board: board2, user: user, updated_at: 5.days.ago - recent = create :board_group_recent_visit, group: board3.group, board: board3, user: user, updated_at: 1.day.ago + it 'returns the most recent visited' do + create_visit(7.days.ago) + create_visit(5.days.ago) + recent = create_visit(1.day.ago) expect(described_class.latest(user, group)).to eq recent end + + it 'returns last 3 visited boards' do + create_visit(7.days.ago) + visit1 = create_visit(3.days.ago) + visit2 = create_visit(2.days.ago) + visit3 = create_visit(5.days.ago) + + expect(described_class.latest(user, group, count: 3)).to eq([visit2, visit1, visit3]) + end end end diff --git a/spec/models/board_project_recent_visit_spec.rb b/spec/models/board_project_recent_visit_spec.rb index 275581945fa..e404fb3bbdb 100644 --- a/spec/models/board_project_recent_visit_spec.rb +++ b/spec/models/board_project_recent_visit_spec.rb @@ -50,15 +50,25 @@ describe BoardProjectRecentVisit do end describe '#latest' do - it 'returns the most recent visited' do - board2 = create(:board, project: project) - board3 = create(:board, project: project) + def create_visit(time) + create :board_project_recent_visit, project: project, user: user, updated_at: time + end - create :board_project_recent_visit, project: board.project, board: board, user: user, updated_at: 7.days.ago - create :board_project_recent_visit, project: board2.project, board: board2, user: user, updated_at: 5.days.ago - recent = create :board_project_recent_visit, project: board3.project, board: board3, user: user, updated_at: 1.day.ago + it 'returns the most recent visited' do + create_visit(7.days.ago) + create_visit(5.days.ago) + recent = create_visit(1.day.ago) expect(described_class.latest(user, project)).to eq recent end + + it 'returns last 3 visited boards' do + create_visit(7.days.ago) + visit1 = create_visit(3.days.ago) + visit2 = create_visit(2.days.ago) + visit3 = create_visit(5.days.ago) + + expect(described_class.latest(user, project, count: 3)).to eq([visit2, visit1, visit3]) + end end end diff --git a/spec/models/ci/bridge_spec.rb b/spec/models/ci/bridge_spec.rb index 741cdfef1a5..b5ec8991720 100644 --- a/spec/models/ci/bridge_spec.rb +++ b/spec/models/ci/bridge_spec.rb @@ -22,4 +22,19 @@ describe Ci::Bridge do expect(status).to be_a Gitlab::Ci::Status::Success end end + + describe '#scoped_variables_hash' do + it 'returns a hash representing variables' do + variables = %w[ + CI_JOB_NAME CI_JOB_STAGE CI_COMMIT_SHA CI_COMMIT_SHORT_SHA + CI_COMMIT_BEFORE_SHA CI_COMMIT_REF_NAME CI_COMMIT_REF_SLUG + CI_PROJECT_ID CI_PROJECT_NAME CI_PROJECT_PATH + CI_PROJECT_PATH_SLUG CI_PROJECT_NAMESPACE CI_PIPELINE_IID + CI_CONFIG_PATH CI_PIPELINE_SOURCE CI_COMMIT_MESSAGE + CI_COMMIT_TITLE CI_COMMIT_DESCRIPTION + ] + + expect(bridge.scoped_variables_hash.keys).to include(*variables) + end + end end diff --git a/spec/models/ci/build_spec.rb b/spec/models/ci/build_spec.rb index 17540443688..fc75d3e23fb 100644 --- a/spec/models/ci/build_spec.rb +++ b/spec/models/ci/build_spec.rb @@ -23,7 +23,7 @@ describe Ci::Build do it { is_expected.to validate_presence_of(:ref) } it { is_expected.to respond_to(:has_trace?) } it { is_expected.to respond_to(:trace) } - it { is_expected.to delegate_method(:merge_request?).to(:pipeline) } + it { is_expected.to delegate_method(:merge_request_event?).to(:pipeline) } it { is_expected.to be_a(ArtifactMigratable) } @@ -2114,55 +2114,55 @@ describe Ci::Build do context 'returns variables' do let(:predefined_variables) do [ - { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true }, - { key: 'CI_PIPELINE_URL', value: project.web_url + "/pipelines/#{pipeline.id}", public: true }, - { key: 'CI_JOB_ID', value: build.id.to_s, public: true }, - { key: 'CI_JOB_URL', value: project.web_url + "/-/jobs/#{build.id}", public: true }, - { key: 'CI_JOB_TOKEN', value: 'my-token', public: false }, - { key: 'CI_BUILD_ID', value: build.id.to_s, public: true }, - { key: 'CI_BUILD_TOKEN', value: 'my-token', public: false }, - { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true }, - { key: 'CI_REGISTRY_PASSWORD', value: 'my-token', public: false }, - { key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false }, - { key: 'CI', value: 'true', public: true }, - { key: 'GITLAB_CI', value: 'true', public: true }, - { key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true }, - { key: 'CI_SERVER_NAME', value: 'GitLab', public: true }, - { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true }, - { key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s, public: true }, - { key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s, public: true }, - { key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s, public: true }, - { key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true }, - { key: 'CI_JOB_NAME', value: 'test', public: true }, - { key: 'CI_JOB_STAGE', value: 'test', public: true }, - { key: 'CI_COMMIT_SHA', value: build.sha, public: true }, - { key: 'CI_COMMIT_SHORT_SHA', value: build.short_sha, public: true }, - { key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true }, - { key: 'CI_COMMIT_REF_NAME', value: build.ref, public: true }, - { key: 'CI_COMMIT_REF_SLUG', value: build.ref_slug, public: true }, - { key: 'CI_NODE_TOTAL', value: '1', public: true }, - { key: 'CI_BUILD_REF', value: build.sha, public: true }, - { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true }, - { key: 'CI_BUILD_REF_NAME', value: build.ref, public: true }, - { key: 'CI_BUILD_REF_SLUG', value: build.ref_slug, public: true }, - { key: 'CI_BUILD_NAME', value: 'test', public: true }, - { key: 'CI_BUILD_STAGE', value: 'test', public: true }, - { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true }, - { key: 'CI_PROJECT_NAME', value: project.path, public: true }, - { key: 'CI_PROJECT_PATH', value: project.full_path, public: true }, - { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true }, - { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true }, - { key: 'CI_PROJECT_URL', value: project.web_url, public: true }, - { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true }, - { key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host, public: true }, - { key: 'CI_PAGES_URL', value: project.pages_url, public: true }, - { key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true }, - { key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true }, - { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true }, - { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true }, - { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true }, - { key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true }, - { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true } + { key: 'CI_PIPELINE_ID', value: pipeline.id.to_s, public: true, masked: false }, + { key: 'CI_PIPELINE_URL', value: project.web_url + "/pipelines/#{pipeline.id}", public: true, masked: false }, + { key: 'CI_JOB_ID', value: build.id.to_s, public: true, masked: false }, + { key: 'CI_JOB_URL', value: project.web_url + "/-/jobs/#{build.id}", public: true, masked: false }, + { key: 'CI_JOB_TOKEN', value: 'my-token', public: false, masked: false }, + { key: 'CI_BUILD_ID', value: build.id.to_s, public: true, masked: false }, + { key: 'CI_BUILD_TOKEN', value: 'my-token', public: false, masked: false }, + { key: 'CI_REGISTRY_USER', value: 'gitlab-ci-token', public: true, masked: false }, + { key: 'CI_REGISTRY_PASSWORD', value: 'my-token', public: false, masked: false }, + { key: 'CI_REPOSITORY_URL', value: build.repo_url, public: false, masked: false }, + { key: 'CI', value: 'true', public: true, masked: false }, + { key: 'GITLAB_CI', value: 'true', public: true, masked: false }, + { key: 'GITLAB_FEATURES', value: project.licensed_features.join(','), public: true, masked: false }, + { key: 'CI_SERVER_NAME', value: 'GitLab', public: true, masked: false }, + { key: 'CI_SERVER_VERSION', value: Gitlab::VERSION, public: true, masked: false }, + { key: 'CI_SERVER_VERSION_MAJOR', value: Gitlab.version_info.major.to_s, public: true, masked: false }, + { key: 'CI_SERVER_VERSION_MINOR', value: Gitlab.version_info.minor.to_s, public: true, masked: false }, + { key: 'CI_SERVER_VERSION_PATCH', value: Gitlab.version_info.patch.to_s, public: true, masked: false }, + { key: 'CI_SERVER_REVISION', value: Gitlab.revision, public: true, masked: false }, + { key: 'CI_JOB_NAME', value: 'test', public: true, masked: false }, + { key: 'CI_JOB_STAGE', value: 'test', public: true, masked: false }, + { key: 'CI_COMMIT_SHA', value: build.sha, public: true, masked: false }, + { key: 'CI_COMMIT_SHORT_SHA', value: build.short_sha, public: true, masked: false }, + { key: 'CI_COMMIT_BEFORE_SHA', value: build.before_sha, public: true, masked: false }, + { key: 'CI_COMMIT_REF_NAME', value: build.ref, public: true, masked: false }, + { key: 'CI_COMMIT_REF_SLUG', value: build.ref_slug, public: true, masked: false }, + { key: 'CI_NODE_TOTAL', value: '1', public: true, masked: false }, + { key: 'CI_BUILD_REF', value: build.sha, public: true, masked: false }, + { key: 'CI_BUILD_BEFORE_SHA', value: build.before_sha, public: true, masked: false }, + { key: 'CI_BUILD_REF_NAME', value: build.ref, public: true, masked: false }, + { key: 'CI_BUILD_REF_SLUG', value: build.ref_slug, public: true, masked: false }, + { key: 'CI_BUILD_NAME', value: 'test', public: true, masked: false }, + { key: 'CI_BUILD_STAGE', value: 'test', public: true, masked: false }, + { key: 'CI_PROJECT_ID', value: project.id.to_s, public: true, masked: false }, + { key: 'CI_PROJECT_NAME', value: project.path, public: true, masked: false }, + { key: 'CI_PROJECT_PATH', value: project.full_path, public: true, masked: false }, + { key: 'CI_PROJECT_PATH_SLUG', value: project.full_path_slug, public: true, masked: false }, + { key: 'CI_PROJECT_NAMESPACE', value: project.namespace.full_path, public: true, masked: false }, + { key: 'CI_PROJECT_URL', value: project.web_url, public: true, masked: false }, + { key: 'CI_PROJECT_VISIBILITY', value: 'private', public: true, masked: false }, + { key: 'CI_PAGES_DOMAIN', value: Gitlab.config.pages.host, public: true, masked: false }, + { key: 'CI_PAGES_URL', value: project.pages_url, public: true, masked: false }, + { key: 'CI_API_V4_URL', value: 'http://localhost/api/v4', public: true, masked: false }, + { key: 'CI_PIPELINE_IID', value: pipeline.iid.to_s, public: true, masked: false }, + { key: 'CI_CONFIG_PATH', value: pipeline.ci_yaml_file_path, public: true, masked: false }, + { key: 'CI_PIPELINE_SOURCE', value: pipeline.source, public: true, masked: false }, + { key: 'CI_COMMIT_MESSAGE', value: pipeline.git_commit_message, public: true, masked: false }, + { key: 'CI_COMMIT_TITLE', value: pipeline.git_commit_title, public: true, masked: false }, + { key: 'CI_COMMIT_DESCRIPTION', value: pipeline.git_commit_description, public: true, masked: false } ] end @@ -2175,10 +2175,10 @@ describe Ci::Build do describe 'variables ordering' do context 'when variables hierarchy is stubbed' do - let(:build_pre_var) { { key: 'build', value: 'value', public: true } } - let(:project_pre_var) { { key: 'project', value: 'value', public: true } } - let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true } } - let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true } } + let(:build_pre_var) { { key: 'build', value: 'value', public: true, masked: false } } + let(:project_pre_var) { { key: 'project', value: 'value', public: true, masked: false } } + let(:pipeline_pre_var) { { key: 'pipeline', value: 'value', public: true, masked: false } } + let(:build_yaml_var) { { key: 'yaml', value: 'value', public: true, masked: false } } before do allow(build).to receive(:predefined_variables) { [build_pre_var] } @@ -2200,7 +2200,7 @@ describe Ci::Build do project_pre_var, pipeline_pre_var, build_yaml_var, - { key: 'secret', value: 'value', public: false }]) + { key: 'secret', value: 'value', public: false, masked: false }]) end end @@ -2233,10 +2233,10 @@ describe Ci::Build do context 'when build has user' do let(:user_variables) do [ - { key: 'GITLAB_USER_ID', value: user.id.to_s, public: true }, - { key: 'GITLAB_USER_EMAIL', value: user.email, public: true }, - { key: 'GITLAB_USER_LOGIN', value: user.username, public: true }, - { key: 'GITLAB_USER_NAME', value: user.name, public: true } + { key: 'GITLAB_USER_ID', value: user.id.to_s, public: true, masked: false }, + { key: 'GITLAB_USER_EMAIL', value: user.email, public: true, masked: false }, + { key: 'GITLAB_USER_LOGIN', value: user.username, public: true, masked: false }, + { key: 'GITLAB_USER_NAME', value: user.name, public: true, masked: false } ] end @@ -2250,8 +2250,8 @@ describe Ci::Build do context 'when build has an environment' do let(:environment_variables) do [ - { key: 'CI_ENVIRONMENT_NAME', value: 'production', public: true }, - { key: 'CI_ENVIRONMENT_SLUG', value: 'prod-slug', public: true } + { key: 'CI_ENVIRONMENT_NAME', value: 'production', public: true, masked: false }, + { key: 'CI_ENVIRONMENT_SLUG', value: 'prod-slug', public: true, masked: false } ] end @@ -2286,7 +2286,7 @@ describe Ci::Build do before do environment_variables << - { key: 'CI_ENVIRONMENT_URL', value: url, public: true } + { key: 'CI_ENVIRONMENT_URL', value: url, public: true, masked: false } end context 'when the URL was set from the job' do @@ -2323,7 +2323,7 @@ describe Ci::Build do end let(:manual_variable) do - { key: 'CI_JOB_MANUAL', value: 'true', public: true } + { key: 'CI_JOB_MANUAL', value: 'true', public: true, masked: false } end it { is_expected.to include(manual_variable) } @@ -2331,7 +2331,7 @@ describe Ci::Build do context 'when build is for tag' do let(:tag_variable) do - { key: 'CI_COMMIT_TAG', value: 'master', public: true } + { key: 'CI_COMMIT_TAG', value: 'master', public: true, masked: false } end before do @@ -2343,7 +2343,7 @@ describe Ci::Build do context 'when CI variable is defined' do let(:ci_variable) do - { key: 'SECRET_KEY', value: 'secret_value', public: false } + { key: 'SECRET_KEY', value: 'secret_value', public: false, masked: false } end before do @@ -2358,7 +2358,7 @@ describe Ci::Build do let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + build.ref } let(:protected_variable) do - { key: 'PROTECTED_KEY', value: 'protected_value', public: false } + { key: 'PROTECTED_KEY', value: 'protected_value', public: false, masked: false } end before do @@ -2390,7 +2390,7 @@ describe Ci::Build do context 'when group CI variable is defined' do let(:ci_variable) do - { key: 'SECRET_KEY', value: 'secret_value', public: false } + { key: 'SECRET_KEY', value: 'secret_value', public: false, masked: false } end before do @@ -2405,7 +2405,7 @@ describe Ci::Build do let(:ref) { Gitlab::Git::BRANCH_REF_PREFIX + build.ref } let(:protected_variable) do - { key: 'PROTECTED_KEY', value: 'protected_value', public: false } + { key: 'PROTECTED_KEY', value: 'protected_value', public: false, masked: false } end before do @@ -2444,11 +2444,11 @@ describe Ci::Build do let(:trigger_request) { create(:ci_trigger_request, pipeline: pipeline, trigger: trigger) } let(:user_trigger_variable) do - { key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1', public: false } + { key: 'TRIGGER_KEY_1', value: 'TRIGGER_VALUE_1', public: false, masked: false } end let(:predefined_trigger_variable) do - { key: 'CI_PIPELINE_TRIGGERED', value: 'true', public: true } + { key: 'CI_PIPELINE_TRIGGERED', value: 'true', public: true, masked: false } end before do @@ -2480,7 +2480,7 @@ describe Ci::Build do context 'when pipeline has a variable' do let!(:pipeline_variable) { create(:ci_pipeline_variable, pipeline: pipeline) } - it { is_expected.to include(pipeline_variable.to_runner_variable) } + it { is_expected.to include(key: pipeline_variable.key, value: pipeline_variable.value, public: false, masked: false) } end context 'when a job was triggered by a pipeline schedule' do @@ -2497,16 +2497,16 @@ describe Ci::Build do pipeline_schedule.reload end - it { is_expected.to include(pipeline_schedule_variable.to_runner_variable) } + it { is_expected.to include(key: pipeline_schedule_variable.key, value: pipeline_schedule_variable.value, public: false, masked: false) } end context 'when container registry is enabled' do let(:container_registry_enabled) { true } let(:ci_registry) do - { key: 'CI_REGISTRY', value: 'registry.example.com', public: true } + { key: 'CI_REGISTRY', value: 'registry.example.com', public: true, masked: false } end let(:ci_registry_image) do - { key: 'CI_REGISTRY_IMAGE', value: project.container_registry_url, public: true } + { key: 'CI_REGISTRY_IMAGE', value: project.container_registry_url, public: true, masked: false } end context 'and is disabled for project' do @@ -2535,13 +2535,13 @@ describe Ci::Build do build.update(runner: runner) end - it { is_expected.to include({ key: 'CI_RUNNER_ID', value: runner.id.to_s, public: true }) } - it { is_expected.to include({ key: 'CI_RUNNER_DESCRIPTION', value: 'description', public: true }) } - it { is_expected.to include({ key: 'CI_RUNNER_TAGS', value: 'docker, linux', public: true }) } + it { is_expected.to include({ key: 'CI_RUNNER_ID', value: runner.id.to_s, public: true, masked: false }) } + it { is_expected.to include({ key: 'CI_RUNNER_DESCRIPTION', value: 'description', public: true, masked: false }) } + it { is_expected.to include({ key: 'CI_RUNNER_TAGS', value: 'docker, linux', public: true, masked: false }) } end context 'when build is for a deployment' do - let(:deployment_variable) { { key: 'KUBERNETES_TOKEN', value: 'TOKEN', public: false } } + let(:deployment_variable) { { key: 'KUBERNETES_TOKEN', value: 'TOKEN', public: false, masked: false } } before do build.environment = 'production' @@ -2555,7 +2555,7 @@ describe Ci::Build do end context 'when project has custom CI config path' do - let(:ci_config_path) { { key: 'CI_CONFIG_PATH', value: 'custom', public: true } } + let(:ci_config_path) { { key: 'CI_CONFIG_PATH', value: 'custom', public: true, masked: false } } before do project.update(ci_config_path: 'custom') @@ -2572,7 +2572,7 @@ describe Ci::Build do it "includes AUTO_DEVOPS_DOMAIN" do is_expected.to include( - { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true }) + { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true, masked: false }) end end @@ -2583,7 +2583,7 @@ describe Ci::Build do it "includes AUTO_DEVOPS_DOMAIN" do is_expected.not_to include( - { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true }) + { key: 'AUTO_DEVOPS_DOMAIN', value: 'example.com', public: true, masked: false }) end end end @@ -2598,9 +2598,9 @@ describe Ci::Build do variables = subject.reverse.uniq { |variable| variable[:key] }.reverse expect(variables) - .not_to include(key: 'MYVAR', value: 'myvar', public: true) + .not_to include(key: 'MYVAR', value: 'myvar', public: true, masked: false) expect(variables) - .to include(key: 'MYVAR', value: 'pipeline value', public: false) + .to include(key: 'MYVAR', value: 'pipeline value', public: false, masked: false) end end @@ -2616,13 +2616,13 @@ describe Ci::Build do it 'includes CI_NODE_INDEX' do is_expected.to include( - { key: 'CI_NODE_INDEX', value: index.to_s, public: true } + { key: 'CI_NODE_INDEX', value: index.to_s, public: true, masked: false } ) end it 'includes correct CI_NODE_TOTAL' do is_expected.to include( - { key: 'CI_NODE_TOTAL', value: total.to_s, public: true } + { key: 'CI_NODE_TOTAL', value: total.to_s, public: true, masked: false } ) end end @@ -2641,7 +2641,7 @@ describe Ci::Build do it 'returns static predefined variables' do expect(build.variables.size).to be >= 28 expect(build.variables) - .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true) + .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true, masked: false) expect(build).not_to be_persisted end end @@ -2651,8 +2651,8 @@ describe Ci::Build do let(:deploy_token_variables) do [ - { key: 'CI_DEPLOY_USER', value: deploy_token.username, public: true }, - { key: 'CI_DEPLOY_PASSWORD', value: deploy_token.token, public: false } + { key: 'CI_DEPLOY_USER', value: deploy_token.username, public: true, masked: false }, + { key: 'CI_DEPLOY_PASSWORD', value: deploy_token.token, public: false, masked: false } ] end @@ -2711,7 +2711,7 @@ describe Ci::Build do end expect(variables) - .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true) + .to include(key: 'CI_COMMIT_REF_NAME', value: 'feature', public: true, masked: false) end it 'does not return prohibited variables' do @@ -2734,6 +2734,122 @@ describe Ci::Build do end end + describe '#secret_group_variables' do + subject { build.secret_group_variables } + + let!(:variable) { create(:ci_group_variable, protected: true, group: group) } + + context 'when ref is branch' do + let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) } + + context 'when ref is protected' do + before do + create(:protected_branch, :developers_can_merge, name: 'master', project: project) + end + + it { is_expected.to include(variable) } + end + + context 'when ref is not protected' do + it { is_expected.not_to include(variable) } + end + end + + context 'when ref is tag' do + let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, project: project) } + + context 'when ref is protected' do + before do + create(:protected_tag, project: project, name: 'v*') + end + + it { is_expected.to include(variable) } + end + + context 'when ref is not protected' do + it { is_expected.not_to include(variable) } + end + end + + context 'when ref is merge request' do + let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) } + let(:pipeline) { merge_request.merge_request_pipelines.first } + let(:build) { create(:ci_build, ref: merge_request.source_branch, tag: false, pipeline: pipeline, project: project) } + + context 'when ref is protected' do + before do + create(:protected_branch, :developers_can_merge, name: merge_request.source_branch, project: project) + end + + it 'does not return protected variables as it is not supported for merge request pipelines' do + is_expected.not_to include(variable) + end + end + + context 'when ref is not protected' do + it { is_expected.not_to include(variable) } + end + end + end + + describe '#secret_project_variables' do + subject { build.secret_project_variables } + + let!(:variable) { create(:ci_variable, protected: true, project: project) } + + context 'when ref is branch' do + let(:build) { create(:ci_build, ref: 'master', tag: false, project: project) } + + context 'when ref is protected' do + before do + create(:protected_branch, :developers_can_merge, name: 'master', project: project) + end + + it { is_expected.to include(variable) } + end + + context 'when ref is not protected' do + it { is_expected.not_to include(variable) } + end + end + + context 'when ref is tag' do + let(:build) { create(:ci_build, ref: 'v1.1.0', tag: true, project: project) } + + context 'when ref is protected' do + before do + create(:protected_tag, project: project, name: 'v*') + end + + it { is_expected.to include(variable) } + end + + context 'when ref is not protected' do + it { is_expected.not_to include(variable) } + end + end + + context 'when ref is merge request' do + let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) } + let(:pipeline) { merge_request.merge_request_pipelines.first } + let(:build) { create(:ci_build, ref: merge_request.source_branch, tag: false, pipeline: pipeline, project: project) } + + context 'when ref is protected' do + before do + create(:protected_branch, :developers_can_merge, name: merge_request.source_branch, project: project) + end + + it 'does not return protected variables as it is not supported for merge request pipelines' do + is_expected.not_to include(variable) + end + end + + context 'when ref is not protected' do + it { is_expected.not_to include(variable) } + end + end + end + describe '#scoped_variables_hash' do context 'when overriding CI variables' do before do diff --git a/spec/models/ci/build_trace_chunk_spec.rb b/spec/models/ci/build_trace_chunk_spec.rb index d214fdf369a..59db347582b 100644 --- a/spec/models/ci/build_trace_chunk_spec.rb +++ b/spec/models/ci/build_trace_chunk_spec.rb @@ -171,7 +171,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end shared_examples_for 'Scheduling sidekiq worker to flush data to persist store' do - context 'when new data fullfilled chunk size' do + context 'when new data fulfilled chunk size' do let(:new_data) { 'a' * described_class::CHUNK_SIZE } it 'schedules trace chunk flush worker' do @@ -193,7 +193,7 @@ describe Ci::BuildTraceChunk, :clean_gitlab_redis_shared_state do end shared_examples_for 'Scheduling no sidekiq worker' do - context 'when new data fullfilled chunk size' do + context 'when new data fulfilled chunk size' do let(:new_data) { 'a' * described_class::CHUNK_SIZE } it 'does not schedule trace chunk flush worker' do diff --git a/spec/models/ci/group_variable_spec.rb b/spec/models/ci/group_variable_spec.rb index 1b10501701c..21d96bf3454 100644 --- a/spec/models/ci/group_variable_spec.rb +++ b/spec/models/ci/group_variable_spec.rb @@ -5,6 +5,7 @@ describe Ci::GroupVariable do it { is_expected.to include_module(HasVariable) } it { is_expected.to include_module(Presentable) } + it { is_expected.to include_module(Maskable) } it { is_expected.to validate_uniqueness_of(:key).scoped_to(:group_id).with_message(/\(\w+\) has already been taken/) } describe '.unprotected' do diff --git a/spec/models/ci/pipeline_spec.rb b/spec/models/ci/pipeline_spec.rb index ee400bec04b..d0b42d103a5 100644 --- a/spec/models/ci/pipeline_spec.rb +++ b/spec/models/ci/pipeline_spec.rb @@ -80,11 +80,11 @@ describe Ci::Pipeline, :mailer do context 'when merge request pipelines exist' do let!(:merge_request_pipeline_1) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) end let!(:merge_request_pipeline_2) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) end let(:merge_request) do @@ -106,11 +106,11 @@ describe Ci::Pipeline, :mailer do let!(:branch_pipeline_2) { create(:ci_pipeline, source: :push) } let!(:merge_request_pipeline_1) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) end let!(:merge_request_pipeline_2) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) end let(:merge_request) do @@ -134,7 +134,7 @@ describe Ci::Pipeline, :mailer do subject { described_class.detached_merge_request_pipelines(merge_request) } let!(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request, target_sha: target_sha) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha) end let(:merge_request) { create(:merge_request) } @@ -157,7 +157,7 @@ describe Ci::Pipeline, :mailer do subject { pipeline.detached_merge_request_pipeline? } let!(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request, target_sha: target_sha) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha) end let(:merge_request) { create(:merge_request) } @@ -176,7 +176,7 @@ describe Ci::Pipeline, :mailer do subject { described_class.merge_request_pipelines(merge_request) } let!(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request, target_sha: target_sha) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha) end let(:merge_request) { create(:merge_request) } @@ -199,7 +199,7 @@ describe Ci::Pipeline, :mailer do subject { pipeline.merge_request_pipeline? } let!(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request, target_sha: target_sha) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha) end let(:merge_request) { create(:merge_request) } @@ -218,7 +218,7 @@ describe Ci::Pipeline, :mailer do subject { described_class.mergeable_merge_request_pipelines(merge_request) } let!(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request, target_sha: target_sha) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha) end let(:merge_request) { create(:merge_request) } @@ -241,7 +241,7 @@ describe Ci::Pipeline, :mailer do subject { pipeline.mergeable_merge_request_pipeline? } let!(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request, target_sha: target_sha) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request, target_sha: target_sha) end let(:merge_request) { create(:merge_request) } @@ -256,11 +256,11 @@ describe Ci::Pipeline, :mailer do end end - describe '.merge_request' do - subject { described_class.merge_request } + describe '.merge_request_event' do + subject { described_class.merge_request_event } context 'when there is a merge request pipeline' do - let!(:pipeline) { create(:ci_pipeline, source: :merge_request, merge_request: merge_request) } + let!(:pipeline) { create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) } let(:merge_request) { create(:merge_request) } it 'returns merge request pipeline first' do @@ -281,7 +281,7 @@ describe Ci::Pipeline, :mailer do let(:pipeline) { build(:ci_pipeline, source: source, merge_request: merge_request) } context 'when source is merge request' do - let(:source) { :merge_request } + let(:source) { :merge_request_event } context 'when merge request is specified' do let(:merge_request) { create(:merge_request, source_project: project, source_branch: 'feature', target_project: project, target_branch: 'master') } @@ -505,7 +505,7 @@ describe Ci::Pipeline, :mailer do context 'when source is merge request' do let(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) end let(:merge_request) do @@ -513,9 +513,16 @@ describe Ci::Pipeline, :mailer do source_project: project, source_branch: 'feature', target_project: project, - target_branch: 'master') + target_branch: 'master', + assignee: assignee, + milestone: milestone, + labels: labels) end + let(:assignee) { create(:user) } + let(:milestone) { create(:milestone, project: project) } + let(:labels) { create_list(:label, 2) } + it 'exposes merge request pipeline variables' do expect(subject.to_hash) .to include( @@ -531,7 +538,11 @@ describe Ci::Pipeline, :mailer do 'CI_MERGE_REQUEST_SOURCE_PROJECT_PATH' => merge_request.source_project.full_path, 'CI_MERGE_REQUEST_SOURCE_PROJECT_URL' => merge_request.source_project.web_url, 'CI_MERGE_REQUEST_SOURCE_BRANCH_NAME' => merge_request.source_branch.to_s, - 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => pipeline.source_sha.to_s) + 'CI_MERGE_REQUEST_SOURCE_BRANCH_SHA' => pipeline.source_sha.to_s, + 'CI_MERGE_REQUEST_TITLE' => merge_request.title, + 'CI_MERGE_REQUEST_ASSIGNEES' => assignee.username, + 'CI_MERGE_REQUEST_MILESTONE' => milestone.title, + 'CI_MERGE_REQUEST_LABELS' => labels.map(&:title).join(',')) end context 'when source project does not exist' do @@ -547,6 +558,30 @@ describe Ci::Pipeline, :mailer do CI_MERGE_REQUEST_SOURCE_BRANCH_NAME]) end end + + context 'without assignee' do + let(:assignee) { nil } + + it 'does not expose assignee variable' do + expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_ASSIGNEES') + end + end + + context 'without milestone' do + let(:milestone) { nil } + + it 'does not expose milestone variable' do + expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_MILESTONE') + end + end + + context 'without labels' do + let(:labels) { [] } + + it 'does not expose labels variable' do + expect(subject.to_hash.keys).not_to include('CI_MERGE_REQUEST_LABELS') + end + end end end @@ -1097,7 +1132,7 @@ describe Ci::Pipeline, :mailer do context 'when source is merge request' do let(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) end let(:merge_request) do @@ -1147,7 +1182,7 @@ describe Ci::Pipeline, :mailer do context 'when ref is merge request' do let(:pipeline) do create(:ci_pipeline, - source: :merge_request, + source: :merge_request_event, merge_request: merge_request) end @@ -1310,7 +1345,7 @@ describe Ci::Pipeline, :mailer do context 'when source is merge request' do let(:pipeline) do - create(:ci_pipeline, source: :merge_request, merge_request: merge_request) + create(:ci_pipeline, source: :merge_request_event, merge_request: merge_request) end let(:merge_request) do @@ -2217,7 +2252,7 @@ describe Ci::Pipeline, :mailer do end end - describe "#merge_requests" do + describe "#merge_requests_as_head_pipeline" do let(:project) { create(:project) } let(:pipeline) { create(:ci_empty_pipeline, status: 'created', project: project, ref: 'master', sha: 'a288a022a53a5a944fae87bcec6efc87b7061808') } @@ -2225,20 +2260,20 @@ describe Ci::Pipeline, :mailer do allow_any_instance_of(MergeRequest).to receive(:diff_head_sha) { 'a288a022a53a5a944fae87bcec6efc87b7061808' } merge_request = create(:merge_request, source_project: project, head_pipeline: pipeline, source_branch: pipeline.ref) - expect(pipeline.merge_requests).to eq([merge_request]) + expect(pipeline.merge_requests_as_head_pipeline).to eq([merge_request]) end it "doesn't return merge requests whose source branch doesn't match the pipeline's ref" do create(:merge_request, source_project: project, source_branch: 'feature', target_branch: 'master') - expect(pipeline.merge_requests).to be_empty + expect(pipeline.merge_requests_as_head_pipeline).to be_empty end it "doesn't return merge requests whose `diff_head_sha` doesn't match the pipeline's SHA" do create(:merge_request, source_project: project, source_branch: pipeline.ref) allow_any_instance_of(MergeRequest).to receive(:diff_head_sha) { '97de212e80737a608d939f648d959671fb0a0142b' } - expect(pipeline.merge_requests).to be_empty + expect(pipeline.merge_requests_as_head_pipeline).to be_empty end end @@ -2266,7 +2301,7 @@ describe Ci::Pipeline, :mailer do let!(:pipeline) do create(:ci_pipeline, - source: :merge_request, + source: :merge_request_event, project: pipeline_project, ref: source_branch, merge_request: merge_request) @@ -2289,7 +2324,7 @@ describe Ci::Pipeline, :mailer do let!(:pipeline_2) do create(:ci_pipeline, - source: :merge_request, + source: :merge_request_event, project: pipeline_project, ref: source_branch, merge_request: merge_request_2) diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 875e8b2b682..02c07a2bd83 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -6,6 +6,7 @@ describe Ci::Variable do describe 'validations' do it { is_expected.to include_module(HasVariable) } it { is_expected.to include_module(Presentable) } + it { is_expected.to include_module(Maskable) } it { is_expected.to validate_uniqueness_of(:key).scoped_to(:project_id, :environment_scope).with_message(/\(\w+\) has already been taken/) } end diff --git a/spec/models/clusters/applications/jupyter_spec.rb b/spec/models/clusters/applications/jupyter_spec.rb index 2c22c24c498..6e58f3ad699 100644 --- a/spec/models/clusters/applications/jupyter_spec.rb +++ b/spec/models/clusters/applications/jupyter_spec.rb @@ -39,7 +39,7 @@ describe Clusters::Applications::Jupyter do it 'should be initialized with 4 arguments' do expect(subject.name).to eq('jupyter') expect(subject.chart).to eq('jupyter/jupyterhub') - expect(subject.version).to eq('v0.6') + expect(subject.version).to eq('0.9-174bbd5') expect(subject).to be_rbac expect(subject.repository).to eq('https://jupyterhub.github.io/helm-chart/') expect(subject.files).to eq(jupyter.files) @@ -57,7 +57,7 @@ describe Clusters::Applications::Jupyter do let(:jupyter) { create(:clusters_applications_jupyter, :errored, version: '0.0.1') } it 'should be initialized with the locked version' do - expect(subject.version).to eq('v0.6') + expect(subject.version).to eq('0.9-174bbd5') end end end @@ -77,6 +77,7 @@ describe Clusters::Applications::Jupyter do expect(values).to include('singleuser') expect(values).to match(/clientId: '?#{application.oauth_application.uid}/) expect(values).to match(/callbackUrl: '?#{application.callback_url}/) + expect(values).to include("gitlabProjectIdWhitelist:\n - #{application.cluster.project.id}") end context 'when cluster belongs to a project' do diff --git a/spec/models/clusters/applications/knative_spec.rb b/spec/models/clusters/applications/knative_spec.rb index 006b922ab27..4884a5927fb 100644 --- a/spec/models/clusters/applications/knative_spec.rb +++ b/spec/models/clusters/applications/knative_spec.rb @@ -66,9 +66,7 @@ describe Clusters::Applications::Knative do end end - describe '#install_command' do - subject { knative.install_command } - + shared_examples 'a command' do it 'should be an instance of Helm::InstallCommand' do expect(subject).to be_an_instance_of(Gitlab::Kubernetes::Helm::InstallCommand) end @@ -76,7 +74,6 @@ describe Clusters::Applications::Knative do it 'should be initialized with knative arguments' do expect(subject.name).to eq('knative') expect(subject.chart).to eq('knative/knative') - expect(subject.version).to eq('0.2.2') expect(subject.files).to eq(knative.files) end @@ -98,6 +95,27 @@ describe Clusters::Applications::Knative do end end + describe '#install_command' do + subject { knative.install_command } + + it 'should be initialized with latest version' do + expect(subject.version).to eq('0.2.2') + end + + it_behaves_like 'a command' + end + + describe '#update_command' do + let!(:current_installed_version) { knative.version = '0.1.0' } + subject { knative.update_command } + + it 'should be initialized with current version' do + expect(subject.version).to eq(current_installed_version) + end + + it_behaves_like 'a command' + end + describe '#files' do let(:application) { knative } let(:values) { subject[:'values.yaml'] } diff --git a/spec/models/clusters/platforms/kubernetes_spec.rb b/spec/models/clusters/platforms/kubernetes_spec.rb index 4068d98d8f7..3b32ca8df05 100644 --- a/spec/models/clusters/platforms/kubernetes_spec.rb +++ b/spec/models/clusters/platforms/kubernetes_spec.rb @@ -98,6 +98,22 @@ describe Clusters::Platforms::Kubernetes, :use_clean_rails_memory_store_caching it { expect(kubernetes.save).to be_truthy } end + + context 'when api_url is localhost' do + let(:api_url) { 'http://localhost:22' } + + it { expect(kubernetes.save).to be_falsey } + + context 'Application settings allows local requests' do + before do + allow(ApplicationSetting) + .to receive(:current) + .and_return(ApplicationSetting.build_from_defaults(allow_local_requests_from_hooks_and_services: true)) + end + + it { expect(kubernetes.save).to be_truthy } + end + end end context 'when validates token' do diff --git a/spec/models/concerns/has_ref_spec.rb b/spec/models/concerns/has_ref_spec.rb index 8aed72d77a4..8aa2fecb18c 100644 --- a/spec/models/concerns/has_ref_spec.rb +++ b/spec/models/concerns/has_ref_spec.rb @@ -16,6 +16,16 @@ describe HasRef do it 'return true when tag is set to false' do is_expected.to be_truthy end + + context 'when it was triggered by merge request' do + let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) } + let(:pipeline) { merge_request.merge_request_pipelines.first } + let(:build) { create(:ci_build, pipeline: pipeline) } + + it 'returns false' do + is_expected.to be_falsy + end + end end context 'is not a tag' do @@ -55,5 +65,15 @@ describe HasRef do is_expected.to start_with(Gitlab::Git::BRANCH_REF_PREFIX) end end + + context 'when it is triggered by a merge request' do + let(:merge_request) { create(:merge_request, :with_merge_request_pipeline) } + let(:pipeline) { merge_request.merge_request_pipelines.first } + let(:build) { create(:ci_build, tag: false, pipeline: pipeline) } + + it 'returns nil' do + is_expected.to be_nil + end + end end end diff --git a/spec/models/concerns/has_variable_spec.rb b/spec/models/concerns/has_variable_spec.rb index 3fbe86c5b56..bff96e12ffa 100644 --- a/spec/models/concerns/has_variable_spec.rb +++ b/spec/models/concerns/has_variable_spec.rb @@ -57,7 +57,7 @@ describe HasVariable do describe '#to_runner_variable' do it 'returns a hash for the runner' do expect(subject.to_runner_variable) - .to eq(key: subject.key, value: subject.value, public: false) + .to include(key: subject.key, value: subject.value, public: false) end end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 41159348e04..72c6161424b 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -32,17 +32,56 @@ describe Issuable do end describe "Validation" do - subject { build(:issue) } + context 'general validations' do + subject { build(:issue) } - before do - allow(InternalId).to receive(:generate_next).and_return(nil) + before do + allow(InternalId).to receive(:generate_next).and_return(nil) + end + + it { is_expected.to validate_presence_of(:project) } + it { is_expected.to validate_presence_of(:iid) } + it { is_expected.to validate_presence_of(:author) } + it { is_expected.to validate_presence_of(:title) } + it { is_expected.to validate_length_of(:title).is_at_most(255) } end - it { is_expected.to validate_presence_of(:project) } - it { is_expected.to validate_presence_of(:iid) } - it { is_expected.to validate_presence_of(:author) } - it { is_expected.to validate_presence_of(:title) } - it { is_expected.to validate_length_of(:title).is_at_most(255) } + describe 'milestone' do + let(:project) { create(:project) } + let(:milestone_id) { create(:milestone, project: project).id } + let(:params) do + { + title: 'something', + project: project, + author: build(:user), + milestone_id: milestone_id + } + end + + subject { issuable_class.new(params) } + + context 'with correct params' do + it { is_expected.to be_valid } + end + + context 'with empty string milestone' do + let(:milestone_id) { '' } + + it { is_expected.to be_valid } + end + + context 'with nil milestone id' do + let(:milestone_id) { nil } + + it { is_expected.to be_valid } + end + + context 'with a milestone id from another project' do + let(:milestone_id) { create(:milestone).id } + + it { is_expected.to be_invalid } + end + end end describe "Scope" do @@ -66,6 +105,48 @@ describe Issuable do end end + describe '#milestone_available?' do + let(:group) { create(:group) } + let(:project) { create(:project, group: group) } + let(:issue) { create(:issue, project: project) } + + def build_issuable(milestone_id) + issuable_class.new(project: project, milestone_id: milestone_id) + end + + it 'returns true with a milestone from the issue project' do + milestone = create(:milestone, project: project) + + expect(build_issuable(milestone.id).milestone_available?).to be_truthy + end + + it 'returns true with a milestone from the issue project group' do + milestone = create(:milestone, group: group) + + expect(build_issuable(milestone.id).milestone_available?).to be_truthy + end + + it 'returns true with a milestone from the the parent of the issue project group', :nested_groups do + parent = create(:group) + group.update(parent: parent) + milestone = create(:milestone, group: parent) + + expect(build_issuable(milestone.id).milestone_available?).to be_truthy + end + + it 'returns false with a milestone from another project' do + milestone = create(:milestone) + + expect(build_issuable(milestone.id).milestone_available?).to be_falsey + end + + it 'returns false with a milestone from another group' do + milestone = create(:milestone, group: create(:group)) + + expect(build_issuable(milestone.id).milestone_available?).to be_falsey + end + end + describe ".search" do let!(:searchable_issue) { create(:issue, title: "Searchable awesome issue") } let!(:searchable_issue2) { create(:issue, title: 'Aw') } diff --git a/spec/models/concerns/maskable_spec.rb b/spec/models/concerns/maskable_spec.rb new file mode 100644 index 00000000000..aeba7ad862f --- /dev/null +++ b/spec/models/concerns/maskable_spec.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'spec_helper' + +describe Maskable do + let(:variable) { build(:ci_variable) } + + describe 'masked value validations' do + subject { variable } + + context 'when variable is masked' do + before do + subject.masked = true + end + + it { is_expected.not_to allow_value('hello').for(:value) } + it { is_expected.not_to allow_value('hello world').for(:value) } + it { is_expected.not_to allow_value('hello$VARIABLEworld').for(:value) } + it { is_expected.not_to allow_value('hello\rworld').for(:value) } + it { is_expected.to allow_value('helloworld').for(:value) } + end + + context 'when variable is not masked' do + before do + subject.masked = false + end + + it { is_expected.to allow_value('hello').for(:value) } + it { is_expected.to allow_value('hello world').for(:value) } + it { is_expected.to allow_value('hello$VARIABLEworld').for(:value) } + it { is_expected.to allow_value('hello\rworld').for(:value) } + it { is_expected.to allow_value('helloworld').for(:value) } + end + end + + describe 'REGEX' do + subject { Maskable::REGEX } + + it 'does not match strings shorter than 8 letters' do + expect(subject.match?('hello')).to eq(false) + end + + it 'does not match strings with spaces' do + expect(subject.match?('hello world')).to eq(false) + end + + it 'does not match strings with shell variables' do + expect(subject.match?('hello$VARIABLEworld')).to eq(false) + end + + it 'does not match strings with escape characters' do + expect(subject.match?('hello\rworld')).to eq(false) + end + + it 'does not match strings that span more than one line' do + string = <<~EOS + hello + world + EOS + + expect(subject.match?(string)).to eq(false) + end + + it 'matches valid strings' do + expect(subject.match?('helloworld')).to eq(true) + end + end + + describe '#to_runner_variable' do + subject { variable.to_runner_variable } + + it 'exposes the masked attribute' do + expect(subject).to include(:masked) + end + end +end diff --git a/spec/models/concerns/milestoneish_spec.rb b/spec/models/concerns/milestoneish_spec.rb index 87bf731340f..81ca5b638fe 100644 --- a/spec/models/concerns/milestoneish_spec.rb +++ b/spec/models/concerns/milestoneish_spec.rb @@ -9,8 +9,10 @@ describe Milestone, 'Milestoneish' do let(:admin) { create(:admin) } let(:project) { create(:project, :public) } let(:milestone) { create(:milestone, project: project) } - let!(:issue) { create(:issue, project: project, milestone: milestone) } - let!(:security_issue_1) { create(:issue, :confidential, project: project, author: author, milestone: milestone) } + let(:label1) { create(:label, project: project) } + let(:label2) { create(:label, project: project) } + let!(:issue) { create(:issue, project: project, milestone: milestone, assignees: [member], labels: [label1]) } + let!(:security_issue_1) { create(:issue, :confidential, project: project, author: author, milestone: milestone, labels: [label2]) } let!(:security_issue_2) { create(:issue, :confidential, project: project, assignees: [assignee], milestone: milestone) } let!(:closed_issue_1) { create(:issue, :closed, project: project, milestone: milestone) } let!(:closed_issue_2) { create(:issue, :closed, project: project, milestone: milestone) } @@ -42,13 +44,102 @@ describe Milestone, 'Milestoneish' do end end + context 'attributes visibility' do + using RSpec::Parameterized::TableSyntax + + let(:users) do + { + anonymous: nil, + non_member: non_member, + guest: guest, + member: member, + assignee: assignee + } + end + + let(:project_visibility_levels) do + { + public: Gitlab::VisibilityLevel::PUBLIC, + internal: Gitlab::VisibilityLevel::INTERNAL, + private: Gitlab::VisibilityLevel::PRIVATE + } + end + + describe '#issue_participants_visible_by_user' do + where(:visibility, :user_role, :result) do + :public | nil | [:member] + :public | :non_member | [:member] + :public | :guest | [:member] + :public | :member | [:member, :assignee] + :internal | nil | [] + :internal | :non_member | [:member] + :internal | :guest | [:member] + :internal | :member | [:member, :assignee] + :private | nil | [] + :private | :non_member | [] + :private | :guest | [:member] + :private | :member | [:member, :assignee] + end + + with_them do + before do + project.update(visibility_level: project_visibility_levels[visibility]) + end + + it 'returns the proper participants' do + user = users[user_role] + participants = result.map { |role| users[role] } + + expect(milestone.issue_participants_visible_by_user(user)).to match_array(participants) + end + end + end + + describe '#issue_labels_visible_by_user' do + let(:labels) do + { + label1: label1, + label2: label2 + } + end + + where(:visibility, :user_role, :result) do + :public | nil | [:label1] + :public | :non_member | [:label1] + :public | :guest | [:label1] + :public | :member | [:label1, :label2] + :internal | nil | [] + :internal | :non_member | [:label1] + :internal | :guest | [:label1] + :internal | :member | [:label1, :label2] + :private | nil | [] + :private | :non_member | [] + :private | :guest | [:label1] + :private | :member | [:label1, :label2] + end + + with_them do + before do + project.update(visibility_level: project_visibility_levels[visibility]) + end + + it 'returns the proper participants' do + user = users[user_role] + expected_labels = result.map { |label| labels[label] } + + expect(milestone.issue_labels_visible_by_user(user)).to match_array(expected_labels) + end + end + end + end + describe '#sorted_merge_requests' do it 'sorts merge requests by label priority' do merge_request_1 = create(:labeled_merge_request, labels: [label_2], source_project: project, source_branch: 'branch_1', milestone: milestone) merge_request_2 = create(:labeled_merge_request, labels: [label_1], source_project: project, source_branch: 'branch_2', milestone: milestone) merge_request_3 = create(:labeled_merge_request, labels: [label_3], source_project: project, source_branch: 'branch_3', milestone: milestone) - merge_requests = milestone.sorted_merge_requests + merge_requests = milestone.sorted_merge_requests(member) expect(merge_requests.first).to eq(merge_request_2) expect(merge_requests.second).to eq(merge_request_1) @@ -56,6 +147,51 @@ describe Milestone, 'Milestoneish' do end end + describe '#merge_requests_visible_to_user' do + let(:merge_request) { create(:merge_request, source_project: project, milestone: milestone) } + + context 'when project is private' do + before do + project.update(visibility_level: Gitlab::VisibilityLevel::PRIVATE) + end + + it 'does not return any merge request for a non member' do + merge_requests = milestone.merge_requests_visible_to_user(non_member) + expect(merge_requests).to be_empty + end + + it 'returns milestone merge requests for a member' do + merge_requests = milestone.merge_requests_visible_to_user(member) + expect(merge_requests).to contain_exactly(merge_request) + end + end + + context 'when project is public' do + context 'when merge requests are available to anyone' do + it 'returns milestone merge requests for a non member' do + merge_requests = milestone.merge_requests_visible_to_user(non_member) + expect(merge_requests).to contain_exactly(merge_request) + end + end + + context 'when merge requests are available to project members' do + before do + project.project_feature.update(merge_requests_access_level: ProjectFeature::PRIVATE) + end + + it 'does not return any merge request for a non member' do + merge_requests = milestone.merge_requests_visible_to_user(non_member) + expect(merge_requests).to be_empty + end + + it 'returns milestone merge requests for a member' do + merge_requests = milestone.merge_requests_visible_to_user(member) + expect(merge_requests).to contain_exactly(merge_request) + end + end + end + end + describe '#closed_items_count' do it 'does not count confidential issues for non project members' do expect(milestone.closed_items_count(non_member)).to eq 2 diff --git a/spec/models/concerns/sortable_spec.rb b/spec/models/concerns/sortable_spec.rb index 39c16ae60af..0a9d2021a19 100644 --- a/spec/models/concerns/sortable_spec.rb +++ b/spec/models/concerns/sortable_spec.rb @@ -99,7 +99,7 @@ describe Sortable do expect(ordered_group_names('id_desc')).to eq(%w(bbb BB AAA aa)) end - it 'sorts groups by name via case-insentitive comparision' do + it 'sorts groups by name via case-insensitive comparision' do expect(ordered_group_names('name_asc')).to eq(%w(aa AAA BB bbb)) expect(ordered_group_names('name_desc')).to eq(%w(bbb BB AAA aa)) end diff --git a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb index 6605f1f5a5f..2a0182b4294 100644 --- a/spec/models/concerns/token_authenticatable_strategies/base_spec.rb +++ b/spec/models/concerns/token_authenticatable_strategies/base_spec.rb @@ -15,7 +15,7 @@ describe TokenAuthenticatableStrategies::Base do context 'when encrypted strategy is specified' do it 'fabricates encrypted strategy object' do - strategy = described_class.fabricate(instance, field, encrypted: true) + strategy = described_class.fabricate(instance, field, encrypted: :required) expect(strategy).to be_a TokenAuthenticatableStrategies::Encrypted end @@ -23,7 +23,7 @@ describe TokenAuthenticatableStrategies::Base do context 'when no strategy is specified' do it 'fabricates insecure strategy object' do - strategy = described_class.fabricate(instance, field, something: true) + strategy = described_class.fabricate(instance, field, something: :required) expect(strategy).to be_a TokenAuthenticatableStrategies::Insecure end @@ -31,35 +31,9 @@ describe TokenAuthenticatableStrategies::Base do context 'when incompatible options are provided' do it 'raises an error' do - expect { described_class.fabricate(instance, field, digest: true, encrypted: true) } + expect { described_class.fabricate(instance, field, digest: true, encrypted: :required) } .to raise_error ArgumentError end end end - - describe '#fallback?' do - context 'when fallback is set' do - it 'recognizes fallback setting' do - strategy = described_class.new(instance, field, fallback: true) - - expect(strategy.fallback?).to be true - end - end - - context 'when fallback is not a valid value' do - it 'raises an error' do - strategy = described_class.new(instance, field, fallback: 'something') - - expect { strategy.fallback? }.to raise_error ArgumentError - end - end - - context 'when fallback is not set' do - it 'raises an error' do - strategy = described_class.new(instance, field, {}) - - expect(strategy.fallback?).to eq false - end - end - end end diff --git a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb index 93cab80cb1f..ca38f86c5ab 100644 --- a/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb +++ b/spec/models/concerns/token_authenticatable_strategies/encrypted_spec.rb @@ -12,19 +12,9 @@ describe TokenAuthenticatableStrategies::Encrypted do described_class.new(model, 'some_field', options) end - describe '.new' do - context 'when fallback and migration strategies are set' do - let(:options) { { fallback: true, migrating: true } } - - it 'raises an error' do - expect { subject }.to raise_error ArgumentError, /not compatible/ - end - end - end - describe '#find_token_authenticatable' do - context 'when using fallback strategy' do - let(:options) { { fallback: true } } + context 'when using optional strategy' do + let(:options) { { encrypted: :optional } } it 'finds the encrypted resource by cleartext' do allow(model).to receive(:find_by) @@ -50,7 +40,7 @@ describe TokenAuthenticatableStrategies::Encrypted do end context 'when using migration strategy' do - let(:options) { { migrating: true } } + let(:options) { { encrypted: :migrating } } it 'finds the cleartext resource by cleartext' do allow(model).to receive(:find_by) @@ -73,8 +63,8 @@ describe TokenAuthenticatableStrategies::Encrypted do end describe '#get_token' do - context 'when using fallback strategy' do - let(:options) { { fallback: true } } + context 'when using optional strategy' do + let(:options) { { encrypted: :optional } } it 'returns decrypted token when an encrypted token is present' do allow(instance).to receive(:read_attribute) @@ -98,7 +88,7 @@ describe TokenAuthenticatableStrategies::Encrypted do end context 'when using migration strategy' do - let(:options) { { migrating: true } } + let(:options) { { encrypted: :migrating } } it 'returns cleartext token when an encrypted token is present' do allow(instance).to receive(:read_attribute) @@ -127,8 +117,8 @@ describe TokenAuthenticatableStrategies::Encrypted do end describe '#set_token' do - context 'when using fallback strategy' do - let(:options) { { fallback: true } } + context 'when using optional strategy' do + let(:options) { { encrypted: :optional } } it 'writes encrypted token and removes plaintext token and returns it' do expect(instance).to receive(:[]=) @@ -141,7 +131,7 @@ describe TokenAuthenticatableStrategies::Encrypted do end context 'when using migration strategy' do - let(:options) { { migrating: true } } + let(:options) { { encrypted: :migrating } } it 'writes encrypted token and writes plaintext token' do expect(instance).to receive(:[]=) diff --git a/spec/models/error_tracking/project_error_tracking_setting_spec.rb b/spec/models/error_tracking/project_error_tracking_setting_spec.rb index 076ccc96041..cbde13a2c7a 100644 --- a/spec/models/error_tracking/project_error_tracking_setting_spec.rb +++ b/spec/models/error_tracking/project_error_tracking_setting_spec.rb @@ -62,11 +62,32 @@ describe ErrorTracking::ProjectErrorTrackingSetting do end context 'URL path' do - it 'fails validation with wrong path' do + it 'fails validation without api/0/projects' do subject.api_url = 'http://gitlab.com/project1/something' expect(subject).not_to be_valid - expect(subject.errors.messages[:api_url]).to include('path needs to start with /api/0/projects') + expect(subject.errors.messages[:api_url]).to include('is invalid') + end + + it 'fails validation without org and project slugs' do + subject.api_url = 'http://gitlab.com/api/0/projects/' + + expect(subject).not_to be_valid + expect(subject.errors.messages[:project]).to include('is a required field') + end + + it 'fails validation when api_url has extra parts' do + subject.api_url = 'http://gitlab.com/api/0/projects/org/proj/something' + + expect(subject).not_to be_valid + expect(subject.errors.messages[:api_url]).to include("is invalid") + end + + it 'fails validation when api_url has less parts' do + subject.api_url = 'http://gitlab.com/api/0/projects/org' + + expect(subject).not_to be_valid + expect(subject.errors.messages[:api_url]).to include("is invalid") end it 'passes validation with correct path' do @@ -275,6 +296,16 @@ describe ErrorTracking::ProjectErrorTrackingSetting do expect(api_url).to eq(':::') end + + it 'returns nil when api_host is blank' do + api_url = described_class.build_api_url_from( + api_host: '', + organization_slug: 'org-slug', + project_slug: 'proj-slug' + ) + + expect(api_url).to be_nil + end end describe '#api_host' do diff --git a/spec/models/issue/metrics_spec.rb b/spec/models/issue/metrics_spec.rb index 1bf0ecb98ad..b7291eebe64 100644 --- a/spec/models/issue/metrics_spec.rb +++ b/spec/models/issue/metrics_spec.rb @@ -9,7 +9,7 @@ describe Issue::Metrics do context "milestones" do it "records the first time an issue is associated with a milestone" do time = Time.now - Timecop.freeze(time) { subject.update(milestone: create(:milestone)) } + Timecop.freeze(time) { subject.update(milestone: create(:milestone, project: project)) } metrics = subject.metrics expect(metrics).to be_present @@ -18,9 +18,9 @@ describe Issue::Metrics do it "does not record the second time an issue is associated with a milestone" do time = Time.now - Timecop.freeze(time) { subject.update(milestone: create(:milestone)) } + Timecop.freeze(time) { subject.update(milestone: create(:milestone, project: project)) } Timecop.freeze(time + 2.hours) { subject.update(milestone: nil) } - Timecop.freeze(time + 6.hours) { subject.update(milestone: create(:milestone)) } + Timecop.freeze(time + 6.hours) { subject.update(milestone: create(:milestone, project: project)) } metrics = subject.metrics expect(metrics).to be_present diff --git a/spec/models/merge_request_diff_spec.rb b/spec/models/merge_request_diff_spec.rb index 1849d3bac12..e530e0302f5 100644 --- a/spec/models/merge_request_diff_spec.rb +++ b/spec/models/merge_request_diff_spec.rb @@ -3,6 +3,18 @@ require 'spec_helper' describe MergeRequestDiff do let(:diff_with_commits) { create(:merge_request).merge_request_diff } + describe 'validations' do + subject { diff_with_commits } + + it 'checks sha format of base_commit_sha, head_commit_sha and start_commit_sha' do + subject.base_commit_sha = subject.head_commit_sha = subject.start_commit_sha = 'foobar' + + expect(subject.valid?).to be false + expect(subject.errors.count).to eq 3 + expect(subject.errors).to all(include('is not a valid SHA')) + end + end + describe 'create new record' do subject { diff_with_commits } @@ -78,7 +90,7 @@ describe MergeRequestDiff do it 'returns persisted diffs if cannot compare with diff refs' do expect(diff).to receive(:load_diffs).and_call_original - diff.update!(head_commit_sha: 'invalid-sha') + diff.update!(head_commit_sha: Digest::SHA1.hexdigest(SecureRandom.hex)) diff.diffs.diff_files end diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 82a853a23b9..07cb4c9c1e3 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1333,7 +1333,7 @@ describe MergeRequest do let!(:merge_request_pipeline) do create(:ci_pipeline, - source: :merge_request, + source: :merge_request_event, project: project, ref: source_ref, sha: shas.second, @@ -1372,7 +1372,7 @@ describe MergeRequest do let!(:merge_request_pipeline_2) do create(:ci_pipeline, - source: :merge_request, + source: :merge_request_event, project: project, ref: source_ref, sha: shas.first, @@ -1399,7 +1399,7 @@ describe MergeRequest do let!(:merge_request_pipeline_2) do create(:ci_pipeline, - source: :merge_request, + source: :merge_request_event, project: project, ref: source_ref, sha: shas.first, diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index af7e3d3a6c9..77b7042424c 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -182,7 +182,7 @@ describe Milestone do describe '#total_items_count' do before do create :closed_issue, milestone: milestone, project: project - create :merge_request, milestone: milestone + create :merge_request, milestone: milestone, source_project: project end it 'returns total count of issues and merge requests assigned to milestone' do @@ -192,10 +192,10 @@ describe Milestone do describe '#can_be_closed?' do before do - milestone = create :milestone - create :closed_issue, milestone: milestone + milestone = create :milestone, project: project + create :closed_issue, milestone: milestone, project: project - create :issue + create :issue, project: project end it 'returns true if milestone active and all nested issues closed' do diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 788b3179b01..5428fcb1271 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -177,9 +177,10 @@ describe JiraService do expect(WebMock).to have_requested(:post, @remote_link_url).with( body: hash_including( GlobalID: 'GitLab', + relationship: 'mentioned on', object: { url: "#{Gitlab.config.gitlab.url}/#{project.full_path}/commit/#{commit_id}", - title: "GitLab: Solved by commit #{commit_id}.", + title: "Solved by commit #{commit_id}.", icon: { title: 'GitLab', url16x16: favicon_path }, status: { resolved: true } } diff --git a/spec/models/project_services/prometheus_service_spec.rb b/spec/models/project_services/prometheus_service_spec.rb index b6cf4c72450..e9c7c94ad70 100644 --- a/spec/models/project_services/prometheus_service_spec.rb +++ b/spec/models/project_services/prometheus_service_spec.rb @@ -33,18 +33,38 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do describe 'Validations' do context 'when manual_configuration is enabled' do before do - subject.manual_configuration = true + service.manual_configuration = true end - it { is_expected.to validate_presence_of(:api_url) } + it 'validates presence of api_url' do + expect(service).to validate_presence_of(:api_url) + end end context 'when manual configuration is disabled' do before do - subject.manual_configuration = false + service.manual_configuration = false end - it { is_expected.not_to validate_presence_of(:api_url) } + it 'does not validate presence of api_url' do + expect(service).not_to validate_presence_of(:api_url) + end + end + + context 'when the api_url domain points to localhost or local network' do + let(:domain) { Addressable::URI.parse(service.api_url).hostname } + + it 'cannot query' do + expect(service.can_query?).to be true + + aggregate_failures do + ['127.0.0.1', '192.168.2.3'].each do |url| + allow(Addrinfo).to receive(:getaddrinfo).with(domain, any_args).and_return([Addrinfo.tcp(url, 80)]) + + expect(service.can_query?).to be false + end + end + end end end @@ -74,30 +94,35 @@ describe PrometheusService, :use_clean_rails_memory_store_caching do end describe '#prometheus_client' do + let(:api_url) { 'http://some_url' } + + before do + service.active = true + service.api_url = api_url + service.manual_configuration = manual_configuration + end + context 'manual configuration is enabled' do - let(:api_url) { 'http://some_url' } + let(:manual_configuration) { true } - before do - subject.active = true - subject.manual_configuration = true - subject.api_url = api_url + it 'returns rest client from api_url' do + expect(service.prometheus_client.url).to eq(api_url) end - it 'returns rest client from api_url' do - expect(subject.prometheus_client.url).to eq(api_url) + it 'calls valid?' do + allow(service).to receive(:valid?).and_call_original + + expect(service.prometheus_client).not_to be_nil + + expect(service).to have_received(:valid?) end end context 'manual configuration is disabled' do - let(:api_url) { 'http://some_url' } - - before do - subject.manual_configuration = false - subject.api_url = api_url - end + let(:manual_configuration) { false } it 'no client provided' do - expect(subject.prometheus_client).to be_nil + expect(service.prometheus_client).to be_nil end end end diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index 9fb0d04ca9e..b2392f9521f 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -3430,28 +3430,42 @@ describe Project do project.migrate_to_hashed_storage! end - it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the project repo is in use' do + it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the project repo is in use' do Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: false)).increase - expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in) + expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in) project.migrate_to_hashed_storage! end - it 'schedules ProjectMigrateHashedStorageWorker with delayed start when the wiki repo is in use' do + it 'schedules HashedStorage::ProjectMigrateWorker with delayed start when the wiki repo is in use' do Gitlab::ReferenceCounter.new(project.gl_repository(is_wiki: true)).increase - expect(ProjectMigrateHashedStorageWorker).to receive(:perform_in) + expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_in) project.migrate_to_hashed_storage! end - it 'schedules ProjectMigrateHashedStorageWorker' do - expect(ProjectMigrateHashedStorageWorker).to receive(:perform_async).with(project.id) + it 'schedules HashedStorage::ProjectMigrateWorker' do + expect(HashedStorage::ProjectMigrateWorker).to receive(:perform_async).with(project.id) project.migrate_to_hashed_storage! end end + + describe '#rollback_to_legacy_storage!' do + let(:project) { create(:project, :empty_repo, :legacy_storage) } + + it 'returns nil' do + expect(project.rollback_to_legacy_storage!).to be_nil + end + + it 'does not run validations' do + expect(project).not_to receive(:valid?) + + project.rollback_to_legacy_storage! + end + end end context 'hashed storage' do @@ -3527,11 +3541,35 @@ describe Project do project = create(:project, storage_version: 1, skip_disk_validation: true) Sidekiq::Testing.fake! do - expect { project.migrate_to_hashed_storage! }.to change(ProjectMigrateHashedStorageWorker.jobs, :size).by(1) + expect { project.migrate_to_hashed_storage! }.to change(HashedStorage::ProjectMigrateWorker.jobs, :size).by(1) end end end end + + describe '#rollback_to_legacy_storage!' do + let(:project) { create(:project, :repository, skip_disk_validation: true) } + + it 'returns true' do + expect(project.rollback_to_legacy_storage!).to be_truthy + end + + it 'does not run validations' do + expect(project).not_to receive(:valid?) + + project.rollback_to_legacy_storage! + end + + it 'does not flag as read-only' do + expect { project.rollback_to_legacy_storage! }.not_to change { project.repository_read_only } + end + + it 'enqueues a job' do + Sidekiq::Testing.fake! do + expect { project.rollback_to_legacy_storage! }.to change(HashedStorage::ProjectRollbackWorker.jobs, :size).by(1) + end + end + end end describe '#gl_repository' do diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 4c677200ae2..dafe7646366 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -190,4 +190,32 @@ describe ProtectedBranch do end end end + + describe '#any_protected?' do + context 'existing project' do + let(:project) { create(:project, :repository) } + + it 'returns true when any of the branch names match a protected branch via direct match' do + create(:protected_branch, project: project, name: 'foo') + + expect(described_class.any_protected?(project, ['foo', 'production/some-branch'])).to eq(true) + end + + it 'returns true when any of the branch matches a protected branch via wildcard match' do + create(:protected_branch, project: project, name: 'production/*') + + expect(described_class.any_protected?(project, ['foo', 'production/some-branch'])).to eq(true) + end + + it 'returns false when none of branches does not match a protected branch via direct match' do + expect(described_class.any_protected?(project, ['foo'])).to eq(false) + end + + it 'returns false when none of the branches does not match a protected branch via wildcard match' do + create(:protected_branch, project: project, name: 'production/*') + + expect(described_class.any_protected?(project, ['staging/some-branch'])).to eq(false) + end + end + end end diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index 17201d8b90a..70630467d24 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -2237,7 +2237,7 @@ describe Repository do rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target.id) end - describe '#ancestor?' do + shared_examples '#ancestor?' do let(:commit) { repository.commit } let(:ancestor) { commit.parents.first } @@ -2261,6 +2261,20 @@ describe Repository do end end + describe '#ancestor? with Gitaly enabled' do + it_behaves_like "#ancestor?" + end + + describe '#ancestor? with Rugged enabled', :enable_rugged do + it 'calls out to the Rugged implementation' do + allow_any_instance_of(Rugged).to receive(:merge_base).with(repository.commit.id, Gitlab::Git::BLANK_SHA).and_call_original + + repository.ancestor?(repository.commit.id, Gitlab::Git::BLANK_SHA) + end + + it_behaves_like '#ancestor?' + end + describe '#archive_metadata' do let(:ref) { 'master' } let(:storage_path) { '/tmp' } |