Welcome to mirror list, hosted at ThFree Co, Russian Federation.

gitlab.com/gitlab-org/gitlab-foss.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
Diffstat (limited to 'spec')
-rw-r--r--spec/config/mail_room_spec.rb2
-rw-r--r--spec/controllers/admin/users_controller_spec.rb76
-rw-r--r--spec/controllers/health_check_controller_spec.rb105
-rw-r--r--spec/controllers/projects/group_links_controller_spec.rb2
-rw-r--r--spec/controllers/projects/notification_settings_controller_spec.rb14
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb2
-rw-r--r--spec/controllers/projects_controller_spec.rb34
-rw-r--r--spec/controllers/registrations_controller_spec.rb33
-rw-r--r--spec/controllers/users_controller_spec.rb22
-rw-r--r--spec/factories/notes.rb4
-rw-r--r--spec/factories/todos.rb4
-rw-r--r--spec/factories_spec.rb2
-rw-r--r--spec/features/admin/admin_builds_spec.rb1
-rw-r--r--spec/features/admin/admin_health_check_spec.rb55
-rw-r--r--spec/features/admin/admin_users_spec.rb3
-rw-r--r--spec/features/builds_spec.rb1
-rw-r--r--spec/features/container_registry_spec.rb44
-rw-r--r--spec/features/issues_spec.rb97
-rw-r--r--spec/features/markdown_spec.rb4
-rw-r--r--spec/features/merge_requests/user_lists_merge_requests_spec.rb15
-rw-r--r--spec/features/notes_on_merge_requests_spec.rb2
-rw-r--r--spec/features/pipelines_spec.rb159
-rw-r--r--spec/features/projects/files/gitignore_dropdown_spec.rb30
-rw-r--r--spec/features/projects/files/project_owner_creates_license_file_spec.rb8
-rw-r--r--spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb4
-rw-r--r--spec/features/runners_spec.rb33
-rw-r--r--spec/features/signup_spec.rb45
-rw-r--r--spec/features/variables_spec.rb61
-rw-r--r--spec/finders/issues_finder_spec.rb180
-rw-r--r--spec/fixtures/container_registry/config_blob.json1
-rw-r--r--spec/fixtures/container_registry/tag_manifest.json1
-rw-r--r--spec/fixtures/markdown.md.erb23
-rw-r--r--spec/helpers/auth_helper_spec.rb47
-rw-r--r--spec/helpers/diff_helper_spec.rb4
-rw-r--r--spec/helpers/events_helper_spec.rb95
-rw-r--r--spec/javascripts/project_title_spec.js.coffee1
-rw-r--r--spec/lib/banzai/filter/inline_diff_filter_spec.rb68
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb151
-rw-r--r--spec/lib/banzai/filter/wiki_link_filter_spec.rb85
-rw-r--r--spec/lib/ci/ansi2html_spec.rb111
-rw-r--r--spec/lib/ci/charts_spec.rb7
-rw-r--r--spec/lib/ci/gitlab_ci_yaml_processor_spec.rb4
-rw-r--r--spec/lib/container_registry/blob_spec.rb61
-rw-r--r--spec/lib/container_registry/registry_spec.rb28
-rw-r--r--spec/lib/container_registry/repository_spec.rb65
-rw-r--r--spec/lib/container_registry/tag_spec.rb89
-rw-r--r--spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb4
-rw-r--r--spec/lib/gitlab/database/migration_helpers_spec.rb128
-rw-r--r--spec/lib/gitlab/email/message/repository_push_spec.rb4
-rw-r--r--spec/lib/gitlab/github_import/branch_formatter_spec.rb71
-rw-r--r--spec/lib/gitlab/github_import/pull_request_formatter_spec.rb46
-rw-r--r--spec/lib/gitlab/gitignore_spec.rb40
-rw-r--r--spec/lib/gitlab/import_url_spec.rb21
-rw-r--r--spec/lib/gitlab/lfs/lfs_router_spec.rb4
-rw-r--r--spec/lib/gitlab/metrics/instrumentation_spec.rb6
-rw-r--r--spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb16
-rw-r--r--spec/lib/gitlab/url_sanitizer_spec.rb68
-rw-r--r--spec/lib/json_web_token/rsa_token_spec.rb43
-rw-r--r--spec/lib/json_web_token/token_spec.rb18
-rw-r--r--spec/mailers/notify_spec.rb48
-rw-r--r--spec/models/application_setting_spec.rb9
-rw-r--r--spec/models/build_spec.rb50
-rw-r--r--spec/models/ci/commit_spec.rb1
-rw-r--r--spec/models/ci/runner_spec.rb32
-rw-r--r--spec/models/commit_spec.rb2
-rw-r--r--spec/models/concerns/subscribable_spec.rb10
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb2
-rw-r--r--spec/models/hooks/service_hook_spec.rb4
-rw-r--r--spec/models/hooks/system_hook_spec.rb20
-rw-r--r--spec/models/hooks/web_hook_spec.rb4
-rw-r--r--spec/models/legacy_diff_note_spec.rb74
-rw-r--r--spec/models/merge_request_spec.rb22
-rw-r--r--spec/models/milestone_spec.rb33
-rw-r--r--spec/models/namespace_spec.rb14
-rw-r--r--spec/models/note_spec.rb80
-rw-r--r--spec/models/project_services/slack_service/issue_message_spec.rb11
-rw-r--r--spec/models/project_services/slack_service/note_message_spec.rb4
-rw-r--r--spec/models/project_spec.rb82
-rw-r--r--spec/models/project_wiki_spec.rb3
-rw-r--r--spec/models/repository_spec.rb24
-rw-r--r--spec/models/user_spec.rb20
-rw-r--r--spec/requests/api/builds_spec.rb4
-rw-r--r--spec/requests/api/gitignores_spec.rb29
-rw-r--r--spec/requests/api/group_members_spec.rb10
-rw-r--r--spec/requests/api/issues_spec.rb12
-rw-r--r--spec/requests/api/labels_spec.rb82
-rw-r--r--spec/requests/api/licenses_spec.rb12
-rw-r--r--spec/requests/api/notes_spec.rb67
-rw-r--r--spec/requests/api/projects_spec.rb21
-rw-r--r--spec/requests/api/runners_spec.rb15
-rw-r--r--spec/requests/ci/api/builds_spec.rb36
-rw-r--r--spec/requests/ci/api/runners_spec.rb83
-rw-r--r--spec/requests/jwt_controller_spec.rb72
-rw-r--r--spec/routing/admin_routing_spec.rb7
-rw-r--r--spec/routing/routing_spec.rb51
-rw-r--r--spec/services/auth/container_registry_authentication_service_spec.rb239
-rw-r--r--spec/services/issues/create_service_spec.rb2
-rw-r--r--spec/services/issues/update_service_spec.rb24
-rw-r--r--spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb81
-rw-r--r--spec/services/merge_requests/create_service_spec.rb4
-rw-r--r--spec/services/merge_requests/merge_service_spec.rb15
-rw-r--r--spec/services/merge_requests/refresh_service_spec.rb28
-rw-r--r--spec/services/merge_requests/update_service_spec.rb8
-rw-r--r--spec/services/notification_service_spec.rb30
-rw-r--r--spec/services/projects/create_service_spec.rb4
-rw-r--r--spec/services/projects/destroy_service_spec.rb31
-rw-r--r--spec/services/projects/transfer_service_spec.rb11
-rw-r--r--spec/services/system_note_service_spec.rb14
-rw-r--r--spec/services/todo_service_spec.rb19
-rw-r--r--spec/support/jira_service_helper.rb10
-rw-r--r--spec/support/markdown_feature.rb6
-rw-r--r--spec/support/matchers/markdown_matchers.rb12
-rw-r--r--spec/support/stub_gitlab_calls.rb37
-rw-r--r--spec/tasks/gitlab/backup_rake_spec.rb14
-rw-r--r--spec/workers/emails_on_push_worker_spec.rb65
-rw-r--r--spec/workers/post_receive_spec.rb16
-rw-r--r--spec/workers/repository_import_worker_spec.rb26
117 files changed, 3534 insertions, 564 deletions
diff --git a/spec/config/mail_room_spec.rb b/spec/config/mail_room_spec.rb
index 462afb24f08..6fad7e2b9e7 100644
--- a/spec/config/mail_room_spec.rb
+++ b/spec/config/mail_room_spec.rb
@@ -43,7 +43,7 @@ describe "mail_room.yml" do
redis_config_file = Rails.root.join('config', 'resque.yml')
redis_url =
- if File.exists?(redis_config_file)
+ if File.exist?(redis_config_file)
YAML.load_file(redis_config_file)[Rails.env]
else
"redis://localhost:6379"
diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb
index ce2a62ae1fd..6caf37ddc2c 100644
--- a/spec/controllers/admin/users_controller_spec.rb
+++ b/spec/controllers/admin/users_controller_spec.rb
@@ -114,6 +114,82 @@ describe Admin::UsersController do
end
end
+ describe 'POST update' do
+ context 'when the password has changed' do
+ def update_password(user, password, password_confirmation = nil)
+ params = {
+ id: user.to_param,
+ user: {
+ password: password,
+ password_confirmation: password_confirmation || password
+ }
+ }
+
+ post :update, params
+ end
+
+ context 'when the new password is valid' do
+ it 'redirects to the user' do
+ update_password(user, 'AValidPassword1')
+
+ expect(response).to redirect_to(admin_user_path(user))
+ end
+
+ it 'updates the password' do
+ update_password(user, 'AValidPassword1')
+
+ expect { user.reload }.to change { user.encrypted_password }
+ end
+
+ it 'sets the new password to expire immediately' do
+ update_password(user, 'AValidPassword1')
+
+ expect { user.reload }.to change { user.password_expires_at }.to(a_value <= Time.now)
+ end
+ end
+
+ context 'when the new password is invalid' do
+ it 'shows the edit page again' do
+ update_password(user, 'invalid')
+
+ expect(response).to render_template(:edit)
+ end
+
+ it 'returns the error message' do
+ update_password(user, 'invalid')
+
+ expect(assigns[:user].errors).to contain_exactly(a_string_matching(/too short/))
+ end
+
+ it 'does not update the password' do
+ update_password(user, 'invalid')
+
+ expect { user.reload }.not_to change { user.encrypted_password }
+ end
+ end
+
+ context 'when the new password does not match the password confirmation' do
+ it 'shows the edit page again' do
+ update_password(user, 'AValidPassword1', 'AValidPassword2')
+
+ expect(response).to render_template(:edit)
+ end
+
+ it 'returns the error message' do
+ update_password(user, 'AValidPassword1', 'AValidPassword2')
+
+ expect(assigns[:user].errors).to contain_exactly(a_string_matching(/doesn't match/))
+ end
+
+ it 'does not update the password' do
+ update_password(user, 'AValidPassword1', 'AValidPassword2')
+
+ expect { user.reload }.not_to change { user.encrypted_password }
+ end
+ end
+ end
+ end
+
describe "POST impersonate" do
context "when the user is blocked" do
before do
diff --git a/spec/controllers/health_check_controller_spec.rb b/spec/controllers/health_check_controller_spec.rb
new file mode 100644
index 00000000000..0d8a68bb51a
--- /dev/null
+++ b/spec/controllers/health_check_controller_spec.rb
@@ -0,0 +1,105 @@
+require 'spec_helper'
+
+describe HealthCheckController do
+ let(:token) { current_application_settings.health_check_access_token }
+ let(:json_response) { JSON.parse(response.body) }
+ let(:xml_response) { Hash.from_xml(response.body)['hash'] }
+
+ describe 'GET #index' do
+ context 'when services are up but NO access token' do
+ it 'returns a not found page' do
+ get :index
+ expect(response).to be_not_found
+ end
+ end
+
+ context 'when services are up and an access token is provided' do
+ it 'supports passing the token in the header' do
+ request.headers['TOKEN'] = token
+ get :index
+ expect(response).to be_success
+ expect(response.content_type).to eq 'text/plain'
+ end
+
+ it 'supports successful plaintest response' do
+ get :index, token: token
+ expect(response).to be_success
+ expect(response.content_type).to eq 'text/plain'
+ end
+
+ it 'supports successful json response' do
+ get :index, token: token, format: :json
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be true
+ end
+
+ it 'supports successful xml response' do
+ get :index, token: token, format: :xml
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/xml'
+ expect(xml_response['healthy']).to be true
+ end
+
+ it 'supports successful responses for specific checks' do
+ get :index, token: token, checks: 'email', format: :json
+ expect(response).to be_success
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be true
+ end
+ end
+
+ context 'when a service is down but NO access token' do
+ it 'returns a not found page' do
+ get :index
+ expect(response).to be_not_found
+ end
+ end
+
+ context 'when a service is down and an access token is provided' do
+ before do
+ allow(HealthCheck::Utils).to receive(:process_checks).with('standard').and_return('The server is on fire')
+ allow(HealthCheck::Utils).to receive(:process_checks).with('email').and_return('Email is on fire')
+ end
+
+ it 'supports passing the token in the header' do
+ request.headers['TOKEN'] = token
+ get :index
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to include('The server is on fire')
+ end
+
+ it 'supports failure plaintest response' do
+ get :index, token: token
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'text/plain'
+ expect(response.body).to include('The server is on fire')
+ end
+
+ it 'supports failure json response' do
+ get :index, token: token, format: :json
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be false
+ expect(json_response['message']).to include('The server is on fire')
+ end
+
+ it 'supports failure xml response' do
+ get :index, token: token, format: :xml
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/xml'
+ expect(xml_response['healthy']).to be false
+ expect(xml_response['message']).to include('The server is on fire')
+ end
+
+ it 'supports failure responses for specific checks' do
+ get :index, token: token, checks: 'email', format: :json
+ expect(response.status).to eq(500)
+ expect(response.content_type).to eq 'application/json'
+ expect(json_response['healthy']).to be false
+ expect(json_response['message']).to include('Email is on fire')
+ end
+ end
+ end
+end
diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb
index 40bd83af861..1bd1fc5189e 100644
--- a/spec/controllers/projects/group_links_controller_spec.rb
+++ b/spec/controllers/projects/group_links_controller_spec.rb
@@ -28,7 +28,7 @@ describe Projects::GroupLinksController do
expect(group.shared_projects).to include project
end
- it 'redirects to project group links page'do
+ it 'redirects to project group links page' do
expect(response).to redirect_to(
namespace_project_group_links_path(project.namespace, project)
)
diff --git a/spec/controllers/projects/notification_settings_controller_spec.rb b/spec/controllers/projects/notification_settings_controller_spec.rb
index 4908b545648..c5d17d97ec9 100644
--- a/spec/controllers/projects/notification_settings_controller_spec.rb
+++ b/spec/controllers/projects/notification_settings_controller_spec.rb
@@ -34,5 +34,19 @@ describe Projects::NotificationSettingsController do
expect(response.status).to eq 200
end
end
+
+ context 'not authorized' do
+ let(:private_project) { create(:project, :private) }
+ before { sign_in(user) }
+
+ it 'returns 404' do
+ put :update,
+ namespace_id: private_project.namespace.to_param,
+ project_id: private_project.to_param,
+ notification_setting: { level: :participating }
+
+ expect(response.status).to eq(404)
+ end
+ end
end
end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 1caa476d37d..fb29274c687 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -42,7 +42,7 @@ describe Projects::RawController do
before do
public_project.lfs_objects << lfs_object
allow_any_instance_of(LfsObjectUploader).to receive(:exists?).and_return(true)
- allow(controller).to receive(:send_file) { controller.render nothing: true }
+ allow(controller).to receive(:send_file) { controller.head :ok }
end
it 'serves the file' do
diff --git a/spec/controllers/projects_controller_spec.rb b/spec/controllers/projects_controller_spec.rb
index 069cd917e5a..91b46c4d65c 100644
--- a/spec/controllers/projects_controller_spec.rb
+++ b/spec/controllers/projects_controller_spec.rb
@@ -8,6 +8,40 @@ describe ProjectsController do
let(:txt) { fixture_file_upload(Rails.root + 'spec/fixtures/doc_sample.txt', 'text/plain') }
describe "GET show" do
+ context "user not project member" do
+ before { sign_in(user) }
+
+ context "user does not have access to project" do
+ let(:private_project) { create(:project, :private) }
+
+ it "does not initialize notification setting" do
+ get :show, namespace_id: private_project.namespace.path, id: private_project.path
+ expect(assigns(:notification_setting)).to be_nil
+ end
+ end
+
+ context "user has access to project" do
+ context "and does not have notification setting" do
+ it "initializes notification as disabled" do
+ get :show, namespace_id: public_project.namespace.path, id: public_project.path
+ expect(assigns(:notification_setting).level).to eq("global")
+ end
+ end
+
+ context "and has notification setting" do
+ before do
+ setting = user.notification_settings_for(public_project)
+ setting.level = :watch
+ setting.save
+ end
+
+ it "shows current notification setting" do
+ get :show, namespace_id: public_project.namespace.path, id: public_project.path
+ expect(assigns(:notification_setting).level).to eq("watch")
+ end
+ end
+ end
+ end
context "rendering default project view" do
render_views
diff --git a/spec/controllers/registrations_controller_spec.rb b/spec/controllers/registrations_controller_spec.rb
new file mode 100644
index 00000000000..df70a589a89
--- /dev/null
+++ b/spec/controllers/registrations_controller_spec.rb
@@ -0,0 +1,33 @@
+require 'spec_helper'
+
+describe RegistrationsController do
+ describe '#create' do
+ around(:each) do |example|
+ perform_enqueued_jobs do
+ example.run
+ end
+ end
+
+ let(:user_params) { { user: { name: "new_user", username: "new_username", email: "new@user.com", password: "Any_password" } } }
+
+ context 'when sending email confirmation' do
+ before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(false) }
+
+ it 'logs user in directly' do
+ post(:create, user_params)
+ expect(ActionMailer::Base.deliveries.last).to be_nil
+ expect(subject.current_user).to_not be_nil
+ end
+ end
+
+ context 'when not sending email confirmation' do
+ before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(true) }
+
+ it 'does not authenticate user and sends confirmation email' do
+ post(:create, user_params)
+ expect(ActionMailer::Base.deliveries.last.to.first).to eq(user_params[:user][:email])
+ expect(subject.current_user).to be_nil
+ end
+ end
+ end
+end
diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb
index 8045c8b940d..c61ec174665 100644
--- a/spec/controllers/users_controller_spec.rb
+++ b/spec/controllers/users_controller_spec.rb
@@ -112,4 +112,26 @@ describe UsersController do
expect(response).to render_template('calendar_activities')
end
end
+
+ describe 'GET #snippets' do
+ before do
+ sign_in(user)
+ end
+
+ context 'format html' do
+ it 'renders snippets page' do
+ get :snippets, username: user.username
+ expect(response.status).to eq(200)
+ expect(response).to render_template('show')
+ end
+ end
+
+ context 'format json' do
+ it 'response with snippets json data' do
+ get :snippets, username: user.username, format: :json
+ expect(response.status).to eq(200)
+ expect(JSON.parse(response.body)).to have_key('html')
+ end
+ end
+ end
end
diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb
index 840b13196a6..26719f2652c 100644
--- a/spec/factories/notes.rb
+++ b/spec/factories/notes.rb
@@ -9,10 +9,10 @@ FactoryGirl.define do
author
factory :note_on_commit, traits: [:on_commit]
- factory :note_on_commit_diff, traits: [:on_commit, :on_diff]
+ factory :note_on_commit_diff, traits: [:on_commit, :on_diff], class: LegacyDiffNote
factory :note_on_issue, traits: [:on_issue], aliases: [:votable_note]
factory :note_on_merge_request, traits: [:on_merge_request]
- factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff]
+ factory :note_on_merge_request_diff, traits: [:on_merge_request, :on_diff], class: LegacyDiffNote
factory :note_on_project_snippet, traits: [:on_project_snippet]
factory :system_note, traits: [:system]
factory :downvote_note, traits: [:award, :downvote]
diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb
index e3681ae93a5..f426e27afed 100644
--- a/spec/factories/todos.rb
+++ b/spec/factories/todos.rb
@@ -18,5 +18,9 @@ FactoryGirl.define do
commit_id RepoHelpers.sample_commit.id
target_type "Commit"
end
+
+ trait :build_failed do
+ action { Todo::BUILD_FAILED }
+ end
end
end
diff --git a/spec/factories_spec.rb b/spec/factories_spec.rb
index 62de081661d..a073a1f9d7c 100644
--- a/spec/factories_spec.rb
+++ b/spec/factories_spec.rb
@@ -5,7 +5,7 @@ describe 'factories' do
describe "#{factory.name} factory" do
let(:entity) { build(factory.name) }
- it 'does not raise error when created 'do
+ it 'does not raise error when created' do
expect { entity }.to_not raise_error
end
diff --git a/spec/features/admin/admin_builds_spec.rb b/spec/features/admin/admin_builds_spec.rb
index 2e9851fb442..7bbe20fec43 100644
--- a/spec/features/admin/admin_builds_spec.rb
+++ b/spec/features/admin/admin_builds_spec.rb
@@ -19,6 +19,7 @@ describe 'Admin Builds' do
visit admin_builds_path
expect(page).to have_selector('.nav-links li.active', text: 'All')
+ expect(page).to have_selector('.row-content-block', text: 'All builds')
expect(page.all('.build-link').size).to eq(4)
expect(page).to have_link 'Cancel all'
end
diff --git a/spec/features/admin/admin_health_check_spec.rb b/spec/features/admin/admin_health_check_spec.rb
new file mode 100644
index 00000000000..dec2dedf2b5
--- /dev/null
+++ b/spec/features/admin/admin_health_check_spec.rb
@@ -0,0 +1,55 @@
+require 'spec_helper'
+
+feature "Admin Health Check", feature: true do
+ include WaitForAjax
+
+ before do
+ login_as :admin
+ end
+
+ describe '#show' do
+ before do
+ visit admin_health_check_path
+ end
+
+ it { page.has_text? 'Health Check' }
+ it { page.has_text? 'Health information can be retrieved' }
+
+ it 'has a health check access token' do
+ token = current_application_settings.health_check_access_token
+ expect(page).to have_content("Access token is #{token}")
+ expect(page).to have_selector('#health-check-token', text: token)
+ end
+
+ describe 'reload access token', js: true do
+ it 'changes the access token' do
+ orig_token = current_application_settings.health_check_access_token
+ click_button 'Reset health check access token'
+ wait_for_ajax
+ expect(find('#health-check-token').text).not_to eq orig_token
+ end
+ end
+ end
+
+ context 'when services are up' do
+ before do
+ visit admin_health_check_path
+ end
+
+ it 'shows healthy status' do
+ expect(page).to have_content('Current Status: Healthy')
+ end
+ end
+
+ context 'when a service is down' do
+ before do
+ allow(HealthCheck::Utils).to receive(:process_checks).and_return('The server is on fire')
+ visit admin_health_check_path
+ end
+
+ it 'shows unhealthy status' do
+ expect(page).to have_content('Current Status: Unhealthy')
+ expect(page).to have_content('The server is on fire')
+ end
+ end
+end
diff --git a/spec/features/admin/admin_users_spec.rb b/spec/features/admin/admin_users_spec.rb
index 4570e409128..6dee0cd8d47 100644
--- a/spec/features/admin/admin_users_spec.rb
+++ b/spec/features/admin/admin_users_spec.rb
@@ -210,6 +210,8 @@ describe "Admin::Users", feature: true do
before do
fill_in "user_name", with: "Big Bang"
fill_in "user_email", with: "bigbang@mail.com"
+ fill_in "user_password", with: "AValidPassword1"
+ fill_in "user_password_confirmation", with: "AValidPassword1"
check "user_admin"
click_button "Save changes"
end
@@ -223,6 +225,7 @@ describe "Admin::Users", feature: true do
@simple_user.reload
expect(@simple_user.name).to eq('Big Bang')
expect(@simple_user.is_admin?).to be_truthy
+ expect(@simple_user.password_expires_at).to be <= Time.now
end
end
end
diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb
index 090a941958f..f83a78308e3 100644
--- a/spec/features/builds_spec.rb
+++ b/spec/features/builds_spec.rb
@@ -43,6 +43,7 @@ describe "Builds" do
end
it { expect(page).to have_selector('.nav-links li.active', text: 'All') }
+ it { expect(page).to have_selector('.row-content-block', text: 'All builds from this project') }
it { expect(page).to have_content @build.short_sha }
it { expect(page).to have_content @build.ref }
it { expect(page).to have_content @build.name }
diff --git a/spec/features/container_registry_spec.rb b/spec/features/container_registry_spec.rb
new file mode 100644
index 00000000000..53b4f027117
--- /dev/null
+++ b/spec/features/container_registry_spec.rb
@@ -0,0 +1,44 @@
+require 'spec_helper'
+
+describe "Container Registry" do
+ let(:project) { create(:empty_project) }
+ let(:repository) { project.container_registry_repository }
+ let(:tag_name) { 'latest' }
+ let(:tags) { [tag_name] }
+
+ before do
+ login_as(:user)
+ project.team << [@user, :developer]
+ stub_container_registry_tags(*tags)
+ stub_container_registry_config(enabled: true)
+ allow(Auth::ContainerRegistryAuthenticationService).to receive(:full_access_token).and_return('token')
+ end
+
+ describe 'GET /:project/container_registry' do
+ before do
+ visit namespace_project_container_registry_index_path(project.namespace, project)
+ end
+
+ context 'when no tags' do
+ let(:tags) { [] }
+
+ it { expect(page).to have_content('No images in Container Registry for this project') }
+ end
+
+ context 'when there are tags' do
+ it { expect(page).to have_content(tag_name)}
+ end
+ end
+
+ describe 'DELETE /:project/container_registry/tag' do
+ before do
+ visit namespace_project_container_registry_index_path(project.namespace, project)
+ end
+
+ it do
+ expect_any_instance_of(::ContainerRegistry::Tag).to receive(:delete).and_return(true)
+
+ click_on 'Remove'
+ end
+ end
+end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index d5755c293c5..749ee01890c 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -64,6 +64,64 @@ describe 'Issues', feature: true do
end
end
+ describe 'due date', js: true do
+ context 'on new form' do
+ before do
+ visit new_namespace_project_issue_path(project.namespace, project)
+ end
+
+ it 'should save with due date' do
+ date = Date.today.at_beginning_of_month
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+
+ page.within '.datepicker' do
+ click_link date.day
+ end
+
+ expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
+
+ click_button 'Submit issue'
+
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
+ end
+ end
+
+ context 'on edit form' do
+ let(:issue) { create(:issue, author: @user,project: project, due_date: Date.today.at_beginning_of_month.to_s) }
+
+ before do
+ visit edit_namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'should save with due date' do
+ date = Date.today.at_beginning_of_month
+
+ expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
+
+ date = date.tomorrow
+
+ fill_in 'issue_title', with: 'bug 345'
+ fill_in 'issue_description', with: 'bug description'
+
+ page.within '.datepicker' do
+ click_link date.day
+ end
+
+ expect(find('#issuable-due-date', visible: false).value).to eq date.to_s
+
+ click_button 'Save changes'
+
+ page.within '.issuable-sidebar' do
+ expect(page).to have_content date.to_s(:medium)
+ end
+ end
+ end
+ end
+
describe 'Issue info' do
it 'excludes award_emoji from comment count' do
issue = create(:issue, author: @user, assignee: @user, project: project, title: 'foobar')
@@ -331,7 +389,7 @@ describe 'Issues', feature: true do
page.within '.assignee' do
click_link 'Edit'
end
-
+
page.within '.dropdown-menu-user' do
click_link @user.name
end
@@ -431,6 +489,43 @@ describe 'Issues', feature: true do
end
end
+ describe 'due date' do
+ context 'update due on issue#show', js: true do
+ let(:issue) { create(:issue, project: project, author: @user, assignee: @user) }
+
+ before do
+ visit namespace_project_issue_path(project.namespace, project, issue)
+ end
+
+ it 'should add due date to issue' do
+ page.within '.due_date' do
+ click_link 'Edit'
+
+ page.within '.ui-datepicker-calendar' do
+ first('.ui-state-default').click
+ end
+
+ expect(page).to have_no_content 'None'
+ end
+ end
+
+ it 'should remove due date from issue' do
+ page.within '.due_date' do
+ click_link 'Edit'
+
+ page.within '.ui-datepicker-calendar' do
+ first('.ui-state-default').click
+ end
+
+ expect(page).to have_no_content 'None'
+
+ click_link 'remove due date'
+ expect(page).to have_content 'None'
+ end
+ end
+ end
+ end
+
def first_issue
page.all('ul.issues-list > li').first.text
end
diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb
index 0148c87084a..1d892fe1a55 100644
--- a/spec/features/markdown_spec.rb
+++ b/spec/features/markdown_spec.rb
@@ -278,6 +278,10 @@ describe 'GitLab Markdown', feature: true do
it 'includes GollumTagsFilter' do
expect(doc).to parse_gollum_tags
end
+
+ it 'includes InlineDiffFilter' do
+ expect(doc).to parse_inline_diffs
+ end
end
# Fake a `current_user` helper
diff --git a/spec/features/merge_requests/user_lists_merge_requests_spec.rb b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
index cc7f78e7325..2c7e1c748ad 100644
--- a/spec/features/merge_requests/user_lists_merge_requests_spec.rb
+++ b/spec/features/merge_requests/user_lists_merge_requests_spec.rb
@@ -38,6 +38,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
expect(page).to have_content 'lfs'
expect(page).not_to have_content 'fix'
expect(page).not_to have_content 'markdown'
+ expect(count_merge_requests).to eq(1)
end
it 'filters on a specific assignee' do
@@ -46,6 +47,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
expect(page).not_to have_content 'lfs'
expect(page).to have_content 'fix'
expect(page).to have_content 'markdown'
+ expect(count_merge_requests).to eq(2)
end
it 'sorts by newest' do
@@ -53,6 +55,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
expect(first_merge_request).to include('lfs')
expect(last_merge_request).to include('fix')
+ expect(count_merge_requests).to eq(3)
end
it 'sorts by oldest' do
@@ -60,30 +63,35 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
expect(first_merge_request).to include('fix')
expect(last_merge_request).to include('lfs')
+ expect(count_merge_requests).to eq(3)
end
it 'sorts by last updated' do
visit_merge_requests(project, sort: sort_value_recently_updated)
expect(first_merge_request).to include('lfs')
+ expect(count_merge_requests).to eq(3)
end
it 'sorts by oldest updated' do
visit_merge_requests(project, sort: sort_value_oldest_updated)
expect(first_merge_request).to include('markdown')
+ expect(count_merge_requests).to eq(3)
end
it 'sorts by milestone due soon' do
visit_merge_requests(project, sort: sort_value_milestone_soon)
expect(first_merge_request).to include('fix')
+ expect(count_merge_requests).to eq(3)
end
it 'sorts by milestone due later' do
visit_merge_requests(project, sort: sort_value_milestone_later)
expect(first_merge_request).to include('markdown')
+ expect(count_merge_requests).to eq(3)
end
it 'filters on one label and sorts by due soon' do
@@ -94,6 +102,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
sort: sort_value_due_date_soon)
expect(first_merge_request).to include('fix')
+ expect(count_merge_requests).to eq(1)
end
context 'while filtering on two labels' do
@@ -110,6 +119,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
sort: sort_value_due_date_soon)
expect(first_merge_request).to include('fix')
+ expect(count_merge_requests).to eq(1)
end
context 'filter on assignee and' do
@@ -119,6 +129,7 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
sort: sort_value_due_date_soon)
expect(first_merge_request).to include('fix')
+ expect(count_merge_requests).to eq(1)
end
end
end
@@ -134,4 +145,8 @@ describe 'Projects > Merge requests > User lists merge requests', feature: true
def last_merge_request
page.all('ul.mr-list > li').last.text
end
+
+ def count_merge_requests
+ page.all('ul.mr-list > li').count
+ end
end
diff --git a/spec/features/notes_on_merge_requests_spec.rb b/spec/features/notes_on_merge_requests_spec.rb
index 389812ff7e1..9e9fec01943 100644
--- a/spec/features/notes_on_merge_requests_spec.rb
+++ b/spec/features/notes_on_merge_requests_spec.rb
@@ -192,7 +192,7 @@ describe 'Comments', feature: true do
end
it 'should be removed when canceled' do
- page.within(".diff-file form[id$='#{line_code}']") do
+ page.within(".diff-file form[id$='#{line_code}-true']") do
find('.js-close-discussion-note-form').trigger('click')
end
diff --git a/spec/features/pipelines_spec.rb b/spec/features/pipelines_spec.rb
new file mode 100644
index 00000000000..1d6f4485c81
--- /dev/null
+++ b/spec/features/pipelines_spec.rb
@@ -0,0 +1,159 @@
+require 'spec_helper'
+
+describe "Pipelines" do
+ include GitlabRoutingHelper
+
+ let(:project) { create(:empty_project) }
+ let(:user) { create(:user) }
+
+ before do
+ login_as(user)
+ project.team << [user, :developer]
+ end
+
+ describe 'GET /:project/pipelines' do
+ let!(:pipeline) { create(:ci_commit, project: project, ref: 'master', status: 'running') }
+
+ [:all, :running, :branches].each do |scope|
+ context "displaying #{scope}" do
+ let(:project) { create(:project) }
+
+ before { visit namespace_project_pipelines_path(project.namespace, project, scope: scope) }
+
+ it { expect(page).to have_content(pipeline.short_sha) }
+ end
+ end
+
+ context 'anonymous access' do
+ before { visit namespace_project_pipelines_path(project.namespace, project) }
+
+ it { expect(page).to have_http_status(:success) }
+ end
+
+ context 'cancelable pipeline' do
+ let!(:running) { create(:ci_build, :running, commit: pipeline, stage: 'test', commands: 'test') }
+
+ before { visit namespace_project_pipelines_path(project.namespace, project) }
+
+ it { expect(page).to have_link('Cancel') }
+ it { expect(page).to have_selector('.ci-running') }
+
+ context 'when canceling' do
+ before { click_link('Cancel') }
+
+ it { expect(page).to_not have_link('Cancel') }
+ it { expect(page).to have_selector('.ci-canceled') }
+ end
+ end
+
+ context 'retryable pipelines' do
+ let!(:failed) { create(:ci_build, :failed, commit: pipeline, stage: 'test', commands: 'test') }
+
+ before { visit namespace_project_pipelines_path(project.namespace, project) }
+
+ it { expect(page).to have_link('Retry') }
+ it { expect(page).to have_selector('.ci-failed') }
+
+ context 'when retrying' do
+ before { click_link('Retry') }
+
+ it { expect(page).to_not have_link('Retry') }
+ it { expect(page).to have_selector('.ci-pending') }
+ end
+ end
+
+ context 'downloadable pipelines' do
+ context 'with artifacts' do
+ let!(:with_artifacts) { create(:ci_build, :artifacts, :success, commit: pipeline, name: 'rspec tests', stage: 'test') }
+
+ before { visit namespace_project_pipelines_path(project.namespace, project) }
+
+ it { expect(page).to have_selector('.build-artifacts') }
+ it { expect(page).to have_link(with_artifacts.name) }
+ end
+
+ context 'without artifacts' do
+ let!(:without_artifacts) { create(:ci_build, :success, commit: pipeline, name: 'rspec', stage: 'test') }
+
+ it { expect(page).to_not have_selector('.build-artifacts') }
+ end
+ end
+ end
+
+ describe 'GET /:project/pipelines/:id' do
+ let(:pipeline) { create(:ci_commit, project: project, ref: 'master') }
+
+ before do
+ @success = create(:ci_build, :success, commit: pipeline, stage: 'build', name: 'build')
+ @failed = create(:ci_build, :failed, commit: pipeline, stage: 'test', name: 'test', commands: 'test')
+ @running = create(:ci_build, :running, commit: pipeline, stage: 'deploy', name: 'deploy')
+ @external = create(:generic_commit_status, status: 'success', commit: pipeline, name: 'jenkins', stage: 'external')
+ end
+
+ before { visit namespace_project_pipeline_path(project.namespace, project, pipeline) }
+
+ it 'showing a list of builds' do
+ expect(page).to have_content('Tests')
+ expect(page).to have_content(@success.id)
+ expect(page).to have_content('Deploy')
+ expect(page).to have_content(@failed.id)
+ expect(page).to have_content(@running.id)
+ expect(page).to have_content(@external.id)
+ expect(page).to have_content('Retry failed')
+ expect(page).to have_content('Cancel running')
+ end
+
+ context 'retrying builds' do
+ it { expect(page).to_not have_content('retried') }
+
+ context 'when retrying' do
+ before { click_on 'Retry failed' }
+
+ it { expect(page).to_not have_content('Retry failed') }
+ it { expect(page).to have_content('retried') }
+ end
+ end
+
+ context 'canceling builds' do
+ it { expect(page).to_not have_selector('.ci-canceled') }
+
+ context 'when canceling' do
+ before { click_on 'Cancel running' }
+
+ it { expect(page).to_not have_content('Cancel running') }
+ it { expect(page).to have_selector('.ci-canceled') }
+ end
+ end
+ end
+
+ describe 'POST /:project/pipelines' do
+ let(:project) { create(:project) }
+
+ before { visit new_namespace_project_pipeline_path(project.namespace, project) }
+
+ context 'for valid commit' do
+ before { fill_in('Create for', with: 'master') }
+
+ context 'with gitlab-ci.yml' do
+ before { stub_ci_commit_to_return_yaml_file }
+
+ it { expect{ click_on 'Create pipeline' }.to change{ Ci::Commit.count }.by(1) }
+ end
+
+ context 'without gitlab-ci.yml' do
+ before { click_on 'Create pipeline' }
+
+ it { expect(page).to have_content('Missing .gitlab-ci.yml file') }
+ end
+ end
+
+ context 'for invalid commit' do
+ before do
+ fill_in('Create for', with: 'invalid reference')
+ click_on 'Create pipeline'
+ end
+
+ it { expect(page).to have_content('Reference not found') }
+ end
+ end
+end
diff --git a/spec/features/projects/files/gitignore_dropdown_spec.rb b/spec/features/projects/files/gitignore_dropdown_spec.rb
new file mode 100644
index 00000000000..073a83b6896
--- /dev/null
+++ b/spec/features/projects/files/gitignore_dropdown_spec.rb
@@ -0,0 +1,30 @@
+require 'spec_helper'
+
+feature 'User wants to add a .gitignore file', feature: true do
+ include WaitForAjax
+
+ before do
+ user = create(:user)
+ project = create(:project)
+ project.team << [user, :master]
+ login_as user
+ visit namespace_project_new_blob_path(project.namespace, project, 'master', file_name: '.gitignore')
+ end
+
+ scenario 'user can see .gitignore dropdown' do
+ expect(page).to have_css('.gitignore-selector')
+ end
+
+ scenario 'user can pick a .gitignore file from the dropdown', js: true do
+ find('.js-gitignore-selector').click
+ wait_for_ajax
+ within '.gitignore-selector' do
+ find('.dropdown-input-field').set('rails')
+ find('.dropdown-content li', text: 'Rails').click
+ end
+ wait_for_ajax
+
+ expect(page).to have_content('/.bundle')
+ expect(page).to have_content('# Gemfile.lock, .ruby-version, .ruby-gemset')
+ end
+end
diff --git a/spec/features/projects/files/project_owner_creates_license_file_spec.rb b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
index 3d6ffbc4c6b..ecc818eb1e1 100644
--- a/spec/features/projects/files/project_owner_creates_license_file_spec.rb
+++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb
@@ -25,7 +25,7 @@ feature 'project owner creates a license file', feature: true, js: true do
file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)')
- expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
click_button 'Commit Changes'
@@ -33,7 +33,7 @@ feature 'project owner creates a license file', feature: true, js: true do
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)')
- expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end
scenario 'project master creates a license file from the "Add license" link' do
@@ -48,7 +48,7 @@ feature 'project owner creates a license file', feature: true, js: true do
file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)')
- expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
click_button 'Commit Changes'
@@ -56,6 +56,6 @@ feature 'project owner creates a license file', feature: true, js: true do
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)')
- expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end
end
diff --git a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
index 3268e240200..34eda29c285 100644
--- a/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
+++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb
@@ -24,7 +24,7 @@ feature 'project owner sees a link to create a license file in empty project', f
file_content = find('.file-content')
expect(file_content).to have_content('The MIT License (MIT)')
- expect(file_content).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ expect(file_content).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
fill_in :commit_message, with: 'Add a LICENSE file', visible: true
# Remove pre-receive hook so we can push without auth
@@ -34,6 +34,6 @@ feature 'project owner sees a link to create a license file in empty project', f
expect(current_path).to eq(
namespace_project_blob_path(project.namespace, project, 'master/LICENSE'))
expect(page).to have_content('The MIT License (MIT)')
- expect(page).to have_content("Copyright (c) 2016 #{project.namespace.human_name}")
+ expect(page).to have_content("Copyright (c) #{Time.now.year} #{project.namespace.human_name}")
end
end
diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb
index 8edeb8d18af..49156130ad3 100644
--- a/spec/features/runners_spec.rb
+++ b/spec/features/runners_spec.rb
@@ -110,4 +110,37 @@ describe "Runners" do
expect(page).to have_content(@specific_runner.platform)
end
end
+
+ feature 'configuring runners ability to picking untagged jobs' do
+ given(:project) { create(:empty_project) }
+ given(:runner) { create(:ci_runner) }
+
+ background do
+ project.team << [user, :master]
+ project.runners << runner
+ end
+
+ scenario 'user checks default configuration' do
+ visit namespace_project_runner_path(project.namespace, project, runner)
+
+ expect(page).to have_content 'Can run untagged jobs Yes'
+ end
+
+ context 'when runner has tags' do
+ before { runner.update_attribute(:tag_list, ['tag']) }
+
+ scenario 'user wants to prevent runner from running untagged job' do
+ visit runners_path(project)
+ page.within('.activated-specific-runners') do
+ first('small > a').click
+ end
+
+ uncheck 'runner_run_untagged'
+ click_button 'Save changes'
+
+ expect(page).to have_content 'Can run untagged jobs No'
+ expect(runner.reload.run_untagged?).to eq false
+ end
+ end
+ end
end
diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb
index 58aabd913eb..4229e82b443 100644
--- a/spec/features/signup_spec.rb
+++ b/spec/features/signup_spec.rb
@@ -2,20 +2,45 @@ require 'spec_helper'
feature 'Signup', feature: true do
describe 'signup with no errors' do
- it 'creates the user account and sends a confirmation email' do
- user = build(:user)
- visit root_path
+ context "when sending confirmation email" do
+ before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(true) }
- fill_in 'new_user_name', with: user.name
- fill_in 'new_user_username', with: user.username
- fill_in 'new_user_email', with: user.email
- fill_in 'new_user_password', with: user.password
- click_button "Sign up"
+ it 'creates the user account and sends a confirmation email' do
+ user = build(:user)
+
+ visit root_path
+
+ fill_in 'new_user_name', with: user.name
+ fill_in 'new_user_username', with: user.username
+ fill_in 'new_user_email', with: user.email
+ fill_in 'new_user_password', with: user.password
+ click_button "Sign up"
- expect(current_path).to eq users_almost_there_path
- expect(page).to have_content("Please check your email to confirm your account")
+ expect(current_path).to eq users_almost_there_path
+ expect(page).to have_content("Please check your email to confirm your account")
+ end
end
+
+ context "when not sending confirmation email" do
+ before { allow_any_instance_of(ApplicationSetting).to receive(:send_user_confirmation_email).and_return(false) }
+
+ it 'creates the user account and goes to dashboard' do
+ user = build(:user)
+
+ visit root_path
+
+ fill_in 'new_user_name', with: user.name
+ fill_in 'new_user_username', with: user.username
+ fill_in 'new_user_email', with: user.email
+ fill_in 'new_user_password', with: user.password
+ click_button "Sign up"
+
+ expect(current_path).to eq dashboard_projects_path
+ expect(page).to have_content("Welcome! You have signed up successfully.")
+ end
+ end
+
end
describe 'signup with errors' do
diff --git a/spec/features/variables_spec.rb b/spec/features/variables_spec.rb
index afea1840cd7..48e2dae4884 100644
--- a/spec/features/variables_spec.rb
+++ b/spec/features/variables_spec.rb
@@ -1,24 +1,53 @@
require 'spec_helper'
-describe "Variables" do
- let(:user) { create(:user) }
- before { login_as(user) }
-
- describe "specific runners" do
- before do
- @project = FactoryGirl.create :empty_project
- @project.team << [user, :master]
+describe 'Project variables', js: true do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:variable) { create(:ci_variable, key: 'test') }
+
+ before do
+ login_as(user)
+ project.team << [user, :master]
+ project.variables << variable
+
+ visit namespace_project_variables_path(project.namespace, project)
+ end
+
+ it 'should show list of variables' do
+ page.within('.variables-table') do
+ expect(page).to have_content(variable.key)
+ end
+ end
+
+ it 'should add new variable' do
+ fill_in('variable_key', with: 'key')
+ fill_in('variable_value', with: 'key value')
+ click_button('Add new variable')
+
+ page.within('.variables-table') do
+ expect(page).to have_content('key')
+ end
+ end
+
+ it 'should delete variable' do
+ page.within('.variables-table') do
+ find('.btn-variable-delete').click
+ end
+
+ expect(page).to_not have_selector('variables-table')
+ end
+
+ it 'should edit variable' do
+ page.within('.variables-table') do
+ find('.btn-variable-edit').click
end
- it "creates variable", js: true do
- visit namespace_project_variables_path(@project.namespace, @project)
- click_on "Add a variable"
- fill_in "Key", with: "SECRET_KEY"
- fill_in "Value", with: "SECRET_VALUE"
- click_on "Save changes"
+ fill_in('variable_key', with: 'key')
+ fill_in('variable_value', with: 'key value')
+ click_button('Save variable')
- expect(page).to have_content("Variables were successfully updated.")
- expect(@project.variables.count).to eq(1)
+ page.within('.variables-table') do
+ expect(page).to have_content('key')
end
end
end
diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb
index bc607a29751..ec8809e6926 100644
--- a/spec/finders/issues_finder_spec.rb
+++ b/spec/finders/issues_finder_spec.rb
@@ -1,10 +1,10 @@
require 'spec_helper'
describe IssuesFinder do
- let(:user) { create :user }
- let(:user2) { create :user }
- let(:project1) { create(:project) }
- let(:project2) { create(:project) }
+ let(:user) { create(:user) }
+ let(:user2) { create(:user) }
+ let(:project1) { create(:empty_project) }
+ let(:project2) { create(:empty_project) }
let(:milestone) { create(:milestone, project: project1) }
let(:label) { create(:label, project: project2) }
let(:issue1) { create(:issue, author: user, assignee: user, project: project1, milestone: milestone) }
@@ -16,101 +16,147 @@ describe IssuesFinder do
project1.team << [user, :master]
project2.team << [user, :developer]
project2.team << [user2, :developer]
+
+ issue1
+ issue2
+ issue3
end
- describe :execute do
- before :each do
- issue1
- issue2
- issue3
- end
+ describe '#execute' do
+ let(:search_user) { user }
+ let(:params) { {} }
+ let(:issues) { IssuesFinder.new(search_user, params.merge(scope: scope, state: 'opened')).execute }
context 'scope: all' do
- it 'should filter by all' do
- params = { scope: "all", state: 'opened' }
- issues = IssuesFinder.new(user, params).execute
- expect(issues.size).to eq(3)
+ let(:scope) { 'all' }
+
+ it 'returns all issues' do
+ expect(issues).to contain_exactly(issue1, issue2, issue3)
end
- it 'should filter by assignee id' do
- params = { scope: "all", assignee_id: user.id, state: 'opened' }
- issues = IssuesFinder.new(user, params).execute
- expect(issues.size).to eq(2)
+ context 'filtering by assignee ID' do
+ let(:params) { { assignee_id: user.id } }
+
+ it 'returns issues assigned to that user' do
+ expect(issues).to contain_exactly(issue1, issue2)
+ end
end
- it 'should filter by author id' do
- params = { scope: "all", author_id: user2.id, state: 'opened' }
- issues = IssuesFinder.new(user, params).execute
- expect(issues).to eq([issue3])
+ context 'filtering by author ID' do
+ let(:params) { { author_id: user2.id } }
+
+ it 'returns issues created by that user' do
+ expect(issues).to contain_exactly(issue3)
+ end
end
- it 'should filter by milestone id' do
- params = { scope: "all", milestone_title: milestone.title, state: 'opened' }
- issues = IssuesFinder.new(user, params).execute
- expect(issues).to eq([issue1])
+ context 'filtering by milestone' do
+ let(:params) { { milestone_title: milestone.title } }
+
+ it 'returns issues assigned to that milestone' do
+ expect(issues).to contain_exactly(issue1)
+ end
end
- it 'should filter by no milestone id' do
- params = { scope: "all", milestone_title: Milestone::None.title, state: 'opened' }
- issues = IssuesFinder.new(user, params).execute
- expect(issues).to match_array([issue2, issue3])
+ context 'filtering by no milestone' do
+ let(:params) { { milestone_title: Milestone::None.title } }
+
+ it 'returns issues with no milestone' do
+ expect(issues).to contain_exactly(issue2, issue3)
+ end
end
- it 'should filter by label name' do
- params = { scope: "all", label_name: label.title, state: 'opened' }
- issues = IssuesFinder.new(user, params).execute
- expect(issues).to eq([issue2])
+ context 'filtering by upcoming milestone' do
+ let(:params) { { milestone_title: Milestone::Upcoming.name } }
+
+ let(:project_no_upcoming_milestones) { create(:empty_project, :public) }
+ let(:project_next_1_1) { create(:empty_project, :public) }
+ let(:project_next_8_8) { create(:empty_project, :public) }
+
+ let(:yesterday) { Date.today - 1.day }
+ let(:tomorrow) { Date.today + 1.day }
+ let(:two_days_from_now) { Date.today + 2.days }
+ let(:ten_days_from_now) { Date.today + 10.days }
+
+ let(:milestones) do
+ [
+ create(:milestone, :closed, project: project_no_upcoming_milestones),
+ create(:milestone, project: project_next_1_1, title: '1.1', due_date: two_days_from_now),
+ create(:milestone, project: project_next_1_1, title: '8.8', due_date: ten_days_from_now),
+ create(:milestone, project: project_next_8_8, title: '1.1', due_date: yesterday),
+ create(:milestone, project: project_next_8_8, title: '8.8', due_date: tomorrow)
+ ]
+ end
+
+ before do
+ milestones.each do |milestone|
+ create(:issue, project: milestone.project, milestone: milestone, author: user, assignee: user)
+ end
+ end
+
+ it 'returns issues in the upcoming milestone for each project' do
+ expect(issues.map { |issue| issue.milestone.title }).to contain_exactly('1.1', '8.8')
+ expect(issues.map { |issue| issue.milestone.due_date }).to contain_exactly(tomorrow, two_days_from_now)
+ end
end
- it 'returns unique issues when filtering by multiple labels' do
- label2 = create(:label, project: project2)
+ context 'filtering by label' do
+ let(:params) { { label_name: label.title } }
- create(:label_link, label: label2, target: issue2)
+ it 'returns issues with that label' do
+ expect(issues).to contain_exactly(issue2)
+ end
+ end
- params = {
- scope: 'all',
- label_name: [label.title, label2.title].join(','),
- state: 'opened'
- }
+ context 'filtering by multiple labels' do
+ let(:params) { { label_name: [label.title, label2.title].join(',') } }
+ let(:label2) { create(:label, project: project2) }
- issues = IssuesFinder.new(user, params).execute
+ before { create(:label_link, label: label2, target: issue2) }
- expect(issues).to eq([issue2])
+ it 'returns the unique issues with any of those labels' do
+ expect(issues).to contain_exactly(issue2)
+ end
end
- it 'should filter by no label name' do
- params = { scope: "all", label_name: Label::None.title, state: 'opened' }
- issues = IssuesFinder.new(user, params).execute
- expect(issues).to match_array([issue1, issue3])
+ context 'filtering by no label' do
+ let(:params) { { label_name: Label::None.title } }
+
+ it 'returns issues with no labels' do
+ expect(issues).to contain_exactly(issue1, issue3)
+ end
end
- it 'should be empty for unauthorized user' do
- params = { scope: "all", state: 'opened' }
- issues = IssuesFinder.new(nil, params).execute
- expect(issues.size).to be_zero
+ context 'when the user is unauthorized' do
+ let(:search_user) { nil }
+
+ it 'returns no results' do
+ expect(issues).to be_empty
+ end
end
- it 'should not include unauthorized issues' do
- params = { scope: "all", state: 'opened' }
- issues = IssuesFinder.new(user2, params).execute
- expect(issues.size).to eq(2)
- expect(issues).not_to include(issue1)
- expect(issues).to include(issue2)
- expect(issues).to include(issue3)
+ context 'when the user can see some, but not all, issues' do
+ let(:search_user) { user2 }
+
+ it 'returns only issues they can see' do
+ expect(issues).to contain_exactly(issue2, issue3)
+ end
end
end
context 'personal scope' do
- it 'should filter by assignee' do
- params = { scope: "assigned-to-me", state: 'opened' }
- issues = IssuesFinder.new(user, params).execute
- expect(issues.size).to eq(2)
+ let(:scope) { 'assigned-to-me' }
+
+ it 'returns issue assigned to the user' do
+ expect(issues).to contain_exactly(issue1, issue2)
end
- it 'should filter by project' do
- params = { scope: "assigned-to-me", state: 'opened', project_id: project1.id }
- issues = IssuesFinder.new(user, params).execute
- expect(issues.size).to eq(1)
+ context 'filtering by project' do
+ let(:params) { { project_id: project1.id } }
+
+ it 'returns issues assigned to the user in that project' do
+ expect(issues).to contain_exactly(issue1)
+ end
end
end
end
diff --git a/spec/fixtures/container_registry/config_blob.json b/spec/fixtures/container_registry/config_blob.json
new file mode 100644
index 00000000000..1028c994a24
--- /dev/null
+++ b/spec/fixtures/container_registry/config_blob.json
@@ -0,0 +1 @@
+{"architecture":"amd64","config":{"Hostname":"b14cd8298755","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":null,"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"container":"b14cd82987550b01af9a666a2f4c996280a6152e66873134fae5a0f223dc5976","container_config":{"Hostname":"b14cd8298755","Domainname":"","User":"","AttachStdin":false,"AttachStdout":false,"AttachStderr":false,"Tty":false,"OpenStdin":false,"StdinOnce":false,"Env":null,"Cmd":["/bin/sh","-c","#(nop) ADD file:033ab063740d9ff4dcfb1c69eccf25f91d88729f57cd5a73050e014e3e094aa0 in /"],"Image":"","Volumes":null,"WorkingDir":"","Entrypoint":null,"OnBuild":null,"Labels":null},"created":"2016-04-01T20:53:00.160300546Z","docker_version":"1.9.1","history":[{"created":"2016-04-01T20:53:00.160300546Z","created_by":"/bin/sh -c #(nop) ADD file:033ab063740d9ff4dcfb1c69eccf25f91d88729f57cd5a73050e014e3e094aa0 in /"}],"os":"linux","rootfs":{"type":"layers","diff_ids":["sha256:c56b7dabbc7aa730eeab07668bdcbd7e3d40855047ca9a0cc1bfed23a2486111"]}}
diff --git a/spec/fixtures/container_registry/tag_manifest.json b/spec/fixtures/container_registry/tag_manifest.json
new file mode 100644
index 00000000000..1b6008e2872
--- /dev/null
+++ b/spec/fixtures/container_registry/tag_manifest.json
@@ -0,0 +1 @@
+{"schemaVersion":2,"mediaType":"application/vnd.docker.distribution.manifest.v2+json","config":{"mediaType":"application/octet-stream","size":1145,"digest":"sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac"},"layers":[{"mediaType":"application/vnd.docker.image.rootfs.diff.tar.gzip","size":2319870,"digest":"sha256:420890c9e918b6668faaedd9000e220190f2493b0693ee563ebd7b4cc754a57d"}]}
diff --git a/spec/fixtures/markdown.md.erb b/spec/fixtures/markdown.md.erb
index 1772cc3f6a4..34ce7c4f033 100644
--- a/spec/fixtures/markdown.md.erb
+++ b/spec/fixtures/markdown.md.erb
@@ -216,10 +216,14 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
#### MilestoneReferenceFilter
-- Milestone: <%= milestone.to_reference %>
+- Milestone by ID: <%= simple_milestone.to_reference %>
+- Milestone by name: <%= Milestone.reference_prefix %><%= simple_milestone.name %>
+- Milestone by name in quotes: <%= milestone.to_reference(format: :name) %>
- Milestone in another project: <%= xmilestone.to_reference(project) %>
-- Ignored in code: `<%= milestone.to_reference %>`
-- Link to milestone by URL: [Milestone](<%= urls.namespace_project_milestone_url(milestone.project.namespace, milestone.project, milestone) %>)
+- Ignored in code: `<%= simple_milestone.to_reference %>`
+- Ignored in links: [Link to <%= simple_milestone.to_reference %>](#milestone-link)
+- Milestone by URL: <%= urls.namespace_project_milestone_url(milestone.project.namespace, milestone.project, milestone) %>
+- Link to milestone by URL: [Milestone](<%= milestone.to_reference %>)
### Task Lists
@@ -239,3 +243,16 @@ References should be parseable even inside _<%= merge_request.to_reference %>_ e
- [[link-text|http://example.com/pdfs/gollum.pdf]]
- [[images/example.jpg]]
- [[http://example.com/images/example.jpg]]
+
+### Inline Diffs
+
+With inline diffs tags you can display {+ additions +} or [- deletions -].
+
+The wrapping tags can be either curly braces or square brackets [+ additions +] or {- deletions -}.
+
+However the wrapping tags can not be mixed as such -
+
+- {+ additions +]
+- [+ additions +}
+- {- delletions -]
+- [- delletions -}
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index e47a54fdac5..16fbb5dcecb 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -2,7 +2,7 @@ require "spec_helper"
describe AuthHelper do
describe "button_based_providers" do
- it 'returns all enabled providers' do
+ it 'returns all enabled providers from devise' do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
expect(helper.button_based_providers).to include(*[:twitter, :github])
end
@@ -17,4 +17,49 @@ describe AuthHelper do
expect(helper.button_based_providers).to eq([])
end
end
+
+ describe 'enabled_button_based_providers' do
+ before do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ end
+
+ context 'all providers are enabled to sign in' do
+ it 'returns all the enabled providers from settings' do
+ expect(helper.enabled_button_based_providers).to include('twitter', 'github')
+ end
+ end
+
+ context 'GitHub OAuth sign in is disabled from application setting' do
+ it "doesn't return github as provider" do
+ stub_application_setting(
+ disabled_oauth_sign_in_sources: ['github']
+ )
+
+ expect(helper.enabled_button_based_providers).to include('twitter')
+ expect(helper.enabled_button_based_providers).to_not include('github')
+ end
+ end
+ end
+
+ describe 'button_based_providers_enabled?' do
+ before do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ end
+
+ context 'button based providers enabled' do
+ it 'returns true' do
+ expect(helper.button_based_providers_enabled?).to be true
+ end
+ end
+
+ context 'all the button based providers are disabled via application_setting' do
+ it 'returns false' do
+ stub_application_setting(
+ disabled_oauth_sign_in_sources: ['github', 'twitter']
+ )
+
+ expect(helper.button_based_providers_enabled?).to be false
+ end
+ end
+ end
end
diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb
index b7810185d16..52764f41e0d 100644
--- a/spec/helpers/diff_helper_spec.rb
+++ b/spec/helpers/diff_helper_spec.rb
@@ -93,9 +93,9 @@ describe DiffHelper do
it "returns strings with marked inline diffs" do
marked_old_line, marked_new_line = mark_inline_diffs(old_line, new_line)
- expect(marked_old_line).to eq("abc <span class='idiff left right'>&#39;def&#39;</span>")
+ expect(marked_old_line).to eq("abc <span class='idiff left right deletion'>&#39;def&#39;</span>")
expect(marked_old_line).to be_html_safe
- expect(marked_new_line).to eq("abc <span class='idiff left right'>&quot;def&quot;</span>")
+ expect(marked_new_line).to eq("abc <span class='idiff left right addition'>&quot;def&quot;</span>")
expect(marked_new_line).to be_html_safe
end
end
diff --git a/spec/helpers/events_helper_spec.rb b/spec/helpers/events_helper_spec.rb
index e68a5ec29ab..c0d2be98e85 100644
--- a/spec/helpers/events_helper_spec.rb
+++ b/spec/helpers/events_helper_spec.rb
@@ -1,64 +1,65 @@
require 'spec_helper'
describe EventsHelper do
- include ApplicationHelper
- include GitlabMarkdownHelper
+ describe '#event_note' do
+ before do
+ allow(helper).to receive(:current_user).and_return(double)
+ end
- let(:current_user) { create(:user, email: "current@email.com") }
+ it 'should display one line of plain text without alteration' do
+ input = 'A short, plain note'
+ expect(helper.event_note(input)).to match(input)
+ expect(helper.event_note(input)).not_to match(/\.\.\.\z/)
+ end
- it 'should display one line of plain text without alteration' do
- input = 'A short, plain note'
- expect(event_note(input)).to match(input)
- expect(event_note(input)).not_to match(/\.\.\.\z/)
- end
+ it 'should display inline code' do
+ input = 'A note with `inline code`'
+ expected = 'A note with <code>inline code</code>'
- it 'should display inline code' do
- input = 'A note with `inline code`'
- expected = 'A note with <code>inline code</code>'
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should truncate a note with multiple paragraphs' do
+ input = "Paragraph 1\n\nParagraph 2"
+ expected = 'Paragraph 1...'
- it 'should truncate a note with multiple paragraphs' do
- input = "Paragraph 1\n\nParagraph 2"
- expected = 'Paragraph 1...'
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should display the first line of a code block' do
+ input = "```\nCode block\nwith two lines\n```"
+ expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
- it 'should display the first line of a code block' do
- input = "```\nCode block\nwith two lines\n```"
- expected = %r{<pre.+><code>Code block\.\.\.</code></pre>}
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
+ it 'should truncate a single long line of text' do
+ text = 'The quick brown fox jumped over the lazy dog twice' # 50 chars
+ input = text * 4
+ expected = (text * 2).sub(/.{3}/, '...')
- it 'should truncate a single long line of text' do
- text = 'The quick brown fox jumped over the lazy dog twice' # 50 chars
- input = "#{text}#{text}#{text}#{text}" # 200 chars
- expected = "#{text}#{text}".sub(/.{3}/, '...')
+ expect(helper.event_note(input)).to match(expected)
+ end
- expect(event_note(input)).to match(expected)
- end
-
- it 'should preserve a link href when link text is truncated' do
- text = 'The quick brown fox jumped over the lazy dog' # 44 chars
- input = "#{text}#{text}#{text} " # 133 chars
- link_url = 'http://example.com/foo/bar/baz' # 30 chars
- input << link_url
- expected_link_text = 'http://example...</a>'
+ it 'should preserve a link href when link text is truncated' do
+ text = 'The quick brown fox jumped over the lazy dog' # 44 chars
+ input = "#{text}#{text}#{text} " # 133 chars
+ link_url = 'http://example.com/foo/bar/baz' # 30 chars
+ input << link_url
+ expected_link_text = 'http://example...</a>'
- expect(event_note(input)).to match(link_url)
- expect(event_note(input)).to match(expected_link_text)
- end
+ expect(helper.event_note(input)).to match(link_url)
+ expect(helper.event_note(input)).to match(expected_link_text)
+ end
- it 'should preserve code color scheme' do
- input = "```ruby\ndef test\n 'hello world'\nend\n```"
- expected = '<pre class="code highlight js-syntax-highlight ruby">' \
- "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
- " <span class=\"s1\">\'hello world\'</span>\n" \
- "<span class=\"k\">end</span>" \
- '</code></pre>'
- expect(event_note(input)).to eq(expected)
+ it 'should preserve code color scheme' do
+ input = "```ruby\ndef test\n 'hello world'\nend\n```"
+ expected = '<pre class="code highlight js-syntax-highlight ruby">' \
+ "<code><span class=\"k\">def</span> <span class=\"nf\">test</span>\n" \
+ " <span class=\"s1\">\'hello world\'</span>\n" \
+ "<span class=\"k\">end</span>" \
+ '</code></pre>'
+ expect(helper.event_note(input)).to eq(expected)
+ end
end
end
diff --git a/spec/javascripts/project_title_spec.js.coffee b/spec/javascripts/project_title_spec.js.coffee
index 3d8de2ff989..1cf34d4d2d3 100644
--- a/spec/javascripts/project_title_spec.js.coffee
+++ b/spec/javascripts/project_title_spec.js.coffee
@@ -1,5 +1,6 @@
#= require bootstrap
#= require select2
+#= require lib/type_utility
#= require gl_dropdown
#= require api
#= require project_select
diff --git a/spec/lib/banzai/filter/inline_diff_filter_spec.rb b/spec/lib/banzai/filter/inline_diff_filter_spec.rb
new file mode 100644
index 00000000000..9e526371294
--- /dev/null
+++ b/spec/lib/banzai/filter/inline_diff_filter_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe Banzai::Filter::InlineDiffFilter, lib: true do
+ include FilterSpecHelper
+
+ it 'adds inline diff span tags for deletions when using square brackets' do
+ doc = "START [-something deleted-] END"
+ expect(filter(doc).to_html).to eq('START <span class="idiff left right deletion">something deleted</span> END')
+ end
+
+ it 'adds inline diff span tags for deletions when using curley braces' do
+ doc = "START {-something deleted-} END"
+ expect(filter(doc).to_html).to eq('START <span class="idiff left right deletion">something deleted</span> END')
+ end
+
+ it 'does not add inline diff span tags when a closing tag is not provided' do
+ doc = "START [- END"
+ expect(filter(doc).to_html).to eq(doc)
+ end
+
+ it 'adds inline span tags for additions when using square brackets' do
+ doc = "START [+something added+] END"
+ expect(filter(doc).to_html).to eq('START <span class="idiff left right addition">something added</span> END')
+ end
+
+ it 'adds inline span tags for additions when using curley braces' do
+ doc = "START {+something added+} END"
+ expect(filter(doc).to_html).to eq('START <span class="idiff left right addition">something added</span> END')
+ end
+
+ it 'does not add inline diff span tags when a closing addition tag is not provided' do
+ doc = "START {+ END"
+ expect(filter(doc).to_html).to eq(doc)
+ end
+
+ it 'does not add inline diff span tags when the tags do not match' do
+ examples = [
+ "{+ additions +]",
+ "[+ additions +}",
+ "{- delletions -]",
+ "[- delletions -}"
+ ]
+
+ examples.each do |doc|
+ expect(filter(doc).to_html).to eq(doc)
+ end
+ end
+
+ it 'prevents user-land html being injected' do
+ doc = "START {+&lt;script&gt;alert('I steal cookies')&lt;/script&gt;+} END"
+ expect(filter(doc).to_html).to eq("START <span class=\"idiff left right addition\">&lt;script&gt;alert('I steal cookies')&lt;/script&gt;</span> END")
+ end
+
+ it 'preserves content inside pre tags' do
+ doc = "<pre>START {+something added+} END</pre>"
+ expect(filter(doc).to_html).to eq(doc)
+ end
+
+ it 'preserves content inside code tags' do
+ doc = "<code>START {+something added+} END</code>"
+ expect(filter(doc).to_html).to eq(doc)
+ end
+
+ it 'preserves content inside tt tags' do
+ doc = "<tt>START {+something added+} END</tt>"
+ expect(filter(doc).to_html).to eq(doc)
+ end
+end
diff --git a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
index 5beb61dac5c..bdf48eabb0e 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -3,8 +3,9 @@ require 'spec_helper'
describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
include FilterSpecHelper
- let(:project) { create(:project, :public) }
- let(:milestone) { create(:milestone, project: project) }
+ let(:project) { create(:project, :public) }
+ let(:milestone) { create(:milestone, project: project) }
+ let(:reference) { milestone.to_reference }
it 'requires project context' do
expect { described_class.call('') }.to raise_error(ArgumentError, /:project/)
@@ -17,11 +18,42 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
end
end
- context 'internal reference' do
- # Convert the Markdown link to only the URL, since these tests aren't run through the regular Markdown pipeline.
- # Milestone reference behavior in the full Markdown pipeline is tested elsewhere.
- let(:reference) { milestone.to_reference.gsub(/\[([^\]]+)\]\(([^)]+)\)/, '\2') }
+ it 'includes default classes' do
+ doc = reference_filter("Milestone #{reference}")
+ expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-milestone'
+ end
+
+ it 'includes a data-project attribute' do
+ doc = reference_filter("Milestone #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-project')
+ expect(link.attr('data-project')).to eq project.id.to_s
+ end
+
+ it 'includes a data-milestone attribute' do
+ doc = reference_filter("See #{reference}")
+ link = doc.css('a').first
+
+ expect(link).to have_attribute('data-milestone')
+ expect(link.attr('data-milestone')).to eq milestone.id.to_s
+ end
+
+ it 'supports an :only_path context' do
+ doc = reference_filter("Milestone #{reference}", only_path: true)
+ link = doc.css('a').first.attr('href')
+ expect(link).not_to match %r(https?://)
+ expect(link).to eq urls.
+ namespace_project_milestone_path(project.namespace, project, milestone)
+ end
+
+ it 'adds to the results hash' do
+ result = reference_pipeline_result("Milestone #{reference}")
+ expect(result[:references][:milestone]).to eq [milestone]
+ end
+
+ context 'Integer-based references' do
it 'links to a valid reference' do
doc = reference_filter("See #{reference}")
@@ -30,29 +62,82 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
end
it 'links with adjacent text' do
- doc = reference_filter("milestone (#{reference}.)")
- expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(milestone.title)}<\/a>\.\)/)
+ doc = reference_filter("Milestone (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+>#{milestone.name}</a>\.\)))
end
- it 'includes a title attribute' do
- doc = reference_filter("milestone #{reference}")
- expect(doc.css('a').first.attr('title')).to eq "Milestone: #{milestone.title}"
+ it 'ignores invalid milestone IIDs' do
+ exp = act = "Milestone #{invalidate_reference(reference)}"
+
+ expect(reference_filter(act).to_html).to eq exp
end
+ end
+
+ context 'String-based single-word references' do
+ let(:milestone) { create(:milestone, name: 'gfm', project: project) }
+ let(:reference) { "#{Milestone.reference_prefix}#{milestone.name}" }
- it 'escapes the title attribute' do
- milestone.update_attribute(:title, %{"></a>whatever<a title="})
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_milestone_url(project.namespace, project, milestone)
+ expect(doc.text).to eq 'See gfm'
+ end
- doc = reference_filter("milestone #{reference}")
- expect(doc.text).to eq "milestone \">whatever"
+ it 'links with adjacent text' do
+ doc = reference_filter("Milestone (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+>#{milestone.name}</a>\.\)))
end
- it 'includes default classes' do
- doc = reference_filter("milestone #{reference}")
- expect(doc.css('a').first.attr('class')).to eq 'gfm gfm-milestone'
+ it 'ignores invalid milestone names' do
+ exp = act = "Milestone #{Milestone.reference_prefix}#{milestone.name.reverse}"
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ context 'String-based multi-word references in quotes' do
+ let(:milestone) { create(:milestone, name: 'gfm references', project: project) }
+ let(:reference) { milestone.to_reference(format: :name) }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_milestone_url(project.namespace, project, milestone)
+ expect(doc.text).to eq 'See gfm references'
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Milestone (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+>#{milestone.name}</a>\.\)))
+ end
+
+ it 'ignores invalid milestone names' do
+ exp = act = %(Milestone #{Milestone.reference_prefix}"#{milestone.name.reverse}")
+
+ expect(reference_filter(act).to_html).to eq exp
+ end
+ end
+
+ describe 'referencing a milestone in a link href' do
+ let(:reference) { %Q{<a href="#{milestone.to_reference}">Milestone</a>} }
+
+ it 'links to a valid reference' do
+ doc = reference_filter("See #{reference}")
+
+ expect(doc.css('a').first.attr('href')).to eq urls.
+ namespace_project_milestone_url(project.namespace, project, milestone)
+ end
+
+ it 'links with adjacent text' do
+ doc = reference_filter("Milestone (#{reference}.)")
+ expect(doc.to_html).to match(%r(\(<a.+>Milestone</a>\.\)))
end
it 'includes a data-project attribute' do
- doc = reference_filter("milestone #{reference}")
+ doc = reference_filter("Milestone #{reference}")
link = doc.css('a').first
expect(link).to have_attribute('data-project')
@@ -68,8 +153,34 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do
end
it 'adds to the results hash' do
- result = reference_pipeline_result("milestone #{reference}")
+ result = reference_pipeline_result("Milestone #{reference}")
expect(result[:references][:milestone]).to eq [milestone]
end
end
+
+ describe 'cross project milestone references' do
+ let(:another_project) { create(:empty_project, :public) }
+ let(:project_path) { another_project.path_with_namespace }
+ let(:milestone) { create(:milestone, project: another_project) }
+ let(:reference) { milestone.to_reference(project) }
+
+ let!(:result) { reference_filter("See #{reference}") }
+
+ it 'points to referenced project milestone page' do
+ expect(result.css('a').first.attr('href')).to eq urls.
+ namespace_project_milestone_url(another_project.namespace,
+ another_project,
+ milestone)
+ end
+
+ it 'contains cross project content' do
+ expect(result.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+ end
+
+ it 'escapes the name attribute' do
+ allow_any_instance_of(Milestone).to receive(:title).and_return(%{"></a>whatever<a title="})
+ doc = reference_filter("See #{reference}")
+ expect(doc.css('a').first.text).to eq "#{milestone.name} in #{project_path}"
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/wiki_link_filter_spec.rb b/spec/lib/banzai/filter/wiki_link_filter_spec.rb
new file mode 100644
index 00000000000..185abbb2108
--- /dev/null
+++ b/spec/lib/banzai/filter/wiki_link_filter_spec.rb
@@ -0,0 +1,85 @@
+require 'spec_helper'
+
+describe Banzai::Filter::WikiLinkFilter, lib: true do
+ include FilterSpecHelper
+
+ let(:namespace) { build_stubbed(:namespace, name: "wiki_link_ns") }
+ let(:project) { build_stubbed(:empty_project, :public, name: "wiki_link_project", namespace: namespace) }
+ let(:user) { double }
+ let(:project_wiki) { ProjectWiki.new(project, user) }
+
+ describe "links within the wiki (relative)" do
+ describe "hierarchical links to the current directory" do
+ it "doesn't rewrite non-file links" do
+ link = "<a href='./page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('./page')
+ end
+
+ it "doesn't rewrite file links" do
+ link = "<a href='./page.md'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('./page.md')
+ end
+ end
+
+ describe "hierarchical links to the parent directory" do
+ it "doesn't rewrite non-file links" do
+ link = "<a href='../page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('../page')
+ end
+
+ it "doesn't rewrite file links" do
+ link = "<a href='../page.md'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('../page.md')
+ end
+ end
+
+ describe "hierarchical links to a sub-directory" do
+ it "doesn't rewrite non-file links" do
+ link = "<a href='./subdirectory/page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('./subdirectory/page')
+ end
+
+ it "doesn't rewrite file links" do
+ link = "<a href='./subdirectory/page.md'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('./subdirectory/page.md')
+ end
+ end
+
+ describe "non-hierarchical links" do
+ it 'rewrites non-file links to be at the scope of the wiki root' do
+ link = "<a href='page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to match('/wiki_link_ns/wiki_link_project/wikis/page')
+ end
+
+ it "doesn't rewrite file links" do
+ link = "<a href='page.md'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('page.md')
+ end
+ end
+ end
+
+ describe "links outside the wiki (absolute)" do
+ it "doesn't rewrite links" do
+ link = "<a href='http://example.com/page'>Link to Page</a>"
+ filtered_link = filter(link, project_wiki: project_wiki).children[0]
+
+ expect(filtered_link.attribute('href').value).to eq('http://example.com/page')
+ end
+ end
+end
diff --git a/spec/lib/ci/ansi2html_spec.rb b/spec/lib/ci/ansi2html_spec.rb
index 3a2b568f4c7..04afbd06929 100644
--- a/spec/lib/ci/ansi2html_spec.rb
+++ b/spec/lib/ci/ansi2html_spec.rb
@@ -4,131 +4,176 @@ describe Ci::Ansi2html, lib: true do
subject { Ci::Ansi2html }
it "prints non-ansi as-is" do
- expect(subject.convert("Hello")).to eq('Hello')
+ expect(subject.convert("Hello")[:html]).to eq('Hello')
end
it "strips non-color-changing controll sequences" do
- expect(subject.convert("Hello \e[2Kworld")).to eq('Hello world')
+ expect(subject.convert("Hello \e[2Kworld")[:html]).to eq('Hello world')
end
it "prints simply red" do
- expect(subject.convert("\e[31mHello\e[0m")).to eq('<span class="term-fg-red">Hello</span>')
+ expect(subject.convert("\e[31mHello\e[0m")[:html]).to eq('<span class="term-fg-red">Hello</span>')
end
it "prints simply red without trailing reset" do
- expect(subject.convert("\e[31mHello")).to eq('<span class="term-fg-red">Hello</span>')
+ expect(subject.convert("\e[31mHello")[:html]).to eq('<span class="term-fg-red">Hello</span>')
end
it "prints simply yellow" do
- expect(subject.convert("\e[33mHello\e[0m")).to eq('<span class="term-fg-yellow">Hello</span>')
+ expect(subject.convert("\e[33mHello\e[0m")[:html]).to eq('<span class="term-fg-yellow">Hello</span>')
end
it "prints default on blue" do
- expect(subject.convert("\e[39;44mHello")).to eq('<span class="term-bg-blue">Hello</span>')
+ expect(subject.convert("\e[39;44mHello")[:html]).to eq('<span class="term-bg-blue">Hello</span>')
end
it "prints red on blue" do
- expect(subject.convert("\e[31;44mHello")).to eq('<span class="term-fg-red term-bg-blue">Hello</span>')
+ expect(subject.convert("\e[31;44mHello")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span>')
end
it "resets colors after red on blue" do
- expect(subject.convert("\e[31;44mHello\e[0m world")).to eq('<span class="term-fg-red term-bg-blue">Hello</span> world')
+ expect(subject.convert("\e[31;44mHello\e[0m world")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span> world')
end
it "performs color change from red/blue to yellow/blue" do
- expect(subject.convert("\e[31;44mHello \e[33mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-blue">world</span>')
+ expect(subject.convert("\e[31;44mHello \e[33mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-blue">world</span>')
end
it "performs color change from red/blue to yellow/green" do
- expect(subject.convert("\e[31;44mHello \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-green">world</span>')
+ expect(subject.convert("\e[31;44mHello \e[33;42mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-fg-yellow term-bg-green">world</span>')
end
it "performs color change from red/blue to reset to yellow/green" do
- expect(subject.convert("\e[31;44mHello\e[0m \e[33;42mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello</span> <span class="term-fg-yellow term-bg-green">world</span>')
+ expect(subject.convert("\e[31;44mHello\e[0m \e[33;42mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello</span> <span class="term-fg-yellow term-bg-green">world</span>')
end
it "ignores unsupported codes" do
- expect(subject.convert("\e[51mHello\e[0m")).to eq('Hello')
+ expect(subject.convert("\e[51mHello\e[0m")[:html]).to eq('Hello')
end
it "prints light red" do
- expect(subject.convert("\e[91mHello\e[0m")).to eq('<span class="term-fg-l-red">Hello</span>')
+ expect(subject.convert("\e[91mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red">Hello</span>')
end
it "prints default on light red" do
- expect(subject.convert("\e[101mHello\e[0m")).to eq('<span class="term-bg-l-red">Hello</span>')
+ expect(subject.convert("\e[101mHello\e[0m")[:html]).to eq('<span class="term-bg-l-red">Hello</span>')
end
it "performs color change from red/blue to default/blue" do
- expect(subject.convert("\e[31;44mHello \e[39mworld")).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
+ expect(subject.convert("\e[31;44mHello \e[39mworld")[:html]).to eq('<span class="term-fg-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
end
it "performs color change from light red/blue to default/blue" do
- expect(subject.convert("\e[91;44mHello \e[39mworld")).to eq('<span class="term-fg-l-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
+ expect(subject.convert("\e[91;44mHello \e[39mworld")[:html]).to eq('<span class="term-fg-l-red term-bg-blue">Hello </span><span class="term-bg-blue">world</span>')
end
it "prints bold text" do
- expect(subject.convert("\e[1mHello")).to eq('<span class="term-bold">Hello</span>')
+ expect(subject.convert("\e[1mHello")[:html]).to eq('<span class="term-bold">Hello</span>')
end
it "resets bold text" do
- expect(subject.convert("\e[1mHello\e[21m world")).to eq('<span class="term-bold">Hello</span> world')
- expect(subject.convert("\e[1mHello\e[22m world")).to eq('<span class="term-bold">Hello</span> world')
+ expect(subject.convert("\e[1mHello\e[21m world")[:html]).to eq('<span class="term-bold">Hello</span> world')
+ expect(subject.convert("\e[1mHello\e[22m world")[:html]).to eq('<span class="term-bold">Hello</span> world')
end
it "prints italic text" do
- expect(subject.convert("\e[3mHello")).to eq('<span class="term-italic">Hello</span>')
+ expect(subject.convert("\e[3mHello")[:html]).to eq('<span class="term-italic">Hello</span>')
end
it "resets italic text" do
- expect(subject.convert("\e[3mHello\e[23m world")).to eq('<span class="term-italic">Hello</span> world')
+ expect(subject.convert("\e[3mHello\e[23m world")[:html]).to eq('<span class="term-italic">Hello</span> world')
end
it "prints underlined text" do
- expect(subject.convert("\e[4mHello")).to eq('<span class="term-underline">Hello</span>')
+ expect(subject.convert("\e[4mHello")[:html]).to eq('<span class="term-underline">Hello</span>')
end
it "resets underlined text" do
- expect(subject.convert("\e[4mHello\e[24m world")).to eq('<span class="term-underline">Hello</span> world')
+ expect(subject.convert("\e[4mHello\e[24m world")[:html]).to eq('<span class="term-underline">Hello</span> world')
end
it "prints concealed text" do
- expect(subject.convert("\e[8mHello")).to eq('<span class="term-conceal">Hello</span>')
+ expect(subject.convert("\e[8mHello")[:html]).to eq('<span class="term-conceal">Hello</span>')
end
it "resets concealed text" do
- expect(subject.convert("\e[8mHello\e[28m world")).to eq('<span class="term-conceal">Hello</span> world')
+ expect(subject.convert("\e[8mHello\e[28m world")[:html]).to eq('<span class="term-conceal">Hello</span> world')
end
it "prints crossed-out text" do
- expect(subject.convert("\e[9mHello")).to eq('<span class="term-cross">Hello</span>')
+ expect(subject.convert("\e[9mHello")[:html]).to eq('<span class="term-cross">Hello</span>')
end
it "resets crossed-out text" do
- expect(subject.convert("\e[9mHello\e[29m world")).to eq('<span class="term-cross">Hello</span> world')
+ expect(subject.convert("\e[9mHello\e[29m world")[:html]).to eq('<span class="term-cross">Hello</span> world')
end
it "can print 256 xterm fg colors" do
- expect(subject.convert("\e[38;5;16mHello")).to eq('<span class="xterm-fg-16">Hello</span>')
+ expect(subject.convert("\e[38;5;16mHello")[:html]).to eq('<span class="xterm-fg-16">Hello</span>')
end
it "can print 256 xterm fg colors on normal magenta background" do
- expect(subject.convert("\e[38;5;16;45mHello")).to eq('<span class="xterm-fg-16 term-bg-magenta">Hello</span>')
+ expect(subject.convert("\e[38;5;16;45mHello")[:html]).to eq('<span class="xterm-fg-16 term-bg-magenta">Hello</span>')
end
it "can print 256 xterm bg colors" do
- expect(subject.convert("\e[48;5;240mHello")).to eq('<span class="xterm-bg-240">Hello</span>')
+ expect(subject.convert("\e[48;5;240mHello")[:html]).to eq('<span class="xterm-bg-240">Hello</span>')
end
it "can print 256 xterm bg colors on normal magenta foreground" do
- expect(subject.convert("\e[48;5;16;35mHello")).to eq('<span class="term-fg-magenta xterm-bg-16">Hello</span>')
+ expect(subject.convert("\e[48;5;16;35mHello")[:html]).to eq('<span class="term-fg-magenta xterm-bg-16">Hello</span>')
end
it "prints bold colored text vividly" do
- expect(subject.convert("\e[1;31mHello\e[0m")).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
+ expect(subject.convert("\e[1;31mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
end
it "prints bold light colored text correctly" do
- expect(subject.convert("\e[1;91mHello\e[0m")).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
+ expect(subject.convert("\e[1;91mHello\e[0m")[:html]).to eq('<span class="term-fg-l-red term-bold">Hello</span>')
+ end
+
+ it "prints &lt;" do
+ expect(subject.convert("<")[:html]).to eq('&lt;')
+ end
+
+ describe "incremental update" do
+ shared_examples 'stateable converter' do
+ let(:pass1) { subject.convert(pre_text) }
+ let(:pass2) { subject.convert(pre_text + text, pass1[:state]) }
+
+ it "to returns html to append" do
+ expect(pass2[:append]).to be_truthy
+ expect(pass2[:html]).to eq(html)
+ expect(pass1[:text] + pass2[:text]).to eq(pre_text + text)
+ expect(pass1[:html] + pass2[:html]).to eq(pre_html + html)
+ end
+ end
+
+ context "with split word" do
+ let(:pre_text) { "\e[1mHello" }
+ let(:pre_html) { "<span class=\"term-bold\">Hello</span>" }
+ let(:text) { "\e[1mWorld" }
+ let(:html) { "<span class=\"term-bold\"></span><span class=\"term-bold\">World</span>" }
+
+ it_behaves_like 'stateable converter'
+ end
+
+ context "with split sequence" do
+ let(:pre_text) { "\e[1m" }
+ let(:pre_html) { "<span class=\"term-bold\"></span>" }
+ let(:text) { "Hello" }
+ let(:html) { "<span class=\"term-bold\">Hello</span>" }
+
+ it_behaves_like 'stateable converter'
+ end
+
+ context "with partial sequence" do
+ let(:pre_text) { "Hello\e" }
+ let(:pre_html) { "Hello" }
+ let(:text) { "[1m World" }
+ let(:html) { "<span class=\"term-bold\"> World</span>" }
+
+ it_behaves_like 'stateable converter'
+ end
end
end
diff --git a/spec/lib/ci/charts_spec.rb b/spec/lib/ci/charts_spec.rb
index 50a77308cde..9d1215a5760 100644
--- a/spec/lib/ci/charts_spec.rb
+++ b/spec/lib/ci/charts_spec.rb
@@ -12,5 +12,12 @@ describe Ci::Charts, lib: true do
chart = Ci::Charts::BuildTime.new(@commit.project)
expect(chart.build_times).to eq([2])
end
+
+ it 'should handle nil build times' do
+ create(:ci_commit, duration: nil, project: @commit.project)
+
+ chart = Ci::Charts::BuildTime.new(@commit.project)
+ expect(chart.build_times).to eq([2, 0])
+ end
end
end
diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
index c7ab3185378..9eef8ea0976 100644
--- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
+++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb
@@ -443,12 +443,12 @@ module Ci
context 'when job variables are defined' do
context 'when syntax is correct' do
it 'returns job variables' do
- variables = {
+ variables = {
KEY1: 'value1',
SOME_KEY_2: 'value2'
}
- config = YAML.dump(
+ config = YAML.dump(
{ before_script: ['pwd'],
rspec: {
variables: variables,
diff --git a/spec/lib/container_registry/blob_spec.rb b/spec/lib/container_registry/blob_spec.rb
new file mode 100644
index 00000000000..4d8cb787dde
--- /dev/null
+++ b/spec/lib/container_registry/blob_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+
+describe ContainerRegistry::Blob do
+ let(:digest) { 'sha256:0123456789012345' }
+ let(:config) do
+ {
+ 'digest' => digest,
+ 'mediaType' => 'binary',
+ 'size' => 1000
+ }
+ end
+
+ let(:registry) { ContainerRegistry::Registry.new('http://example.com') }
+ let(:repository) { registry.repository('group/test') }
+ let(:blob) { repository.blob(config) }
+
+ it { expect(blob).to respond_to(:repository) }
+ it { expect(blob).to delegate_method(:registry).to(:repository) }
+ it { expect(blob).to delegate_method(:client).to(:repository) }
+
+ context '#path' do
+ subject { blob.path }
+
+ it { is_expected.to eq('example.com/group/test@sha256:0123456789012345') }
+ end
+
+ context '#digest' do
+ subject { blob.digest }
+
+ it { is_expected.to eq(digest) }
+ end
+
+ context '#type' do
+ subject { blob.type }
+
+ it { is_expected.to eq('binary') }
+ end
+
+ context '#revision' do
+ subject { blob.revision }
+
+ it { is_expected.to eq('0123456789012345') }
+ end
+
+ context '#short_revision' do
+ subject { blob.short_revision }
+
+ it { is_expected.to eq('012345678') }
+ end
+
+ context '#delete' do
+ before do
+ stub_request(:delete, 'http://example.com/v2/group/test/blobs/sha256:0123456789012345').
+ to_return(status: 200)
+ end
+
+ subject { blob.delete }
+
+ it { is_expected.to be_truthy }
+ end
+end
diff --git a/spec/lib/container_registry/registry_spec.rb b/spec/lib/container_registry/registry_spec.rb
new file mode 100644
index 00000000000..2638401ae6e
--- /dev/null
+++ b/spec/lib/container_registry/registry_spec.rb
@@ -0,0 +1,28 @@
+require 'spec_helper'
+
+describe ContainerRegistry::Registry do
+ let(:path) { nil }
+ let(:registry) { described_class.new('http://example.com', path: path) }
+
+ subject { registry }
+
+ it { is_expected.to respond_to(:client) }
+ it { is_expected.to respond_to(:uri) }
+ it { is_expected.to respond_to(:path) }
+
+ it { expect(subject.repository('test')).to_not be_nil }
+
+ context '#path' do
+ subject { registry.path }
+
+ context 'path from URL' do
+ it { is_expected.to eq('example.com') }
+ end
+
+ context 'custom path' do
+ let(:path) { 'registry.example.com' }
+
+ it { is_expected.to eq(path) }
+ end
+ end
+end
diff --git a/spec/lib/container_registry/repository_spec.rb b/spec/lib/container_registry/repository_spec.rb
new file mode 100644
index 00000000000..e6d66b11e4e
--- /dev/null
+++ b/spec/lib/container_registry/repository_spec.rb
@@ -0,0 +1,65 @@
+require 'spec_helper'
+
+describe ContainerRegistry::Repository do
+ let(:registry) { ContainerRegistry::Registry.new('http://example.com') }
+ let(:repository) { registry.repository('group/test') }
+
+ it { expect(repository).to respond_to(:registry) }
+ it { expect(repository).to delegate_method(:client).to(:registry) }
+ it { expect(repository.tag('test')).to_not be_nil }
+
+ context '#path' do
+ subject { repository.path }
+
+ it { is_expected.to eq('example.com/group/test') }
+ end
+
+ context 'manifest processing' do
+ before do
+ stub_request(:get, 'http://example.com/v2/group/test/tags/list').
+ with(headers: { 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json' }).
+ to_return(
+ status: 200,
+ body: JSON.dump(tags: ['test']),
+ headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
+ end
+
+ context '#manifest' do
+ subject { repository.manifest }
+
+ it { is_expected.to_not be_nil }
+ end
+
+ context '#valid?' do
+ subject { repository.valid? }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context '#tags' do
+ subject { repository.tags }
+
+ it { is_expected.to_not be_empty }
+ end
+ end
+
+ context '#delete_tags' do
+ let(:tag) { ContainerRegistry::Tag.new(repository, 'tag') }
+
+ before { expect(repository).to receive(:tags).twice.and_return([tag]) }
+
+ subject { repository.delete_tags }
+
+ context 'succeeds' do
+ before { expect(tag).to receive(:delete).and_return(true) }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'any fails' do
+ before { expect(tag).to receive(:delete).and_return(false) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+end
diff --git a/spec/lib/container_registry/tag_spec.rb b/spec/lib/container_registry/tag_spec.rb
new file mode 100644
index 00000000000..12cf91127ed
--- /dev/null
+++ b/spec/lib/container_registry/tag_spec.rb
@@ -0,0 +1,89 @@
+require 'spec_helper'
+
+describe ContainerRegistry::Tag do
+ let(:registry) { ContainerRegistry::Registry.new('http://example.com') }
+ let(:repository) { registry.repository('group/test') }
+ let(:tag) { repository.tag('tag') }
+ let(:headers) { { 'Accept' => 'application/vnd.docker.distribution.manifest.v2+json' } }
+
+ it { expect(tag).to respond_to(:repository) }
+ it { expect(tag).to delegate_method(:registry).to(:repository) }
+ it { expect(tag).to delegate_method(:client).to(:repository) }
+
+ context '#path' do
+ subject { tag.path }
+
+ it { is_expected.to eq('example.com/group/test:tag') }
+ end
+
+ context 'manifest processing' do
+ before do
+ stub_request(:get, 'http://example.com/v2/group/test/manifests/tag').
+ with(headers: headers).
+ to_return(
+ status: 200,
+ body: File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json'),
+ headers: { 'Content-Type' => 'application/vnd.docker.distribution.manifest.v2+json' })
+ end
+
+ context '#layers' do
+ subject { tag.layers }
+
+ it { expect(subject.length).to eq(1) }
+ end
+
+ context '#total_size' do
+ subject { tag.total_size }
+
+ it { is_expected.to eq(2319870) }
+ end
+
+ context 'config processing' do
+ before do
+ stub_request(:get, 'http://example.com/v2/group/test/blobs/sha256:d7a513a663c1a6dcdba9ed832ca53c02ac2af0c333322cd6ca92936d1d9917ac').
+ with(headers: { 'Accept' => 'application/octet-stream' }).
+ to_return(
+ status: 200,
+ body: File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json'))
+ end
+
+ context '#config' do
+ subject { tag.config }
+
+ it { is_expected.to_not be_nil }
+ end
+
+ context '#created_at' do
+ subject { tag.created_at }
+
+ it { is_expected.to_not be_nil }
+ end
+ end
+ end
+
+ context 'manifest digest' do
+ before do
+ stub_request(:head, 'http://example.com/v2/group/test/manifests/tag').
+ with(headers: headers).
+ to_return(status: 200, headers: { 'Docker-Content-Digest' => 'sha256:digest' })
+ end
+
+ context '#digest' do
+ subject { tag.digest }
+
+ it { is_expected.to eq('sha256:digest') }
+ end
+
+ context '#delete' do
+ before do
+ stub_request(:delete, 'http://example.com/v2/group/test/manifests/sha256:digest').
+ with(headers: headers).
+ to_return(status: 200)
+ end
+
+ subject { tag.delete }
+
+ it { is_expected.to be_truthy }
+ end
+ end
+end
diff --git a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
index acca0b08bab..46a5b7fce65 100644
--- a/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
+++ b/spec/lib/gitlab/ci/build/artifacts/metadata/entry_spec.rb
@@ -10,8 +10,8 @@ describe Gitlab::Ci::Build::Artifacts::Metadata::Entry do
'path/dir_1/subdir/subfile' => { size: 10 },
'path/second_dir' => {},
'path/second_dir/dir_3/file_2' => { size: 10 },
- 'path/second_dir/dir_3/file_3'=> { size: 10 },
- 'another_directory/'=> {},
+ 'path/second_dir/dir_3/file_3' => { size: 10 },
+ 'another_directory/' => {},
'another_file' => {},
'/file/with/absolute_path' => {} }
end
diff --git a/spec/lib/gitlab/database/migration_helpers_spec.rb b/spec/lib/gitlab/database/migration_helpers_spec.rb
new file mode 100644
index 00000000000..35ade7a2be0
--- /dev/null
+++ b/spec/lib/gitlab/database/migration_helpers_spec.rb
@@ -0,0 +1,128 @@
+require 'spec_helper'
+
+describe Gitlab::Database::MigrationHelpers, lib: true do
+ let(:model) do
+ ActiveRecord::Migration.new.extend(
+ Gitlab::Database::MigrationHelpers
+ )
+ end
+
+ before { allow(model).to receive(:puts) }
+
+ describe '#add_concurrent_index' do
+ context 'outside a transaction' do
+ before do
+ expect(model).to receive(:transaction_open?).and_return(false)
+ end
+
+ context 'using PostgreSQL' do
+ it 'creates the index concurrently' do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(true)
+
+ expect(model).to receive(:add_index).
+ with(:users, :foo, algorithm: :concurrently)
+
+ model.add_concurrent_index(:users, :foo)
+ end
+ end
+
+ context 'using MySQL' do
+ it 'creates a regular index' do
+ expect(Gitlab::Database).to receive(:postgresql?).and_return(false)
+
+ expect(model).to receive(:add_index).
+ with(:users, :foo)
+
+ model.add_concurrent_index(:users, :foo)
+ end
+ end
+ end
+
+ context 'inside a transaction' do
+ it 'raises RuntimeError' do
+ expect(model).to receive(:transaction_open?).and_return(true)
+
+ expect { model.add_concurrent_index(:users, :foo) }.
+ to raise_error(RuntimeError)
+ end
+ end
+ end
+
+ describe '#update_column_in_batches' do
+ before do
+ create_list(:empty_project, 5)
+ end
+
+ it 'updates all the rows in a table' do
+ model.update_column_in_batches(:projects, :import_error, 'foo')
+
+ expect(Project.where(import_error: 'foo').count).to eq(5)
+ end
+
+ it 'updates boolean values correctly' do
+ model.update_column_in_batches(:projects, :archived, true)
+
+ expect(Project.where(archived: true).count).to eq(5)
+ end
+ end
+
+ describe '#add_column_with_default' do
+ context 'outside of a transaction' do
+ before do
+ expect(model).to receive(:transaction_open?).and_return(false)
+
+ expect(model).to receive(:transaction).twice.and_yield
+
+ expect(model).to receive(:add_column).
+ with(:projects, :foo, :integer, default: nil)
+
+ expect(model).to receive(:change_column_default).
+ with(:projects, :foo, 10)
+ end
+
+ it 'adds the column while allowing NULL values' do
+ expect(model).to receive(:update_column_in_batches).
+ with(:projects, :foo, 10)
+
+ expect(model).not_to receive(:change_column_null)
+
+ model.add_column_with_default(:projects, :foo, :integer,
+ default: 10,
+ allow_null: true)
+ end
+
+ it 'adds the column while not allowing NULL values' do
+ expect(model).to receive(:update_column_in_batches).
+ with(:projects, :foo, 10)
+
+ expect(model).to receive(:change_column_null).
+ with(:projects, :foo, false)
+
+ model.add_column_with_default(:projects, :foo, :integer, default: 10)
+ end
+
+ it 'removes the added column whenever updating the rows fails' do
+ expect(model).to receive(:update_column_in_batches).
+ with(:projects, :foo, 10).
+ and_raise(RuntimeError)
+
+ expect(model).to receive(:remove_column).
+ with(:projects, :foo)
+
+ expect do
+ model.add_column_with_default(:projects, :foo, :integer, default: 10)
+ end.to raise_error(RuntimeError)
+ end
+ end
+
+ context 'inside a transaction' do
+ it 'raises RuntimeError' do
+ expect(model).to receive(:transaction_open?).and_return(true)
+
+ expect do
+ model.add_column_with_default(:projects, :foo, :integer, default: 10)
+ end.to raise_error(RuntimeError)
+ end
+ end
+ end
+end
diff --git a/spec/lib/gitlab/email/message/repository_push_spec.rb b/spec/lib/gitlab/email/message/repository_push_spec.rb
index b2d7a799810..c19f33e2224 100644
--- a/spec/lib/gitlab/email/message/repository_push_spec.rb
+++ b/spec/lib/gitlab/email/message/repository_push_spec.rb
@@ -8,7 +8,7 @@ describe Gitlab::Email::Message::RepositoryPush do
let!(:author) { create(:author, name: 'Author') }
let(:message) do
- described_class.new(Notify, project.id, 'recipient@example.com', opts)
+ described_class.new(Notify, project.id, opts)
end
context 'new commits have been pushed to repository' do
@@ -57,7 +57,7 @@ describe Gitlab::Email::Message::RepositoryPush do
describe '#diffs' do
subject { message.diffs }
- it { is_expected.to all(be_an_instance_of Gitlab::Git::Diff) }
+ it { is_expected.to all(be_an_instance_of Gitlab::Diff::File) }
end
describe '#diffs_count' do
diff --git a/spec/lib/gitlab/github_import/branch_formatter_spec.rb b/spec/lib/gitlab/github_import/branch_formatter_spec.rb
new file mode 100644
index 00000000000..3cb634ba010
--- /dev/null
+++ b/spec/lib/gitlab/github_import/branch_formatter_spec.rb
@@ -0,0 +1,71 @@
+require 'spec_helper'
+
+describe Gitlab::GithubImport::BranchFormatter, lib: true do
+ let(:project) { create(:project) }
+ let(:repo) { double }
+ let(:raw) do
+ {
+ ref: 'feature',
+ repo: repo,
+ sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b'
+ }
+ end
+
+ describe '#exists?' do
+ it 'returns true when branch exists' do
+ branch = described_class.new(project, double(raw))
+
+ expect(branch.exists?).to eq true
+ end
+
+ it 'returns false when branch does not exist' do
+ branch = described_class.new(project, double(raw.merge(ref: 'removed-branch')))
+
+ expect(branch.exists?).to eq false
+ end
+ end
+
+ describe '#name' do
+ it 'returns raw ref when branch exists' do
+ branch = described_class.new(project, double(raw))
+
+ expect(branch.name).to eq 'feature'
+ end
+
+ it 'returns formatted ref when branch does not exist' do
+ branch = described_class.new(project, double(raw.merge(ref: 'removed-branch')))
+
+ expect(branch.name).to eq 'removed-branch-2e5d3239'
+ end
+ end
+
+ describe '#repo' do
+ it 'returns raw repo' do
+ branch = described_class.new(project, double(raw))
+
+ expect(branch.repo).to eq repo
+ end
+ end
+
+ describe '#sha' do
+ it 'returns raw sha' do
+ branch = described_class.new(project, double(raw))
+
+ expect(branch.sha).to eq '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b'
+ end
+ end
+
+ describe '#valid?' do
+ it 'returns true when repository exists' do
+ branch = described_class.new(project, double(raw))
+
+ expect(branch.valid?).to eq true
+ end
+
+ it 'returns false when repository does not exist' do
+ branch = described_class.new(project, double(raw.merge(repo: nil)))
+
+ expect(branch.valid?).to eq false
+ end
+ end
+end
diff --git a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
index e59c0ca110e..120f59e6e71 100644
--- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
+++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb
@@ -4,9 +4,9 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:project) { create(:project) }
let(:repository) { double(id: 1, fork: false) }
let(:source_repo) { repository }
- let(:source_branch) { double(ref: 'feature', repo: source_repo) }
+ let(:source_branch) { double(ref: 'feature', repo: source_repo, sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b') }
let(:target_repo) { repository }
- let(:target_branch) { double(ref: 'master', repo: target_repo) }
+ let(:target_branch) { double(ref: 'master', repo: target_repo, sha: '8ffb3c15a5475e59ae909384297fede4badcb4c7') }
let(:octocat) { double(id: 123456, login: 'octocat') }
let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') }
let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') }
@@ -41,8 +41,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project,
source_branch: 'feature',
+ head_source_sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b',
target_project: project,
target_branch: 'master',
+ base_target_sha: '8ffb3c15a5475e59ae909384297fede4badcb4c7',
state: 'opened',
milestone: nil,
author_id: project.creator_id,
@@ -66,8 +68,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project,
source_branch: 'feature',
+ head_source_sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b',
target_project: project,
target_branch: 'master',
+ base_target_sha: '8ffb3c15a5475e59ae909384297fede4badcb4c7',
state: 'closed',
milestone: nil,
author_id: project.creator_id,
@@ -91,8 +95,10 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
description: "*Created by: octocat*\n\nPlease pull these awesome changes",
source_project: project,
source_branch: 'feature',
+ head_source_sha: '2e5d3239642f9161dcbbc4b70a211a68e5e45e2b',
target_project: project,
target_branch: 'master',
+ base_target_sha: '8ffb3c15a5475e59ae909384297fede4badcb4c7',
state: 'merged',
milestone: nil,
author_id: project.creator_id,
@@ -137,11 +143,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
let(:milestone) { double(number: 45) }
let(:raw_data) { double(base_data.merge(milestone: milestone)) }
- it 'returns nil when milestone does not exists' do
+ it 'returns nil when milestone does not exist' do
expect(pull_request.attributes.fetch(:milestone)).to be_nil
end
- it 'returns milestone when is exists' do
+ it 'returns milestone when it exists' do
milestone = create(:milestone, project: project, iid: 45)
expect(pull_request.attributes.fetch(:milestone)).to eq milestone
@@ -158,36 +164,16 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
describe '#valid?' do
- let(:invalid_branch) { double(ref: 'invalid-branch').as_null_object }
-
- context 'when source, and target repositories are the same' do
- context 'and source and target branches exists' do
- let(:raw_data) { double(base_data.merge(head: source_branch, base: target_branch)) }
-
- it 'returns true' do
- expect(pull_request.valid?).to eq true
- end
- end
-
- context 'and source branch doesn not exists' do
- let(:raw_data) { double(base_data.merge(head: invalid_branch, base: target_branch)) }
-
- it 'returns false' do
- expect(pull_request.valid?).to eq false
- end
- end
-
- context 'and target branch doesn not exists' do
- let(:raw_data) { double(base_data.merge(head: source_branch, base: invalid_branch)) }
+ context 'when source, and target repos are not a fork' do
+ let(:raw_data) { double(base_data) }
- it 'returns false' do
- expect(pull_request.valid?).to eq false
- end
+ it 'returns true' do
+ expect(pull_request.valid?).to eq true
end
end
context 'when source repo is a fork' do
- let(:source_repo) { double(id: 2, fork: true) }
+ let(:source_repo) { double(id: 2) }
let(:raw_data) { double(base_data) }
it 'returns false' do
@@ -196,7 +182,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do
end
context 'when target repo is a fork' do
- let(:target_repo) { double(id: 2, fork: true) }
+ let(:target_repo) { double(id: 2) }
let(:raw_data) { double(base_data) }
it 'returns false' do
diff --git a/spec/lib/gitlab/gitignore_spec.rb b/spec/lib/gitlab/gitignore_spec.rb
new file mode 100644
index 00000000000..72baa516cc4
--- /dev/null
+++ b/spec/lib/gitlab/gitignore_spec.rb
@@ -0,0 +1,40 @@
+require 'spec_helper'
+
+describe Gitlab::Gitignore do
+ subject { Gitlab::Gitignore }
+
+ describe '.all' do
+ it 'strips the gitignore suffix' do
+ expect(subject.all.first.name).not_to end_with('.gitignore')
+ end
+
+ it 'combines the globals and rest' do
+ all = subject.all.map(&:name)
+
+ expect(all).to include('Vim')
+ expect(all).to include('Ruby')
+ end
+ end
+
+ describe '.find' do
+ it 'returns nil if the file does not exist' do
+ expect(subject.find('mepmep-yadida')).to be nil
+ end
+
+ it 'returns the Gitignore object of a valid file' do
+ ruby = subject.find('Ruby')
+
+ expect(ruby).to be_a Gitlab::Gitignore
+ expect(ruby.name).to eq('Ruby')
+ end
+ end
+
+ describe '#content' do
+ it 'loads the full file' do
+ gitignore = subject.new(Rails.root.join('vendor/gitignore/Ruby.gitignore'))
+
+ expect(gitignore.name).to eq 'Ruby'
+ expect(gitignore.content).to start_with('*.gem')
+ end
+ end
+end
diff --git a/spec/lib/gitlab/import_url_spec.rb b/spec/lib/gitlab/import_url_spec.rb
deleted file mode 100644
index f758cb8693c..00000000000
--- a/spec/lib/gitlab/import_url_spec.rb
+++ /dev/null
@@ -1,21 +0,0 @@
-require 'spec_helper'
-
-describe Gitlab::ImportUrl do
-
- let(:credentials) { { user: 'blah', password: 'password' } }
- let(:import_url) do
- Gitlab::ImportUrl.new("https://github.com/me/project.git", credentials: credentials)
- end
-
- describe :full_url do
- it { expect(import_url.full_url).to eq("https://blah:password@github.com/me/project.git") }
- end
-
- describe :sanitized_url do
- it { expect(import_url.sanitized_url).to eq("https://github.com/me/project.git") }
- end
-
- describe :credentials do
- it { expect(import_url.credentials).to eq(credentials) }
- end
-end
diff --git a/spec/lib/gitlab/lfs/lfs_router_spec.rb b/spec/lib/gitlab/lfs/lfs_router_spec.rb
index 5852b31ab3a..3325190789b 100644
--- a/spec/lib/gitlab/lfs/lfs_router_spec.rb
+++ b/spec/lib/gitlab/lfs/lfs_router_spec.rb
@@ -26,8 +26,8 @@ describe Gitlab::Lfs::Router, lib: true do
let(:sample_oid) { "b68143e6463773b1b6c6fd009a76c32aeec041faff32ba2ed42fd7f708a17f80" }
let(:sample_size) { 499013 }
- let(:respond_with_deprecated) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
- let(:respond_with_disabled) {[ 501, { "Content-Type"=>"application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
+ let(:respond_with_deprecated) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Server supports batch API only, please update your Git LFS client to version 1.0.1 and up.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
+ let(:respond_with_disabled) {[ 501, { "Content-Type" => "application/json; charset=utf-8" }, ["{\"message\":\"Git LFS is not enabled on this GitLab server, contact your admin.\",\"documentation_url\":\"#{Gitlab.config.gitlab.url}/help\"}"]]}
describe 'when lfs is disabled' do
before do
diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb
index 5c885a7a982..7b86450a223 100644
--- a/spec/lib/gitlab/metrics/instrumentation_spec.rb
+++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb
@@ -56,9 +56,6 @@ describe Gitlab::Metrics::Instrumentation do
allow(described_class).to receive(:transaction).
and_return(transaction)
- expect(transaction).to receive(:increment).
- with(:method_duration, a_kind_of(Numeric))
-
expect(transaction).to receive(:add_metric).
with(described_class::SERIES, an_instance_of(Hash),
method: 'Dummy.foo')
@@ -139,9 +136,6 @@ describe Gitlab::Metrics::Instrumentation do
allow(described_class).to receive(:transaction).
and_return(transaction)
- expect(transaction).to receive(:increment).
- with(:method_duration, a_kind_of(Numeric))
-
expect(transaction).to receive(:add_metric).
with(described_class::SERIES, an_instance_of(Hash),
method: 'Dummy#bar')
diff --git a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
index e01b0b4bd21..d824dc54438 100644
--- a/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
+++ b/spec/lib/gitlab/metrics/subscribers/rails_cache_spec.rb
@@ -9,7 +9,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_read' do
it 'increments the cache_read duration' do
expect(subscriber).to receive(:increment).
- with(:cache_read_duration, event.duration)
+ with(:cache_read, event.duration)
subscriber.cache_read(event)
end
@@ -18,7 +18,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_write' do
it 'increments the cache_write duration' do
expect(subscriber).to receive(:increment).
- with(:cache_write_duration, event.duration)
+ with(:cache_write, event.duration)
subscriber.cache_write(event)
end
@@ -27,7 +27,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_delete' do
it 'increments the cache_delete duration' do
expect(subscriber).to receive(:increment).
- with(:cache_delete_duration, event.duration)
+ with(:cache_delete, event.duration)
subscriber.cache_delete(event)
end
@@ -36,7 +36,7 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
describe '#cache_exist?' do
it 'increments the cache_exists duration' do
expect(subscriber).to receive(:increment).
- with(:cache_exists_duration, event.duration)
+ with(:cache_exists, event.duration)
subscriber.cache_exist?(event)
end
@@ -62,9 +62,15 @@ describe Gitlab::Metrics::Subscribers::RailsCache do
with(:cache_duration, event.duration)
expect(transaction).to receive(:increment).
+ with(:cache_count, 1)
+
+ expect(transaction).to receive(:increment).
with(:cache_delete_duration, event.duration)
- subscriber.increment(:cache_delete_duration, event.duration)
+ expect(transaction).to receive(:increment).
+ with(:cache_delete_count, 1)
+
+ subscriber.increment(:cache_delete, event.duration)
end
end
end
diff --git a/spec/lib/gitlab/url_sanitizer_spec.rb b/spec/lib/gitlab/url_sanitizer_spec.rb
new file mode 100644
index 00000000000..de55334118f
--- /dev/null
+++ b/spec/lib/gitlab/url_sanitizer_spec.rb
@@ -0,0 +1,68 @@
+require 'spec_helper'
+
+describe Gitlab::UrlSanitizer, lib: true do
+ let(:credentials) { { user: 'blah', password: 'password' } }
+ let(:url_sanitizer) do
+ described_class.new("https://github.com/me/project.git", credentials: credentials)
+ end
+
+ describe '.sanitize' do
+ def sanitize_url(url)
+ # We want to try with multi-line content because is how error messages are formatted
+ described_class.sanitize(%Q{
+ remote: Not Found
+ fatal: repository '#{url}' not found
+ })
+ end
+
+ it 'mask the credentials from HTTP URLs' do
+ filtered_content = sanitize_url('http://user:pass@test.com/root/repoC.git/')
+
+ expect(filtered_content).to include("http://*****:*****@test.com/root/repoC.git/")
+ end
+
+ it 'mask the credentials from HTTPS URLs' do
+ filtered_content = sanitize_url('https://user:pass@test.com/root/repoA.git/')
+
+ expect(filtered_content).to include("https://*****:*****@test.com/root/repoA.git/")
+ end
+
+ it 'mask credentials from SSH URLs' do
+ filtered_content = sanitize_url('ssh://user@host.test/path/to/repo.git')
+
+ expect(filtered_content).to include("ssh://*****@host.test/path/to/repo.git")
+ end
+
+ it 'does not modify Git URLs' do
+ # git protocol does not support authentication
+ filtered_content = sanitize_url('git://host.test/path/to/repo.git')
+
+ expect(filtered_content).to include("git://host.test/path/to/repo.git")
+ end
+
+ it 'does not modify scp-like URLs' do
+ filtered_content = sanitize_url('user@server:project.git')
+
+ expect(filtered_content).to include("user@server:project.git")
+ end
+ end
+
+ describe '#sanitized_url' do
+ it { expect(url_sanitizer.sanitized_url).to eq("https://github.com/me/project.git") }
+ end
+
+ describe '#credentials' do
+ it { expect(url_sanitizer.credentials).to eq(credentials) }
+ end
+
+ describe '#full_url' do
+ it { expect(url_sanitizer.full_url).to eq("https://blah:password@github.com/me/project.git") }
+
+ it 'supports scp-like URLs' do
+ sanitizer = described_class.new('user@server:project.git')
+
+ expect(sanitizer.full_url).to eq('user@server:project.git')
+ end
+ end
+
+end
diff --git a/spec/lib/json_web_token/rsa_token_spec.rb b/spec/lib/json_web_token/rsa_token_spec.rb
new file mode 100644
index 00000000000..0c3d3ea7019
--- /dev/null
+++ b/spec/lib/json_web_token/rsa_token_spec.rb
@@ -0,0 +1,43 @@
+describe JSONWebToken::RSAToken do
+ let(:rsa_key) do
+ OpenSSL::PKey::RSA.new <<-eos.strip_heredoc
+ -----BEGIN RSA PRIVATE KEY-----
+ MIIBOgIBAAJBAMA5sXIBE0HwgIB40iNidN4PGWzOyLQK0bsdOBNgpEXkDlZBvnak
+ OUgAPF+rME4PB0Yl415DabUI40T5UNmlwxcCAwEAAQJAZtY2pSwIFm3JAXIh0cZZ
+ iXcAfiJ+YzuqinUOS+eW2sBCAEzjcARlU/o6sFQgtsOi4FOMczAd1Yx8UDMXMmrw
+ 2QIhAPBgVhJiTF09pdmeFWutCvTJDlFFAQNbrbo2X2x/9WF9AiEAzLgqMKeStSRu
+ H9N16TuDrUoO8R+DPqriCwkKrSHaWyMCIFzMhE4inuKcSywBaLmiG4m3GQzs++Al
+ A6PRG/PSTpQtAiBxtBg6zdf+JC3GH3zt/dA0/10tL4OF2wORfYQghRzyYQIhAL2l
+ 0ZQW+yLIZAGrdBFWYEAa52GZosncmzBNlsoTgwE4
+ -----END RSA PRIVATE KEY-----
+ eos
+ end
+ let(:rsa_token) { described_class.new(nil) }
+ let(:rsa_encoded) { rsa_token.encoded }
+
+ before { allow_any_instance_of(described_class).to receive(:key).and_return(rsa_key) }
+
+ context 'token' do
+ context 'for valid key to be validated' do
+ before { rsa_token['key'] = 'value' }
+
+ subject { JWT.decode(rsa_encoded, rsa_key) }
+
+ it { expect{subject}.to_not raise_error }
+ it { expect(subject.first).to include('key' => 'value') }
+ it do
+ expect(subject.second).to eq(
+ "typ" => "JWT",
+ "alg" => "RS256",
+ "kid" => "OGXY:4TR7:FAVO:WEM2:XXEW:E4FP:TKL7:7ACK:TZAF:D54P:SUIA:P3B2")
+ end
+ end
+
+ context 'for invalid key to raise an exception' do
+ let(:new_key) { OpenSSL::PKey::RSA.generate(512) }
+ subject { JWT.decode(rsa_encoded, new_key) }
+
+ it { expect{subject}.to raise_error(JWT::DecodeError) }
+ end
+ end
+end
diff --git a/spec/lib/json_web_token/token_spec.rb b/spec/lib/json_web_token/token_spec.rb
new file mode 100644
index 00000000000..3d955e4d774
--- /dev/null
+++ b/spec/lib/json_web_token/token_spec.rb
@@ -0,0 +1,18 @@
+describe JSONWebToken::Token do
+ let(:token) { described_class.new }
+
+ context 'custom parameters' do
+ let(:value) { 'value' }
+ before { token[:key] = value }
+
+ it { expect(token[:key]).to eq(value) }
+ it { expect(token.payload).to include(key: value) }
+ end
+
+ context 'embeds default payload' do
+ subject { token.payload }
+ let(:default) { token.send(:default_payload) }
+
+ it { is_expected.to include(default) }
+ end
+end
diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb
index 495c5cbac00..b963a3e0324 100644
--- a/spec/mailers/notify_spec.rb
+++ b/spec/mailers/notify_spec.rb
@@ -593,7 +593,7 @@ describe Notify do
let(:user) { create(:user) }
let(:tree_path) { namespace_project_tree_path(project.namespace, project, "master") }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :create) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :create) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -606,10 +606,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /Pushed new branch master/
end
@@ -624,7 +620,7 @@ describe Notify do
let(:user) { create(:user) }
let(:tree_path) { namespace_project_tree_path(project.namespace, project, "v1.0") }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :create) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :create) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -637,10 +633,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /Pushed new tag v1\.0/
end
@@ -654,7 +646,7 @@ describe Notify do
let(:example_site_path) { root_path }
let(:user) { create(:user) }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :delete) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :delete) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -667,10 +659,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /Deleted branch master/
end
@@ -680,7 +668,7 @@ describe Notify do
let(:example_site_path) { root_path }
let(:user) { create(:user) }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/tags/v1.0', action: :delete) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -693,10 +681,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /Deleted tag v1\.0/
end
@@ -709,8 +693,9 @@ describe Notify do
let(:commits) { Commit.decorate(compare.commits, nil) }
let(:diff_path) { namespace_project_compare_path(project.namespace, project, from: Commit.new(compare.base, project), to: Commit.new(compare.head, project)) }
let(:send_from_committer_email) { false }
+ let(:diff_refs) { [project.merge_base_commit(sample_image_commit.id, sample_commit.id), project.commit(sample_commit.id)] }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, send_from_committer_email: send_from_committer_email) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, reverse_compare: false, diff_refs: diff_refs, send_from_committer_email: send_from_committer_email) }
it_behaves_like 'it should not have Gmail Actions links'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -723,10 +708,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /\[#{project.path_with_namespace}\]\[master\] #{commits.length} commits:/
end
@@ -735,15 +716,15 @@ describe Notify do
is_expected.to have_body_text /Change some files/
end
- it 'includes diffs' do
- is_expected.to have_body_text /def archive_formats_regex/
+ it 'includes diffs with character-level highlighting' do
+ is_expected.to have_body_text /def<\/span> <span class=\"nf\">archive_formats_regex/
end
it 'contains a link to the diff' do
is_expected.to have_body_text /#{diff_path}/
end
- it 'doesn not contain the misleading footer' do
+ it 'does not contain the misleading footer' do
is_expected.not_to have_body_text /you are a member of/
end
@@ -817,8 +798,9 @@ describe Notify do
let(:compare) { Gitlab::Git::Compare.new(project.repository.raw_repository, sample_commit.parent_id, sample_commit.id) }
let(:commits) { Commit.decorate(compare.commits, nil) }
let(:diff_path) { namespace_project_commit_path(project.namespace, project, commits.first) }
+ let(:diff_refs) { [project.merge_base_commit(sample_commit.parent_id, sample_commit.id), project.commit(sample_commit.id)] }
- subject { Notify.repository_push_email(project.id, 'devs@company.name', author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare) }
+ subject { Notify.repository_push_email(project.id, author_id: user.id, ref: 'refs/heads/master', action: :push, compare: compare, diff_refs: diff_refs) }
it_behaves_like 'it should show Gmail Actions View Commit link'
it_behaves_like "a user cannot unsubscribe through footer link"
@@ -831,10 +813,6 @@ describe Notify do
expect(sender.address).to eq(gitlab_sender)
end
- it 'is sent to recipient' do
- is_expected.to deliver_to 'devs@company.name'
- end
-
it 'has the correct subject' do
is_expected.to have_subject /#{commits.first.title}/
end
@@ -843,8 +821,8 @@ describe Notify do
is_expected.to have_body_text /Change some files/
end
- it 'includes diffs' do
- is_expected.to have_body_text /def archive_formats_regex/
+ it 'includes diffs with character-level highlighting' do
+ is_expected.to have_body_text /def<\/span> <span class=\"nf\">archive_formats_regex/
end
it 'contains a link to the diff' do
diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb
index 1ce22feed5c..d84f3e998f5 100644
--- a/spec/models/application_setting_spec.rb
+++ b/spec/models/application_setting_spec.rb
@@ -20,6 +20,15 @@ describe ApplicationSetting, models: true do
it { is_expected.to allow_value(https).for(:after_sign_out_path) }
it { is_expected.not_to allow_value(ftp).for(:after_sign_out_path) }
+ describe 'disabled_oauth_sign_in_sources validations' do
+ before do
+ allow(Devise).to receive(:omniauth_providers).and_return([:github])
+ end
+
+ it { is_expected.to allow_value(['github']).for(:disabled_oauth_sign_in_sources) }
+ it { is_expected.not_to allow_value(['test']).for(:disabled_oauth_sign_in_sources) }
+ end
+
it { is_expected.to validate_presence_of(:max_attachment_size) }
it do
diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb
index b5d356aa066..abae3271a5c 100644
--- a/spec/models/build_spec.rb
+++ b/spec/models/build_spec.rb
@@ -259,11 +259,11 @@ describe Ci::Build, models: true do
end
describe '#can_be_served?' do
- let(:runner) { FactoryGirl.create :ci_runner }
+ let(:runner) { create(:ci_runner) }
before { build.project.runners << runner }
- context 'runner without tags' do
+ context 'when runner does not have tags' do
it 'can handle builds without tags' do
expect(build.can_be_served?(runner)).to be_truthy
end
@@ -274,25 +274,53 @@ describe Ci::Build, models: true do
end
end
- context 'runner with tags' do
+ context 'when runner has tags' do
before { runner.tag_list = ['bb', 'cc'] }
- it 'can handle builds without tags' do
- expect(build.can_be_served?(runner)).to be_truthy
+ shared_examples 'tagged build picker' do
+ it 'can handle build with matching tags' do
+ build.tag_list = ['bb']
+ expect(build.can_be_served?(runner)).to be_truthy
+ end
+
+ it 'cannot handle build without matching tags' do
+ build.tag_list = ['aa']
+ expect(build.can_be_served?(runner)).to be_falsey
+ end
end
- it 'can handle build with matching tags' do
- build.tag_list = ['bb']
- expect(build.can_be_served?(runner)).to be_truthy
+ context 'when runner can pick untagged jobs' do
+ it 'can handle builds without tags' do
+ expect(build.can_be_served?(runner)).to be_truthy
+ end
+
+ it_behaves_like 'tagged build picker'
end
- it 'cannot handle build with not matching tags' do
- build.tag_list = ['aa']
- expect(build.can_be_served?(runner)).to be_falsey
+ context 'when runner can not pick untagged jobs' do
+ before { runner.run_untagged = false }
+
+ it 'can not handle builds without tags' do
+ expect(build.can_be_served?(runner)).to be_falsey
+ end
+
+ it_behaves_like 'tagged build picker'
end
end
end
+ describe '#has_tags?' do
+ context 'when build has tags' do
+ subject { create(:ci_build, tag_list: ['tag']) }
+ it { is_expected.to have_tags }
+ end
+
+ context 'when build does not have tags' do
+ subject { create(:ci_build, tag_list: []) }
+ it { is_expected.to_not have_tags }
+ end
+ end
+
describe '#any_runners_online?' do
subject { build.any_runners_online? }
diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb
index dc071ad1c90..1b5940ad5a8 100644
--- a/spec/models/ci/commit_spec.rb
+++ b/spec/models/ci/commit_spec.rb
@@ -10,7 +10,6 @@ describe Ci::Commit, models: true do
it { is_expected.to have_many(:builds) }
it { is_expected.to validate_presence_of :sha }
it { is_expected.to validate_presence_of :status }
- it { is_expected.to delegate_method(:stages).to(:statuses) }
it { is_expected.to respond_to :git_author_name }
it { is_expected.to respond_to :git_author_email }
diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb
index eaa94228922..7c6e39419ed 100644
--- a/spec/models/ci/runner_spec.rb
+++ b/spec/models/ci/runner_spec.rb
@@ -1,6 +1,24 @@
require 'spec_helper'
describe Ci::Runner, models: true do
+ describe 'validation' do
+ context 'when runner is not allowed to pick untagged jobs' do
+ context 'when runner does not have tags' do
+ it 'is not valid' do
+ runner = build(:ci_runner, tag_list: [], run_untagged: false)
+ expect(runner).to be_invalid
+ end
+ end
+
+ context 'when runner has tags' do
+ it 'is valid' do
+ runner = build(:ci_runner, tag_list: ['tag'], run_untagged: false)
+ expect(runner).to be_valid
+ end
+ end
+ end
+ end
+
describe '#display_name' do
it 'should return the description if it has a value' do
runner = FactoryGirl.build(:ci_runner, description: 'Linux/Ruby-1.9.3-p448')
@@ -114,7 +132,19 @@ describe Ci::Runner, models: true do
end
end
- describe '#search' do
+ describe '#has_tags?' do
+ context 'when runner has tags' do
+ subject { create(:ci_runner, tag_list: ['tag']) }
+ it { is_expected.to have_tags }
+ end
+
+ context 'when runner does not have tags' do
+ subject { create(:ci_runner, tag_list: []) }
+ it { is_expected.to_not have_tags }
+ end
+ end
+
+ describe '.search' do
let(:runner) { create(:ci_runner, token: '123abc') }
it 'returns runners with a matching token' do
diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb
index ad47e338a33..ccb100cd96f 100644
--- a/spec/models/commit_spec.rb
+++ b/spec/models/commit_spec.rb
@@ -56,7 +56,7 @@ describe Commit, models: true do
end
it "does not truncates a message with a newline after 80 but less 100 characters" do
- message =<<eos
+ message = <<eos
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec sodales id felis id blandit.
Vivamus egestas lacinia lacus, sed rutrum mauris.
eos
diff --git a/spec/models/concerns/subscribable_spec.rb b/spec/models/concerns/subscribable_spec.rb
index e31fdb0bffb..b7fc5a92497 100644
--- a/spec/models/concerns/subscribable_spec.rb
+++ b/spec/models/concerns/subscribable_spec.rb
@@ -44,6 +44,16 @@ describe Subscribable, 'Subscribable' do
end
end
+ describe '#subscribe' do
+ it 'subscribes the given user' do
+ expect(resource.subscribed?(user)).to be_falsey
+
+ resource.subscribe(user)
+
+ expect(resource.subscribed?(user)).to be_truthy
+ end
+ end
+
describe '#unsubscribe' do
it 'unsubscribes the given current user' do
resource.subscriptions.create(user: user, subscribed: true)
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 30c0a04b840..b6adc2bf247 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -49,7 +49,7 @@ describe ApplicationSetting, 'TokenAuthenticatable' do
context 'token is generated' do
before { subject.send("reset_#{token_field}!") }
- it 'persists a new token 'do
+ it 'persists a new token' do
expect(subject.send(:read_attribute, token_field)).to be_a String
end
end
diff --git a/spec/models/hooks/service_hook_spec.rb b/spec/models/hooks/service_hook_spec.rb
index f800f415bd2..534e1b4f128 100644
--- a/spec/models/hooks/service_hook_spec.rb
+++ b/spec/models/hooks/service_hook_spec.rb
@@ -34,14 +34,14 @@ describe ServiceHook, models: true do
it "POSTs to the webhook URL" do
@service_hook.execute(@data)
expect(WebMock).to have_requested(:post, @service_hook.url).with(
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Service Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Service Hook' }
).once
end
it "POSTs the data as JSON" do
@service_hook.execute(@data)
expect(WebMock).to have_requested(:post, @service_hook.url).with(
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Service Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Service Hook' }
).once
end
diff --git a/spec/models/hooks/system_hook_spec.rb b/spec/models/hooks/system_hook_spec.rb
index 56a9fbe9720..4078b9e4ff5 100644
--- a/spec/models/hooks/system_hook_spec.rb
+++ b/spec/models/hooks/system_hook_spec.rb
@@ -33,7 +33,7 @@ describe SystemHook, models: true do
Projects::CreateService.new(user, name: 'empty').execute
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /project_create/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -42,7 +42,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /project_destroy/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -51,7 +51,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_create/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -60,7 +60,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_destroy/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -69,7 +69,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_team/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -79,7 +79,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_team/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -88,7 +88,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /group_create/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -97,7 +97,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /group_destroy/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -106,7 +106,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_add_to_group/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
@@ -116,7 +116,7 @@ describe SystemHook, models: true do
expect(WebMock).to have_requested(:post, system_hook.url).with(
body: /user_remove_from_group/,
- headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'System Hook' }
+ headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'System Hook' }
).once
end
end
diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb
index 37a27d73aab..f9bab487b96 100644
--- a/spec/models/hooks/web_hook_spec.rb
+++ b/spec/models/hooks/web_hook_spec.rb
@@ -95,13 +95,13 @@ describe WebHook, models: true do
it "handles 200 status code" do
WebMock.stub_request(:post, project_hook.url).to_return(status: 200, body: "Success")
- expect(project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success'])
+ expect(project_hook.execute(@data, 'push_hooks')).to eq([200, 'Success'])
end
it "handles 2xx status codes" do
WebMock.stub_request(:post, project_hook.url).to_return(status: 201, body: "Success")
- expect(project_hook.execute(@data, 'push_hooks')).to eq([true, 'Success'])
+ expect(project_hook.execute(@data, 'push_hooks')).to eq([201, 'Success'])
end
end
end
diff --git a/spec/models/legacy_diff_note_spec.rb b/spec/models/legacy_diff_note_spec.rb
new file mode 100644
index 00000000000..7c29bef54e4
--- /dev/null
+++ b/spec/models/legacy_diff_note_spec.rb
@@ -0,0 +1,74 @@
+require 'spec_helper'
+
+describe LegacyDiffNote, models: true do
+ describe "Commit diff line notes" do
+ let!(:note) { create(:note_on_commit_diff, note: "+1 from me") }
+ let!(:commit) { note.noteable }
+
+ it "should save a valid note" do
+ expect(note.commit_id).to eq(commit.id)
+ expect(note.noteable.id).to eq(commit.id)
+ end
+
+ it "should be recognized by #legacy_diff_note?" do
+ expect(note).to be_legacy_diff_note
+ end
+ end
+
+ describe '#active?' do
+ it 'is always true when the note has no associated diff' do
+ note = build(:note_on_merge_request_diff)
+
+ expect(note).to receive(:diff).and_return(nil)
+
+ expect(note).to be_active
+ end
+
+ it 'is never true when the note has no noteable associated' do
+ note = build(:note_on_merge_request_diff)
+
+ expect(note).to receive(:diff).and_return(double)
+ expect(note).to receive(:noteable).and_return(nil)
+
+ expect(note).not_to be_active
+ end
+
+ it 'returns the memoized value if defined' do
+ note = build(:note_on_merge_request_diff)
+
+ note.instance_variable_set(:@active, 'foo')
+ expect(note).not_to receive(:find_noteable_diff)
+
+ expect(note.active?).to eq 'foo'
+ end
+
+ context 'for a merge request noteable' do
+ it 'is false when noteable has no matching diff' do
+ merge = build_stubbed(:merge_request, :simple)
+ note = build(:note_on_merge_request_diff, noteable: merge)
+
+ allow(note).to receive(:diff).and_return(double)
+ expect(note).to receive(:find_noteable_diff).and_return(nil)
+
+ expect(note).not_to be_active
+ end
+
+ it 'is true when noteable has a matching diff' do
+ merge = create(:merge_request, :simple)
+
+ # Generate a real line_code value so we know it will match. We use a
+ # random line from a random diff just for funsies.
+ diff = merge.diffs.to_a.sample
+ line = Gitlab::Diff::Parser.new.parse(diff.diff.each_line).to_a.sample
+ code = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos)
+
+ # We're persisting in order to trigger the set_diff callback
+ note = create(:note_on_merge_request_diff, noteable: merge, line_code: code)
+
+ # Make sure we don't get a false positive from a guard clause
+ expect(note).to receive(:find_noteable_diff).and_call_original
+ expect(note).to be_active
+ end
+ end
+ end
+end
diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb
index c8578749b21..e269ff26a04 100644
--- a/spec/models/merge_request_spec.rb
+++ b/spec/models/merge_request_spec.rb
@@ -64,7 +64,13 @@ describe MergeRequest, models: true do
describe '#target_sha' do
context 'when the target branch does not exist anymore' do
- subject { create(:merge_request).tap { |mr| mr.update_attribute(:target_branch, 'deleted') } }
+ let(:project) { create(:project) }
+
+ subject { create(:merge_request, source_project: project, target_project: project) }
+
+ before do
+ project.repository.raw_repository.delete_branch(subject.target_branch)
+ end
it 'returns nil' do
expect(subject.target_sha).to be_nil
@@ -254,13 +260,18 @@ describe MergeRequest, models: true do
end
describe "#reset_merge_when_build_succeeds" do
- let(:merge_if_green) { create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user) }
+ let(:merge_if_green) do
+ create :merge_request, merge_when_build_succeeds: true, merge_user: create(:user),
+ merge_params: { "should_remove_source_branch" => "1", "commit_message" => "msg" }
+ end
it "sets the item to false" do
merge_if_green.reset_merge_when_build_succeeds
merge_if_green.reload
expect(merge_if_green.merge_when_build_succeeds).to be_falsey
+ expect(merge_if_green.merge_params["should_remove_source_branch"]).to be_nil
+ expect(merge_if_green.merge_params["commit_message"]).to be_nil
end
end
@@ -289,7 +300,12 @@ describe MergeRequest, models: true do
let(:fork_project) { create(:project, forked_from_project: project) }
context 'when the target branch does not exist anymore' do
- subject { create(:merge_request).tap { |mr| mr.update_attribute(:target_branch, 'deleted') } }
+ subject { create(:merge_request, source_project: project, target_project: project) }
+
+ before do
+ project.repository.raw_repository.delete_branch(subject.target_branch)
+ subject.reload
+ end
it 'does not crash' do
expect{ subject.diverged_commits_count }.not_to raise_error
diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb
index 247a9fa9910..1e18c788b50 100644
--- a/spec/models/milestone_spec.rb
+++ b/spec/models/milestone_spec.rb
@@ -204,4 +204,37 @@ describe Milestone, models: true do
to eq([milestone])
end
end
+
+ describe '.upcoming_ids_by_projects' do
+ let(:project_1) { create(:empty_project) }
+ let(:project_2) { create(:empty_project) }
+ let(:project_3) { create(:empty_project) }
+ let(:projects) { [project_1, project_2, project_3] }
+
+ let!(:past_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now - 1.day) }
+ let!(:current_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now + 1.day) }
+ let!(:future_milestone_project_1) { create(:milestone, project: project_1, due_date: Time.now + 2.days) }
+
+ let!(:past_milestone_project_2) { create(:milestone, project: project_2, due_date: Time.now - 1.day) }
+ let!(:closed_milestone_project_2) { create(:milestone, :closed, project: project_2, due_date: Time.now + 1.day) }
+ let!(:current_milestone_project_2) { create(:milestone, project: project_2, due_date: Time.now + 2.days) }
+
+ let!(:past_milestone_project_3) { create(:milestone, project: project_3, due_date: Time.now - 1.day) }
+
+ # The call to `#try` is because this returns a relation with a Postgres DB,
+ # and an array of IDs with a MySQL DB.
+ let(:milestone_ids) { Milestone.upcoming_ids_by_projects(projects).map { |id| id.try(:id) || id } }
+
+ it 'returns the next upcoming open milestone ID for each project' do
+ expect(milestone_ids).to contain_exactly(current_milestone_project_1.id, current_milestone_project_2.id)
+ end
+
+ context 'when the projects have no open upcoming milestones' do
+ let(:projects) { [project_3] }
+
+ it 'returns no results' do
+ expect(milestone_ids).to be_empty
+ end
+ end
+ end
end
diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb
index 4074f966299..4e68ac5e63a 100644
--- a/spec/models/namespace_spec.rb
+++ b/spec/models/namespace_spec.rb
@@ -70,6 +70,20 @@ describe Namespace, models: true do
allow(@namespace).to receive(:path).and_return(new_path)
expect(@namespace.move_dir).to be_truthy
end
+
+ context "when any project has container tags" do
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags('tag')
+
+ create(:empty_project, namespace: @namespace)
+
+ allow(@namespace).to receive(:path_was).and_return(@namespace.path)
+ allow(@namespace).to receive(:path).and_return('new_path')
+ end
+
+ it { expect { @namespace.move_dir }.to raise_error('Namespace cannot be moved, because at least one project has tags in container registry') }
+ end
end
describe :rm_dir do
diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb
index 4b788b57882..5d916f0e6a6 100644
--- a/spec/models/note_spec.rb
+++ b/spec/models/note_spec.rb
@@ -34,24 +34,6 @@ describe Note, models: true do
end
end
- describe "Commit diff line notes" do
- let!(:note) { create(:note_on_commit_diff, note: "+1 from me") }
- let!(:commit) { note.noteable }
-
- it "should save a valid note" do
- expect(note.commit_id).to eq(commit.id)
- expect(note.noteable.id).to eq(commit.id)
- end
-
- it "should be recognized by #for_diff_line?" do
- expect(note).to be_for_diff_line
- end
-
- it "should be recognized by #for_commit_diff_line?" do
- expect(note).to be_for_commit_diff_line
- end
- end
-
describe 'authorization' do
before do
@p1 = create(:project)
@@ -148,66 +130,6 @@ describe Note, models: true do
end
end
- describe '#active?' do
- it 'is always true when the note has no associated diff' do
- note = build(:note)
-
- expect(note).to receive(:diff).and_return(nil)
-
- expect(note).to be_active
- end
-
- it 'is never true when the note has no noteable associated' do
- note = build(:note)
-
- expect(note).to receive(:diff).and_return(double)
- expect(note).to receive(:noteable).and_return(nil)
-
- expect(note).not_to be_active
- end
-
- it 'returns the memoized value if defined' do
- note = build(:note)
-
- expect(note).to receive(:diff).and_return(double)
- expect(note).to receive(:noteable).and_return(double)
-
- note.instance_variable_set(:@active, 'foo')
- expect(note).not_to receive(:find_noteable_diff)
-
- expect(note.active?).to eq 'foo'
- end
-
- context 'for a merge request noteable' do
- it 'is false when noteable has no matching diff' do
- merge = build_stubbed(:merge_request, :simple)
- note = build(:note, noteable: merge)
-
- allow(note).to receive(:diff).and_return(double)
- expect(note).to receive(:find_noteable_diff).and_return(nil)
-
- expect(note).not_to be_active
- end
-
- it 'is true when noteable has a matching diff' do
- merge = create(:merge_request, :simple)
-
- # Generate a real line_code value so we know it will match. We use a
- # random line from a random diff just for funsies.
- diff = merge.diffs.to_a.sample
- line = Gitlab::Diff::Parser.new.parse(diff.diff.each_line).to_a.sample
- code = Gitlab::Diff::LineCode.generate(diff.new_path, line.new_pos, line.old_pos)
-
- # We're persisting in order to trigger the set_diff callback
- note = create(:note, noteable: merge, line_code: code)
-
- # Make sure we don't get a false positive from a guard clause
- expect(note).to receive(:find_noteable_diff).and_call_original
- expect(note).to be_active
- end
- end
- end
-
describe "editable?" do
it "returns true" do
note = build(:note)
@@ -258,7 +180,7 @@ describe Note, models: true do
end
it "is not an award emoji when comment is on a diff" do
- note = create(:note, note: ":blowfish:", noteable: merge_request, line_code: "11d5d2e667e9da4f7f610f81d86c974b146b13bd_0_2")
+ note = create(:note_on_merge_request_diff, note: ":blowfish:", noteable: merge_request, line_code: "11d5d2e667e9da4f7f610f81d86c974b146b13bd_0_2")
note = note.reload
expect(note.note).to eq(":blowfish:")
diff --git a/spec/models/project_services/slack_service/issue_message_spec.rb b/spec/models/project_services/slack_service/issue_message_spec.rb
index f648cbe2dee..0f8889bdf3c 100644
--- a/spec/models/project_services/slack_service/issue_message_spec.rb
+++ b/spec/models/project_services/slack_service/issue_message_spec.rb
@@ -25,7 +25,7 @@ describe SlackService::IssueMessage, models: true do
}
end
- let(:color) { '#345' }
+ let(:color) { '#C95823' }
context '#initialize' do
before do
@@ -40,10 +40,11 @@ describe SlackService::IssueMessage, models: true do
context 'open' do
it 'returns a message regarding opening of issues' do
expect(subject.pretext).to eq(
- 'Test User opened <url|issue #100> in <somewhere.com|project_name>: '\
- '*Issue title*')
+ '<somewhere.com|[project_name>] Issue opened by Test User')
expect(subject.attachments).to eq([
{
+ title: "#100 Issue title",
+ title_link: "url",
text: "issue description",
color: color,
}
@@ -56,10 +57,10 @@ describe SlackService::IssueMessage, models: true do
args[:object_attributes][:action] = 'close'
args[:object_attributes][:state] = 'closed'
end
+
it 'returns a message regarding closing of issues' do
expect(subject.pretext). to eq(
- 'Test User closed <url|issue #100> in <somewhere.com|project_name>: '\
- '*Issue title*')
+ '<somewhere.com|[project_name>] Issue <url|#100 Issue title> closed by Test User')
expect(subject.attachments).to be_empty
end
end
diff --git a/spec/models/project_services/slack_service/note_message_spec.rb b/spec/models/project_services/slack_service/note_message_spec.rb
index d37590cab75..379c3e1219c 100644
--- a/spec/models/project_services/slack_service/note_message_spec.rb
+++ b/spec/models/project_services/slack_service/note_message_spec.rb
@@ -65,7 +65,7 @@ describe SlackService::NoteMessage, models: true do
expect(message.pretext).to eq("Test User commented on " \
"<url|merge request !30> in <somewhere.com|project_name>: " \
"*merge request title*")
- expected_attachments = [
+ expected_attachments = [
{
text: "comment on a merge request",
color: color,
@@ -117,7 +117,7 @@ describe SlackService::NoteMessage, models: true do
expect(message.pretext).to eq("Test User commented on " \
"<url|snippet #5> in <somewhere.com|project_name>: " \
"*snippet title*")
- expected_attachments = [
+ expected_attachments = [
{
text: "comment on a snippet",
color: color,
diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb
index f6e5b132643..60e1ec43f2b 100644
--- a/spec/models/project_spec.rb
+++ b/spec/models/project_spec.rb
@@ -634,11 +634,11 @@ describe Project, models: true do
# Project#gitlab_shell returns a new instance of Gitlab::Shell on every
# call. This makes testing a bit easier.
allow(project).to receive(:gitlab_shell).and_return(gitlab_shell)
- end
- it 'renames a repository' do
allow(project).to receive(:previous_changes).and_return('path' => ['foo'])
+ end
+ it 'renames a repository' do
ns = project.namespace_dir
expect(gitlab_shell).to receive(:mv_repository).
@@ -663,6 +663,17 @@ describe Project, models: true do
project.rename_repo
end
+
+ context 'container registry with tags' do
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags('tag')
+ end
+
+ subject { project.rename_repo }
+
+ it { expect{subject}.to raise_error(Exception) }
+ end
end
describe '#expire_caches_before_rename' do
@@ -772,4 +783,71 @@ describe Project, models: true do
expect(project.protected_branch?('foo')).to eq(false)
end
end
+
+ describe '#container_registry_repository' do
+ let(:project) { create(:empty_project) }
+
+ before { stub_container_registry_config(enabled: true) }
+
+ subject { project.container_registry_repository }
+
+ it { is_expected.to_not be_nil }
+ end
+
+ describe '#container_registry_repository_url' do
+ let(:project) { create(:empty_project) }
+
+ subject { project.container_registry_repository_url }
+
+ before { stub_container_registry_config(**registry_settings) }
+
+ context 'for enabled registry' do
+ let(:registry_settings) do
+ {
+ enabled: true,
+ host_port: 'example.com',
+ }
+ end
+
+ it { is_expected.to_not be_nil }
+ end
+
+ context 'for disabled registry' do
+ let(:registry_settings) do
+ {
+ enabled: false
+ }
+ end
+
+ it { is_expected.to be_nil }
+ end
+ end
+
+ describe '#has_container_registry_tags?' do
+ let(:project) { create(:empty_project) }
+
+ subject { project.has_container_registry_tags? }
+
+ context 'for enabled registry' do
+ before { stub_container_registry_config(enabled: true) }
+
+ context 'with tags' do
+ before { stub_container_registry_tags('test', 'test2') }
+
+ it { is_expected.to be_truthy }
+ end
+
+ context 'when no tags' do
+ before { stub_container_registry_tags }
+
+ it { is_expected.to be_falsey }
+ end
+ end
+
+ context 'for disabled registry' do
+ before { stub_container_registry_config(enabled: false) }
+
+ it { is_expected.to be_falsey }
+ end
+ end
end
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 532e3f013fd..91ebb612baa 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -38,7 +38,8 @@ describe ProjectWiki, models: true do
describe "#wiki_base_path" do
it "returns the wiki base path" do
- wiki_base_path = "/#{project.path_with_namespace}/wikis"
+ wiki_base_path = "#{Gitlab.config.gitlab.relative_url_root}/#{project.path_with_namespace}/wikis"
+
expect(subject.wiki_base_path).to eq(wiki_base_path)
end
end
diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb
index 34a13f9b5c9..583151023b6 100644
--- a/spec/models/repository_spec.rb
+++ b/spec/models/repository_spec.rb
@@ -100,6 +100,12 @@ describe Repository, models: true do
expect(results.first).not_to start_with('fatal:')
end
+ it 'properly handles an unmatched parenthesis' do
+ results = repository.search_files("test(", 'master')
+
+ expect(results.first).not_to start_with('fatal:')
+ end
+
describe 'result' do
subject { results.first }
@@ -176,6 +182,15 @@ describe Repository, models: true do
repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'master')
end
+ it 'handles when HEAD points to non-existent ref' do
+ repository.commit_file(user, 'LICENSE', 'Copyright!', 'Add LICENSE', 'master', false)
+ rugged = double('rugged')
+ expect(rugged).to receive(:head_unborn?).and_return(true)
+ expect(repository).to receive(:rugged).and_return(rugged)
+
+ expect(repository.license_blob).to be_nil
+ end
+
it 'looks in the root_ref only' do
repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'markdown')
repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'markdown', false)
@@ -204,6 +219,15 @@ describe Repository, models: true do
repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'master')
end
+ it 'handles when HEAD points to non-existent ref' do
+ repository.commit_file(user, 'LICENSE', 'Copyright!', 'Add LICENSE', 'master', false)
+ rugged = double('rugged')
+ expect(rugged).to receive(:head_unborn?).and_return(true)
+ expect(repository).to receive(:rugged).and_return(rugged)
+
+ expect(repository.license_key).to be_nil
+ end
+
it 'returns nil when no license is detected' do
expect(repository.license_key).to be_nil
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 26d4e139396..9581990666b 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -141,6 +141,7 @@ describe User, models: true do
end
describe '#confirm' do
+ before { allow(current_application_settings).to receive(:send_user_confirmation_email).and_return(true) }
let(:user) { create(:user, confirmed_at: nil, unconfirmed_email: 'test@gitlab.com') }
it 'returns unconfirmed' do
@@ -782,4 +783,23 @@ describe User, models: true do
it { is_expected.to eq([private_project]) }
end
+
+ describe '#viewable_starred_projects' do
+ let(:user) { create(:user) }
+ let(:public_project) { create(:empty_project, :public) }
+ let(:private_project) { create(:empty_project, :private) }
+ let(:private_viewable_project) { create(:empty_project, :private) }
+
+ before do
+ private_viewable_project.team << [user, Gitlab::Access::MASTER]
+
+ [public_project, private_project, private_viewable_project].each do |project|
+ user.toggle_star(project)
+ end
+ end
+
+ it 'returns only starred projects the user can view' do
+ expect(user.viewable_starred_projects).not_to include(private_project)
+ end
+ end
end
diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb
index 5ead735be48..0fbc984c061 100644
--- a/spec/requests/api/builds_spec.rb
+++ b/spec/requests/api/builds_spec.rb
@@ -106,8 +106,8 @@ describe API::API, api: true do
context 'authorized user' do
let(:download_headers) do
- { 'Content-Transfer-Encoding'=>'binary',
- 'Content-Disposition'=>'attachment; filename=ci_build_artifacts.zip' }
+ { 'Content-Transfer-Encoding' => 'binary',
+ 'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
end
it 'should return specific build artifacts' do
diff --git a/spec/requests/api/gitignores_spec.rb b/spec/requests/api/gitignores_spec.rb
new file mode 100644
index 00000000000..aab2d8c81b9
--- /dev/null
+++ b/spec/requests/api/gitignores_spec.rb
@@ -0,0 +1,29 @@
+require 'spec_helper'
+
+describe API::Gitignores, api: true do
+ include ApiHelpers
+
+ describe 'Entity Gitignore' do
+ before { get api('/gitignores/Ruby') }
+
+ it { expect(json_response['name']).to eq('Ruby') }
+ it { expect(json_response['content']).to include('*.gem') }
+ end
+
+ describe 'Entity GitignoresList' do
+ before { get api('/gitignores') }
+
+ it { expect(json_response.first['name']).not_to be_nil }
+ it { expect(json_response.first['content']).to be_nil }
+ end
+
+ describe 'GET /gitignores' do
+ it 'returns a list of available license templates' do
+ get api('/gitignores')
+
+ expect(response.status).to eq(200)
+ expect(json_response).to be_an Array
+ expect(json_response.size).to be > 15
+ end
+ end
+end
diff --git a/spec/requests/api/group_members_spec.rb b/spec/requests/api/group_members_spec.rb
index 96d89e69209..02553d0f8e2 100644
--- a/spec/requests/api/group_members_spec.rb
+++ b/spec/requests/api/group_members_spec.rb
@@ -34,11 +34,11 @@ describe API::API, api: true do
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.size).to eq(5)
- expect(json_response.find { |e| e['id']==owner.id }['access_level']).to eq(GroupMember::OWNER)
- expect(json_response.find { |e| e['id']==reporter.id }['access_level']).to eq(GroupMember::REPORTER)
- expect(json_response.find { |e| e['id']==developer.id }['access_level']).to eq(GroupMember::DEVELOPER)
- expect(json_response.find { |e| e['id']==master.id }['access_level']).to eq(GroupMember::MASTER)
- expect(json_response.find { |e| e['id']==guest.id }['access_level']).to eq(GroupMember::GUEST)
+ expect(json_response.find { |e| e['id'] == owner.id }['access_level']).to eq(GroupMember::OWNER)
+ expect(json_response.find { |e| e['id'] == reporter.id }['access_level']).to eq(GroupMember::REPORTER)
+ expect(json_response.find { |e| e['id'] == developer.id }['access_level']).to eq(GroupMember::DEVELOPER)
+ expect(json_response.find { |e| e['id'] == master.id }['access_level']).to eq(GroupMember::MASTER)
+ expect(json_response.find { |e| e['id'] == guest.id }['access_level']).to eq(GroupMember::GUEST)
end
end
diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb
index 9dd43f4fab3..37ab9cc8cfe 100644
--- a/spec/requests/api/issues_spec.rb
+++ b/spec/requests/api/issues_spec.rb
@@ -623,6 +623,12 @@ describe API::API, api: true do
expect(response.status).to eq(404)
end
+
+ it 'returns 404 if the issue is confidential' do
+ post api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)
+
+ expect(response.status).to eq(404)
+ end
end
describe 'DELETE :id/issues/:issue_id/subscription' do
@@ -644,5 +650,11 @@ describe API::API, api: true do
expect(response.status).to eq(404)
end
+
+ it 'returns 404 if the issue is confidential' do
+ delete api("/projects/#{project.id}/issues/#{confidential_issue.id}/subscription", non_member)
+
+ expect(response.status).to eq(404)
+ end
end
end
diff --git a/spec/requests/api/labels_spec.rb b/spec/requests/api/labels_spec.rb
index 6943ff9d26c..b2c7f8d9acb 100644
--- a/spec/requests/api/labels_spec.rb
+++ b/spec/requests/api/labels_spec.rb
@@ -190,4 +190,86 @@ describe API::API, api: true do
expect(json_response['message']['color']).to eq(['must be a valid color code'])
end
end
+
+ describe "POST /projects/:id/labels/:label_id/subscription" do
+ context "when label_id is a label title" do
+ it "should subscribe to the label" do
+ post api("/projects/#{project.id}/labels/#{label1.title}/subscription", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_truthy
+ end
+ end
+
+ context "when label_id is a label ID" do
+ it "should subscribe to the label" do
+ post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(201)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_truthy
+ end
+ end
+
+ context "when user is already subscribed to label" do
+ before { label1.subscribe(user) }
+
+ it "should return 304" do
+ post api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(304)
+ end
+ end
+
+ context "when label ID is not found" do
+ it "should a return 404 error" do
+ post api("/projects/#{project.id}/labels/1234/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
+
+ describe "DELETE /projects/:id/labels/:label_id/subscription" do
+ before { label1.subscribe(user) }
+
+ context "when label_id is a label title" do
+ it "should unsubscribe from the label" do
+ delete api("/projects/#{project.id}/labels/#{label1.title}/subscription", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_falsey
+ end
+ end
+
+ context "when label_id is a label ID" do
+ it "should unsubscribe from the label" do
+ delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(200)
+ expect(json_response["name"]).to eq(label1.title)
+ expect(json_response["subscribed"]).to be_falsey
+ end
+ end
+
+ context "when user is already unsubscribed from label" do
+ before { label1.unsubscribe(user) }
+
+ it "should return 304" do
+ delete api("/projects/#{project.id}/labels/#{label1.id}/subscription", user)
+
+ expect(response.status).to eq(304)
+ end
+ end
+
+ context "when label ID is not found" do
+ it "should a return 404 error" do
+ delete api("/projects/#{project.id}/labels/1234/subscription", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+ end
end
diff --git a/spec/requests/api/licenses_spec.rb b/spec/requests/api/licenses_spec.rb
index c17dcb222a9..3726b2f5688 100644
--- a/spec/requests/api/licenses_spec.rb
+++ b/spec/requests/api/licenses_spec.rb
@@ -57,7 +57,7 @@ describe API::Licenses, api: true do
end
it 'replaces placeholder values' do
- expect(json_response['content']).to include('Copyright (c) 2016 Anton')
+ expect(json_response['content']).to include("Copyright (c) #{Time.now.year} Anton")
end
end
@@ -70,7 +70,7 @@ describe API::Licenses, api: true do
it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project')
- expect(json_response['content']).to include('Copyright (C) 2016 Anton')
+ expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
end
end
@@ -83,7 +83,7 @@ describe API::Licenses, api: true do
it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project')
- expect(json_response['content']).to include('Copyright (C) 2016 Anton')
+ expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
end
end
@@ -96,7 +96,7 @@ describe API::Licenses, api: true do
it 'replaces placeholder values' do
expect(json_response['content']).to include('My Awesome Project')
- expect(json_response['content']).to include('Copyright (C) 2016 Anton')
+ expect(json_response['content']).to include("Copyright (C) #{Time.now.year} Anton")
end
end
@@ -108,7 +108,7 @@ describe API::Licenses, api: true do
end
it 'replaces placeholder values' do
- expect(json_response['content']).to include('Copyright 2016 Anton')
+ expect(json_response['content']).to include("Copyright #{Time.now.year} Anton")
end
end
@@ -128,7 +128,7 @@ describe API::Licenses, api: true do
it 'replaces the copyright owner placeholder with the name of the current user' do
get api('/licenses/mit', user)
- expect(json_response['content']).to include("Copyright (c) 2016 #{user.name}")
+ expect(json_response['content']).to include("Copyright (c) #{Time.now.year} #{user.name}")
end
end
end
diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb
index 49091fc0f49..ed1ed5aeb95 100644
--- a/spec/requests/api/notes_spec.rb
+++ b/spec/requests/api/notes_spec.rb
@@ -3,7 +3,7 @@ require 'spec_helper'
describe API::API, api: true do
include ApiHelpers
let(:user) { create(:user) }
- let!(:project) { create(:project, namespace: user.namespace) }
+ let!(:project) { create(:project, :public, namespace: user.namespace) }
let!(:issue) { create(:issue, project: project, author: user) }
let!(:merge_request) { create(:merge_request, source_project: project, target_project: project, author: user) }
let!(:snippet) { create(:project_snippet, project: project, author: user) }
@@ -39,6 +39,7 @@ describe API::API, api: true do
context "when noteable is an Issue" do
it "should return an array of issue notes" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
+
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.first['body']).to eq(issue_note.note)
@@ -46,20 +47,33 @@ describe API::API, api: true do
it "should return a 404 error when issue id not found" do
get api("/projects/#{project.id}/issues/12345/notes", user)
+
expect(response.status).to eq(404)
end
- context "that references a private issue" do
+ context "and current user cannot view the notes" do
it "should return an empty array" do
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
+
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response).to be_empty
end
+ context "and issue is confidential" do
+ before { ext_issue.update_attributes(confidential: true) }
+
+ it "returns 404" do
+ get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
context "and current user can view the note" do
it "should return an empty array" do
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes", private_user)
+
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.first['body']).to eq(cross_reference_note.note)
@@ -71,6 +85,7 @@ describe API::API, api: true do
context "when noteable is a Snippet" do
it "should return an array of snippet notes" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
+
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.first['body']).to eq(snippet_note.note)
@@ -78,6 +93,13 @@ describe API::API, api: true do
it "should return a 404 error when snippet id not found" do
get api("/projects/#{project.id}/snippets/42/notes", user)
+
+ expect(response.status).to eq(404)
+ end
+
+ it "returns 404 when not authorized" do
+ get api("/projects/#{project.id}/snippets/#{snippet.id}/notes", private_user)
+
expect(response.status).to eq(404)
end
end
@@ -85,6 +107,7 @@ describe API::API, api: true do
context "when noteable is a Merge Request" do
it "should return an array of merge_requests notes" do
get api("/projects/#{project.id}/merge_requests/#{merge_request.id}/notes", user)
+
expect(response.status).to eq(200)
expect(json_response).to be_an Array
expect(json_response.first['body']).to eq(merge_request_note.note)
@@ -92,6 +115,13 @@ describe API::API, api: true do
it "should return a 404 error if merge request id not found" do
get api("/projects/#{project.id}/merge_requests/4444/notes", user)
+
+ expect(response.status).to eq(404)
+ end
+
+ it "returns 404 when not authorized" do
+ get api("/projects/#{project.id}/merge_requests/4444/notes", private_user)
+
expect(response.status).to eq(404)
end
end
@@ -101,24 +131,39 @@ describe API::API, api: true do
context "when noteable is an Issue" do
it "should return an issue note by id" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", user)
+
expect(response.status).to eq(200)
expect(json_response['body']).to eq(issue_note.note)
end
it "should return a 404 error if issue note not found" do
get api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user)
+
expect(response.status).to eq(404)
end
- context "that references a private issue" do
+ context "and current user cannot view the note" do
it "should return a 404 error" do
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", user)
+
expect(response.status).to eq(404)
end
+ context "when issue is confidential" do
+ before { issue.update_attributes(confidential: true) }
+
+ it "returns 404" do
+ get api("/projects/#{project.id}/issues/#{issue.id}/notes/#{issue_note.id}", private_user)
+
+ expect(response.status).to eq(404)
+ end
+ end
+
+
context "and current user can view the note" do
it "should return an issue note by id" do
get api("/projects/#{ext_proj.id}/issues/#{ext_issue.id}/notes/#{cross_reference_note.id}", private_user)
+
expect(response.status).to eq(200)
expect(json_response['body']).to eq(cross_reference_note.note)
end
@@ -129,12 +174,14 @@ describe API::API, api: true do
context "when noteable is a Snippet" do
it "should return a snippet note by id" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/#{snippet_note.id}", user)
+
expect(response.status).to eq(200)
expect(json_response['body']).to eq(snippet_note.note)
end
it "should return a 404 error if snippet note not found" do
get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user)
+
expect(response.status).to eq(404)
end
end
@@ -144,6 +191,7 @@ describe API::API, api: true do
context "when noteable is an Issue" do
it "should create a new issue note" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user), body: 'hi!'
+
expect(response.status).to eq(201)
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
@@ -151,11 +199,13 @@ describe API::API, api: true do
it "should return a 400 bad request error if body not given" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user)
+
expect(response.status).to eq(400)
end
it "should return a 401 unauthorized error if user not authenticated" do
post api("/projects/#{project.id}/issues/#{issue.id}/notes"), body: 'hi!'
+
expect(response.status).to eq(401)
end
@@ -164,6 +214,7 @@ describe API::API, api: true do
creation_time = 2.weeks.ago
post api("/projects/#{project.id}/issues/#{issue.id}/notes", user),
body: 'hi!', created_at: creation_time
+
expect(response.status).to eq(201)
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
@@ -176,6 +227,7 @@ describe API::API, api: true do
context "when noteable is a Snippet" do
it "should create a new snippet note" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user), body: 'hi!'
+
expect(response.status).to eq(201)
expect(json_response['body']).to eq('hi!')
expect(json_response['author']['username']).to eq(user.username)
@@ -183,11 +235,13 @@ describe API::API, api: true do
it "should return a 400 bad request error if body not given" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes", user)
+
expect(response.status).to eq(400)
end
it "should return a 401 unauthorized error if user not authenticated" do
post api("/projects/#{project.id}/snippets/#{snippet.id}/notes"), body: 'hi!'
+
expect(response.status).to eq(401)
end
end
@@ -227,6 +281,7 @@ describe API::API, api: true do
it 'should return modified note' do
put api("/projects/#{project.id}/issues/#{issue.id}/"\
"notes/#{issue_note.id}", user), body: 'Hello!'
+
expect(response.status).to eq(200)
expect(json_response['body']).to eq('Hello!')
end
@@ -234,12 +289,14 @@ describe API::API, api: true do
it 'should return a 404 error when note id not found' do
put api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user),
body: 'Hello!'
+
expect(response.status).to eq(404)
end
it 'should return a 400 bad request error if body not given' do
put api("/projects/#{project.id}/issues/#{issue.id}/"\
"notes/#{issue_note.id}", user)
+
expect(response.status).to eq(400)
end
end
@@ -248,6 +305,7 @@ describe API::API, api: true do
it 'should return modified note' do
put api("/projects/#{project.id}/snippets/#{snippet.id}/"\
"notes/#{snippet_note.id}", user), body: 'Hello!'
+
expect(response.status).to eq(200)
expect(json_response['body']).to eq('Hello!')
end
@@ -255,6 +313,7 @@ describe API::API, api: true do
it 'should return a 404 error when note id not found' do
put api("/projects/#{project.id}/snippets/#{snippet.id}/"\
"notes/12345", user), body: "Hello!"
+
expect(response.status).to eq(404)
end
end
@@ -263,6 +322,7 @@ describe API::API, api: true do
it 'should return modified note' do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
"notes/#{merge_request_note.id}", user), body: 'Hello!'
+
expect(response.status).to eq(200)
expect(json_response['body']).to eq('Hello!')
end
@@ -270,6 +330,7 @@ describe API::API, api: true do
it 'should return a 404 error when note id not found' do
put api("/projects/#{project.id}/merge_requests/#{merge_request.id}/"\
"notes/12345", user), body: "Hello!"
+
expect(response.status).to eq(404)
end
end
diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb
index 66193eac051..f167813e07d 100644
--- a/spec/requests/api/projects_spec.rb
+++ b/spec/requests/api/projects_spec.rb
@@ -10,20 +10,20 @@ describe API::API, api: true do
let(:admin) { create(:admin) }
let(:project) { create(:project, creator_id: user.id, namespace: user.namespace) }
let(:project2) { create(:project, path: 'project2', creator_id: user.id, namespace: user.namespace) }
- let(:project3) { create(:project, path: 'project3', creator_id: user.id, namespace: user.namespace) }
let(:snippet) { create(:project_snippet, :public, author: user, project: project, title: 'example') }
let(:project_member) { create(:project_member, :master, user: user, project: project) }
let(:project_member2) { create(:project_member, :developer, user: user3, project: project) }
let(:user4) { create(:user) }
let(:project3) do
create(:project,
+ :private,
name: 'second_project',
path: 'second_project',
creator_id: user.id,
namespace: user.namespace,
merge_requests_enabled: false,
issues_enabled: false, wiki_enabled: false,
- snippets_enabled: false, visibility_level: 0)
+ snippets_enabled: false)
end
let(:project_member3) do
create(:project_member,
@@ -164,21 +164,18 @@ describe API::API, api: true do
end
describe 'GET /projects/starred' do
+ let(:public_project) { create(:project, :public) }
+
before do
- admin.starred_projects << project
- admin.save!
+ project_member2
+ user3.update_attributes(starred_projects: [project, project2, project3, public_project])
end
- it 'should return the starred projects' do
- get api('/projects/all', admin)
+ it 'should return the starred projects viewable by the user' do
+ get api('/projects/starred', user3)
expect(response.status).to eq(200)
expect(json_response).to be_an Array
-
- expect(json_response).to satisfy do |response|
- response.one? do |entry|
- entry['name'] == project.name
- end
- end
+ expect(json_response.map { |project| project['id'] }).to contain_exactly(project.id, public_project.id)
end
end
diff --git a/spec/requests/api/runners_spec.rb b/spec/requests/api/runners_spec.rb
index 3af61d4b335..73ae8ef631c 100644
--- a/spec/requests/api/runners_spec.rb
+++ b/spec/requests/api/runners_spec.rb
@@ -184,21 +184,24 @@ describe API::Runners, api: true do
description = shared_runner.description
active = shared_runner.active
- put api("/runners/#{shared_runner.id}", admin), description: "#{description}_updated", active: !active,
- tag_list: ['ruby2.1', 'pgsql', 'mysql']
+ update_runner(shared_runner.id, admin, description: "#{description}_updated",
+ active: !active,
+ tag_list: ['ruby2.1', 'pgsql', 'mysql'],
+ run_untagged: 'false')
shared_runner.reload
expect(response.status).to eq(200)
expect(shared_runner.description).to eq("#{description}_updated")
expect(shared_runner.active).to eq(!active)
expect(shared_runner.tag_list).to include('ruby2.1', 'pgsql', 'mysql')
+ expect(shared_runner.run_untagged?).to be false
end
end
context 'when runner is not shared' do
it 'should update runner' do
description = specific_runner.description
- put api("/runners/#{specific_runner.id}", admin), description: 'test'
+ update_runner(specific_runner.id, admin, description: 'test')
specific_runner.reload
expect(response.status).to eq(200)
@@ -208,10 +211,14 @@ describe API::Runners, api: true do
end
it 'should return 404 if runner does not exists' do
- put api('/runners/9999', admin), description: 'test'
+ update_runner(9999, admin, description: 'test')
expect(response.status).to eq(404)
end
+
+ def update_runner(id, user, args)
+ put api("/runners/#{id}", user), args
+ end
end
context 'authorized user' do
diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb
index dfd361a2cdd..7ebf8e41f3b 100644
--- a/spec/requests/ci/api/builds_spec.rb
+++ b/spec/requests/ci/api/builds_spec.rb
@@ -128,6 +128,38 @@ describe Ci::API::API do
end
end
end
+
+ context 'when build has no tags' do
+ before do
+ commit = create(:ci_commit, project: project)
+ create(:ci_build, commit: commit, tags: [])
+ end
+
+ context 'when runner is allowed to pick untagged builds' do
+ before { runner.update_column(:run_untagged, true) }
+
+ it 'picks build' do
+ register_builds
+
+ expect(response).to have_http_status 201
+ end
+ end
+
+ context 'when runner is not allowed to pick untagged builds' do
+ before { runner.update_column(:run_untagged, false) }
+
+ it 'does not pick build' do
+ register_builds
+
+ expect(response).to have_http_status 404
+ end
+ end
+
+ def register_builds
+ post ci_api("/builds/register"), token: runner.token,
+ info: { platform: :darwin }
+ end
+ end
end
describe "PUT /builds/:id" do
@@ -402,8 +434,8 @@ describe Ci::API::API do
context 'build has artifacts' do
let(:build) { create(:ci_build, :artifacts) }
let(:download_headers) do
- { 'Content-Transfer-Encoding'=>'binary',
- 'Content-Disposition'=>'attachment; filename=ci_build_artifacts.zip' }
+ { 'Content-Transfer-Encoding' => 'binary',
+ 'Content-Disposition' => 'attachment; filename=ci_build_artifacts.zip' }
end
it 'should download artifact' do
diff --git a/spec/requests/ci/api/runners_spec.rb b/spec/requests/ci/api/runners_spec.rb
index db8189ffb79..43596f07cb5 100644
--- a/spec/requests/ci/api/runners_spec.rb
+++ b/spec/requests/ci/api/runners_spec.rb
@@ -12,44 +12,85 @@ describe Ci::API::API do
end
describe "POST /runners/register" do
- describe "should create a runner if token provided" do
+ context 'when runner token is provided' do
before { post ci_api("/runners/register"), token: registration_token }
- it { expect(response.status).to eq(201) }
+ it 'creates runner with default values' do
+ expect(response).to have_http_status 201
+ expect(Ci::Runner.first.run_untagged).to be true
+ end
end
- describe "should create a runner with description" do
- before { post ci_api("/runners/register"), token: registration_token, description: "server.hostname" }
+ context 'when runner description is provided' do
+ before do
+ post ci_api("/runners/register"), token: registration_token,
+ description: "server.hostname"
+ end
- it { expect(response.status).to eq(201) }
- it { expect(Ci::Runner.first.description).to eq("server.hostname") }
+ it 'creates runner' do
+ expect(response).to have_http_status 201
+ expect(Ci::Runner.first.description).to eq("server.hostname")
+ end
end
- describe "should create a runner with tags" do
- before { post ci_api("/runners/register"), token: registration_token, tag_list: "tag1, tag2" }
+ context 'when runner tags are provided' do
+ before do
+ post ci_api("/runners/register"), token: registration_token,
+ tag_list: "tag1, tag2"
+ end
- it { expect(response.status).to eq(201) }
- it { expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"]) }
+ it 'creates runner' do
+ expect(response).to have_http_status 201
+ expect(Ci::Runner.first.tag_list.sort).to eq(["tag1", "tag2"])
+ end
end
- describe "should create a runner if project token provided" do
+ context 'when option for running untagged jobs is provided' do
+ context 'when tags are provided' do
+ it 'creates runner' do
+ post ci_api("/runners/register"), token: registration_token,
+ run_untagged: false,
+ tag_list: ['tag']
+
+ expect(response).to have_http_status 201
+ expect(Ci::Runner.first.run_untagged).to be false
+ end
+ end
+
+ context 'when tags are not provided' do
+ it 'does not create runner' do
+ post ci_api("/runners/register"), token: registration_token,
+ run_untagged: false
+
+ expect(response).to have_http_status 404
+ end
+ end
+ end
+
+ context 'when project token is provided' do
let(:project) { FactoryGirl.create(:empty_project) }
before { post ci_api("/runners/register"), token: project.runners_token }
- it { expect(response.status).to eq(201) }
- it { expect(project.runners.size).to eq(1) }
+ it 'creates runner' do
+ expect(response).to have_http_status 201
+ expect(project.runners.size).to eq(1)
+ end
end
- it "should return 403 error if token is invalid" do
- post ci_api("/runners/register"), token: 'invalid'
+ context 'when token is invalid' do
+ it 'returns 403 error' do
+ post ci_api("/runners/register"), token: 'invalid'
- expect(response.status).to eq(403)
+ expect(response).to have_http_status 403
+ end
end
- it "should return 400 error if no token" do
- post ci_api("/runners/register")
+ context 'when no token provided' do
+ it 'returns 400 error' do
+ post ci_api("/runners/register")
- expect(response.status).to eq(400)
+ expect(response).to have_http_status 400
+ end
end
%w(name version revision platform architecture).each do |param|
@@ -60,7 +101,7 @@ describe Ci::API::API do
it do
post ci_api("/runners/register"), token: registration_token, info: { param => value }
- expect(response.status).to eq(201)
+ expect(response).to have_http_status 201
is_expected.to eq(value)
end
end
@@ -71,7 +112,7 @@ describe Ci::API::API do
let!(:runner) { FactoryGirl.create(:ci_runner) }
before { delete ci_api("/runners/delete"), token: runner.token }
- it { expect(response.status).to eq(200) }
+ it { expect(response).to have_http_status 200 }
it { expect(Ci::Runner.count).to eq(0) }
end
end
diff --git a/spec/requests/jwt_controller_spec.rb b/spec/requests/jwt_controller_spec.rb
new file mode 100644
index 00000000000..d006ff195cf
--- /dev/null
+++ b/spec/requests/jwt_controller_spec.rb
@@ -0,0 +1,72 @@
+require 'spec_helper'
+
+describe JwtController do
+ let(:service) { double(execute: {}) }
+ let(:service_class) { double(new: service) }
+ let(:service_name) { 'test' }
+ let(:parameters) { { service: service_name } }
+
+ before { stub_const('JwtController::SERVICES', service_name => service_class) }
+
+ context 'existing service' do
+ subject! { get '/jwt/auth', parameters }
+
+ it { expect(response.status).to eq(200) }
+
+ context 'returning custom http code' do
+ let(:service) { double(execute: { http_status: 505 }) }
+
+ it { expect(response.status).to eq(505) }
+ end
+ end
+
+ context 'when using authorized request' do
+ context 'using CI token' do
+ let(:project) { create(:empty_project, runners_token: 'token', builds_enabled: builds_enabled) }
+ let(:headers) { { authorization: credentials('gitlab-ci-token', project.runners_token) } }
+
+ subject! { get '/jwt/auth', parameters, headers }
+
+ context 'project with enabled CI' do
+ let(:builds_enabled) { true }
+
+ it { expect(service_class).to have_received(:new).with(project, nil, parameters) }
+ end
+
+ context 'project with disabled CI' do
+ let(:builds_enabled) { false }
+
+ it { expect(response.status).to eq(403) }
+ end
+ end
+
+ context 'using User login' do
+ let(:user) { create(:user) }
+ let(:headers) { { authorization: credentials('user', 'password') } }
+
+ before { expect_any_instance_of(Gitlab::Auth).to receive(:find).with('user', 'password').and_return(user) }
+
+ subject! { get '/jwt/auth', parameters, headers }
+
+ it { expect(service_class).to have_received(:new).with(nil, user, parameters) }
+ end
+
+ context 'using invalid login' do
+ let(:headers) { { authorization: credentials('invalid', 'password') } }
+
+ subject! { get '/jwt/auth', parameters, headers }
+
+ it { expect(response.status).to eq(403) }
+ end
+ end
+
+ context 'unknown service' do
+ subject! { get '/jwt/auth', service: 'unknown' }
+
+ it { expect(response.status).to eq(404) }
+ end
+
+ def credentials(login, password)
+ ActionController::HttpAuthentication::Basic.encode_credentials(login, password)
+ end
+end
diff --git a/spec/routing/admin_routing_spec.rb b/spec/routing/admin_routing_spec.rb
index cd16a8e6322..b5ed8584c8a 100644
--- a/spec/routing/admin_routing_spec.rb
+++ b/spec/routing/admin_routing_spec.rb
@@ -118,3 +118,10 @@ describe Admin::DashboardController, "routing" do
expect(get("/admin")).to route_to('admin/dashboard#index')
end
end
+
+# admin_health_check GET /admin/health_check(.:format) admin/health_check#show
+describe Admin::HealthCheckController, "routing" do
+ it "to #show" do
+ expect(get("/admin/health_check")).to route_to('admin/health_check#show')
+ end
+end
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 1527eddfa48..de13c0db5d1 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -1,5 +1,42 @@
require 'spec_helper'
+# user GET /u/:username/
+# user_groups GET /u/:username/groups(.:format)
+# user_projects GET /u/:username/projects(.:format)
+# user_contributed_projects GET /u/:username/contributed(.:format)
+# user_snippets GET /u/:username/snippets(.:format)
+# user_calendar GET /u/:username/calendar(.:format)
+# user_calendar_activities GET /u/:username/calendar_activities(.:format)
+describe UsersController, "routing" do
+ it "to #show" do
+ expect(get("/u/User")).to route_to('users#show', username: 'User')
+ end
+
+ it "to #groups" do
+ expect(get("/u/User/groups")).to route_to('users#groups', username: 'User')
+ end
+
+ it "to #projects" do
+ expect(get("/u/User/projects")).to route_to('users#projects', username: 'User')
+ end
+
+ it "to #contributed" do
+ expect(get("/u/User/contributed")).to route_to('users#contributed', username: 'User')
+ end
+
+ it "to #snippets" do
+ expect(get("/u/User/snippets")).to route_to('users#snippets', username: 'User')
+ end
+
+ it "to #calendar" do
+ expect(get("/u/User/calendar")).to route_to('users#calendar', username: 'User')
+ end
+
+ it "to #calendar_activities" do
+ expect(get("/u/User/calendar_activities")).to route_to('users#calendar_activities', username: 'User')
+ end
+end
+
# search GET /search(.:format) search#show
describe SearchController, "routing" do
it "to #show" do
@@ -27,10 +64,6 @@ end
# PUT /snippets/:id(.:format) snippets#update
# DELETE /snippets/:id(.:format) snippets#destroy
describe SnippetsController, "routing" do
- it "to #user_index" do
- expect(get("/s/User")).to route_to('snippets#index', username: 'User')
- end
-
it "to #raw" do
expect(get("/snippets/1/raw")).to route_to('snippets#raw', id: '1')
end
@@ -243,3 +276,13 @@ describe "Groups", "routing" do
expect(get('/1')).to route_to('namespaces#show', id: '1')
end
end
+
+describe HealthCheckController, 'routing' do
+ it 'to #index' do
+ expect(get('/health_check')).to route_to('health_check#index')
+ end
+
+ it 'also supports passing checks in the url' do
+ expect(get('/health_check/email')).to route_to('health_check#index', checks: 'email')
+ end
+end
diff --git a/spec/services/auth/container_registry_authentication_service_spec.rb b/spec/services/auth/container_registry_authentication_service_spec.rb
new file mode 100644
index 00000000000..3f4a1ced2b6
--- /dev/null
+++ b/spec/services/auth/container_registry_authentication_service_spec.rb
@@ -0,0 +1,239 @@
+require 'spec_helper'
+
+describe Auth::ContainerRegistryAuthenticationService, services: true do
+ let(:current_project) { nil }
+ let(:current_user) { nil }
+ let(:current_params) { {} }
+ let(:rsa_key) { OpenSSL::PKey::RSA.generate(512) }
+ let(:payload) { JWT.decode(subject[:token], rsa_key).first }
+
+ subject { described_class.new(current_project, current_user, current_params).execute }
+
+ before do
+ allow(Gitlab.config.registry).to receive_messages(enabled: true, issuer: 'rspec', key: nil)
+ allow_any_instance_of(JSONWebToken::RSAToken).to receive(:key).and_return(rsa_key)
+ end
+
+ shared_examples 'an authenticated' do
+ it { is_expected.to include(:token) }
+ it { expect(payload).to include('access') }
+ end
+
+ shared_examples 'a accessible' do
+ let(:access) do
+ [{
+ 'type' => 'repository',
+ 'name' => project.path_with_namespace,
+ 'actions' => actions,
+ }]
+ end
+
+ it_behaves_like 'an authenticated'
+ it { expect(payload).to include('access' => access) }
+ end
+
+ shared_examples 'a pullable' do
+ it_behaves_like 'a accessible' do
+ let(:actions) { ['pull'] }
+ end
+ end
+
+ shared_examples 'a pushable' do
+ it_behaves_like 'a accessible' do
+ let(:actions) { ['push'] }
+ end
+ end
+
+ shared_examples 'a pullable and pushable' do
+ it_behaves_like 'a accessible' do
+ let(:actions) { ['pull', 'push'] }
+ end
+ end
+
+ shared_examples 'an unauthorized' do
+ it { is_expected.to include(http_status: 401) }
+ it { is_expected.to_not include(:token) }
+ end
+
+ shared_examples 'a forbidden' do
+ it { is_expected.to include(http_status: 403) }
+ it { is_expected.to_not include(:token) }
+ end
+
+ describe '#full_access_token' do
+ let(:project) { create(:empty_project) }
+ let(:token) { described_class.full_access_token(project.path_with_namespace) }
+
+ subject { { token: token } }
+
+ it_behaves_like 'a accessible' do
+ let(:actions) { ['*'] }
+ end
+ end
+
+ context 'user authorization' do
+ let(:project) { create(:project) }
+ let(:current_user) { create(:user) }
+
+ context 'allow to use offline_token' do
+ let(:current_params) do
+ { offline_token: true }
+ end
+
+ it_behaves_like 'an authenticated'
+ end
+
+ context 'allow developer to push images' do
+ before { project.team << [current_user, :developer] }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:push" }
+ end
+
+ it_behaves_like 'a pushable'
+ end
+
+ context 'allow reporter to pull images' do
+ before { project.team << [current_user, :reporter] }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull" }
+ end
+
+ it_behaves_like 'a pullable'
+ end
+
+ context 'return a least of privileges' do
+ before { project.team << [current_user, :reporter] }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:push,pull" }
+ end
+
+ it_behaves_like 'a pullable'
+ end
+
+ context 'disallow guest to pull or push images' do
+ before { project.team << [current_user, :guest] }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull,push" }
+ end
+
+ it_behaves_like 'a forbidden'
+ end
+ end
+
+ context 'project authorization' do
+ let(:current_project) { create(:empty_project) }
+
+ context 'allow to use offline_token' do
+ let(:current_params) do
+ { offline_token: true }
+ end
+
+ it_behaves_like 'an authenticated'
+ end
+
+ context 'allow to pull and push images' do
+ let(:current_params) do
+ { scope: "repository:#{current_project.path_with_namespace}:pull,push" }
+ end
+
+ it_behaves_like 'a pullable and pushable' do
+ let(:project) { current_project }
+ end
+ end
+
+ context 'for other projects' do
+ context 'when pulling' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull" }
+ end
+
+ context 'allow for public' do
+ let(:project) { create(:empty_project, :public) }
+ it_behaves_like 'a pullable'
+ end
+
+ context 'disallow for private' do
+ let(:project) { create(:empty_project, :private) }
+ it_behaves_like 'a forbidden'
+ end
+ end
+
+ context 'when pushing' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:push" }
+ end
+
+ context 'disallow for all' do
+ let(:project) { create(:empty_project, :public) }
+ it_behaves_like 'a forbidden'
+ end
+ end
+ end
+
+ context 'for project without container registry' do
+ let(:project) { create(:empty_project, :public, container_registry_enabled: false) }
+
+ before { project.update(container_registry_enabled: false) }
+
+ context 'disallow when pulling' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull" }
+ end
+
+ it_behaves_like 'a forbidden'
+ end
+ end
+ end
+
+ context 'unauthorized' do
+ context 'disallow to use offline_token' do
+ let(:current_params) do
+ { offline_token: true }
+ end
+
+ it_behaves_like 'an unauthorized'
+ end
+
+ context 'for invalid scope' do
+ let(:current_params) do
+ { scope: 'invalid:aa:bb' }
+ end
+
+ it_behaves_like 'a forbidden'
+ end
+
+ context 'for private project' do
+ let(:project) { create(:empty_project, :private) }
+
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull" }
+ end
+
+ it_behaves_like 'a forbidden'
+ end
+
+ context 'for public project' do
+ let(:project) { create(:empty_project, :public) }
+
+ context 'when pulling and pushing' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:pull,push" }
+ end
+
+ it_behaves_like 'a pullable'
+ end
+
+ context 'when pushing' do
+ let(:current_params) do
+ { scope: "repository:#{project.path_with_namespace}:push" }
+ end
+
+ it_behaves_like 'a forbidden'
+ end
+ end
+ end
+end
diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb
index ac28b6f71f9..6aa0a89f893 100644
--- a/spec/services/issues/create_service_spec.rb
+++ b/spec/services/issues/create_service_spec.rb
@@ -54,7 +54,7 @@ describe Issues::CreateService, services: true do
label_ids: [label.id] }
end
- it 'does not assign label'do
+ it 'does not assign label' do
expect(issue.labels).to_not include label
end
end
diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb
index 52f69306994..be19be17151 100644
--- a/spec/services/issues/update_service_spec.rb
+++ b/spec/services/issues/update_service_spec.rb
@@ -27,11 +27,6 @@ describe Issues::UpdateService, services: true do
end
end
- def update_issue(opts)
- @issue = Issues::UpdateService.new(project, user, opts).execute(issue)
- @issue.reload
- end
-
context "valid params" do
before do
opts = {
@@ -39,7 +34,8 @@ describe Issues::UpdateService, services: true do
description: 'Also please fix',
assignee_id: user2.id,
state_event: 'close',
- label_ids: [label.id]
+ label_ids: [label.id],
+ confidential: true
}
perform_enqueued_jobs do
@@ -79,13 +75,25 @@ describe Issues::UpdateService, services: true do
end
it 'creates system note about title change' do
- note = find_note('Title changed')
+ note = find_note('Changed title:')
+
+ expect(note).not_to be_nil
+ expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
+ end
+
+ it 'creates system note about confidentiality change' do
+ note = find_note('Made the issue confidential')
expect(note).not_to be_nil
- expect(note.note).to eq 'Title changed from **Old title** to **New title**'
+ expect(note.note).to eq 'Made the issue confidential'
end
end
+ def update_issue(opts)
+ @issue = Issues::UpdateService.new(project, user, opts).execute(issue)
+ @issue.reload
+ end
+
context 'todos' do
let!(:todo) { create(:todo, :assigned, user: user, project: project, target: issue, author: user2) }
diff --git a/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
new file mode 100644
index 00000000000..f70716c9d19
--- /dev/null
+++ b/spec/services/merge_requests/add_todo_when_build_fails_service_spec.rb
@@ -0,0 +1,81 @@
+require 'spec_helper'
+
+# Write specs in this file.
+describe MergeRequests::AddTodoWhenBuildFailsService do
+ let(:user) { create(:user) }
+ let(:merge_request) { create(:merge_request) }
+ let(:project) { create(:project) }
+ let(:sha) { '1234567890abcdef1234567890abcdef12345678' }
+ let(:ci_commit) { create(:ci_commit_with_one_job, ref: merge_request.source_branch, project: project, sha: sha) }
+ let(:service) { MergeRequests::AddTodoWhenBuildFailsService.new(project, user, commit_message: 'Awesome message') }
+ let(:todo_service) { TodoService.new }
+
+ let(:merge_request) do
+ create(:merge_request, merge_user: user, source_branch: 'master',
+ target_branch: 'feature', source_project: project, target_project: project,
+ state: 'opened')
+ end
+
+ before do
+ allow_any_instance_of(MergeRequest).to receive(:ci_commit).and_return(ci_commit)
+ allow(service).to receive(:todo_service).and_return(todo_service)
+ end
+
+ describe '#execute' do
+ context 'commit status with ref' do
+ let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch, commit: ci_commit) }
+
+ it 'notifies the todo service' do
+ expect(todo_service).to receive(:merge_request_build_failed).with(merge_request)
+ service.execute(commit_status)
+ end
+ end
+
+ context 'commit status with non-HEAD ref' do
+ let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch) }
+
+ it 'does not notify the todo service' do
+ expect(todo_service).not_to receive(:merge_request_build_failed)
+ service.execute(commit_status)
+ end
+ end
+
+ context 'commit status without ref' do
+ let(:commit_status) { create(:generic_commit_status) }
+
+ it 'does not notify the todo service' do
+ expect(todo_service).not_to receive(:merge_request_build_failed)
+ service.execute(commit_status)
+ end
+ end
+ end
+
+ describe '#close' do
+ context 'commit status with ref' do
+ let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch, commit: ci_commit) }
+
+ it 'notifies the todo service' do
+ expect(todo_service).to receive(:merge_request_build_retried).with(merge_request)
+ service.close(commit_status)
+ end
+ end
+
+ context 'commit status with non-HEAD ref' do
+ let(:commit_status) { create(:generic_commit_status, ref: merge_request.source_branch) }
+
+ it 'does not notify the todo service' do
+ expect(todo_service).not_to receive(:merge_request_build_retried)
+ service.close(commit_status)
+ end
+ end
+
+ context 'commit status without ref' do
+ let(:commit_status) { create(:generic_commit_status) }
+
+ it 'does not notify the todo service' do
+ expect(todo_service).not_to receive(:merge_request_build_retried)
+ service.close(commit_status)
+ end
+ end
+ end
+end
diff --git a/spec/services/merge_requests/create_service_spec.rb b/spec/services/merge_requests/create_service_spec.rb
index 120f4d6a669..e433f49872d 100644
--- a/spec/services/merge_requests/create_service_spec.rb
+++ b/spec/services/merge_requests/create_service_spec.rb
@@ -12,7 +12,8 @@ describe MergeRequests::CreateService, services: true do
title: 'Awesome merge_request',
description: 'please fix',
source_branch: 'feature',
- target_branch: 'master'
+ target_branch: 'master',
+ force_remove_source_branch: '1'
}
end
@@ -29,6 +30,7 @@ describe MergeRequests::CreateService, services: true do
it { expect(@merge_request).to be_valid }
it { expect(@merge_request.title).to eq('Awesome merge_request') }
it { expect(@merge_request.assignee).to be_nil }
+ it { expect(@merge_request.merge_params['force_remove_source_branch']).to eq('1') }
it 'should execute hooks with default action' do
expect(service).to have_received(:execute_hooks).with(@merge_request)
diff --git a/spec/services/merge_requests/merge_service_spec.rb b/spec/services/merge_requests/merge_service_spec.rb
index ceb3f97280e..1b0396eb686 100644
--- a/spec/services/merge_requests/merge_service_spec.rb
+++ b/spec/services/merge_requests/merge_service_spec.rb
@@ -38,6 +38,21 @@ describe MergeRequests::MergeService, services: true do
end
end
+ context 'remove source branch by author' do
+ let(:service) do
+ merge_request.merge_params['force_remove_source_branch'] = '1'
+ merge_request.save!
+ MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message')
+ end
+
+ it 'removes the source branch' do
+ expect(DeleteBranchService).to receive(:new).
+ with(merge_request.source_project, merge_request.author).
+ and_call_original
+ service.execute(merge_request)
+ end
+ end
+
context "error handling" do
let(:service) { MergeRequests::MergeService.new(project, user, commit_message: 'Awesome message') }
diff --git a/spec/services/merge_requests/refresh_service_spec.rb b/spec/services/merge_requests/refresh_service_spec.rb
index fea8182bd30..31b93850c7c 100644
--- a/spec/services/merge_requests/refresh_service_spec.rb
+++ b/spec/services/merge_requests/refresh_service_spec.rb
@@ -27,6 +27,20 @@ describe MergeRequests::RefreshService, services: true do
target_branch: 'feature',
target_project: @project)
+ @build_failed_todo = create(:todo,
+ :build_failed,
+ user: @user,
+ project: @project,
+ target: @merge_request,
+ author: @user)
+
+ @fork_build_failed_todo = create(:todo,
+ :build_failed,
+ user: @user,
+ project: @project,
+ target: @merge_request,
+ author: @user)
+
@commits = @merge_request.commits
@oldrev = @commits.last.id
@@ -51,6 +65,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.merge_when_build_succeeds).to be_falsey}
it { expect(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
+ it { expect(@build_failed_todo).to be_done }
+ it { expect(@fork_build_failed_todo).to be_done }
end
context 'push to origin repo target branch' do
@@ -63,6 +79,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request).to be_merged }
it { expect(@fork_merge_request).to be_merged }
it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@build_failed_todo).to be_pending }
+ it { expect(@fork_build_failed_todo).to be_pending }
end
context 'manual merge of source branch' do
@@ -82,6 +100,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request.diffs.size).to be > 0 }
it { expect(@fork_merge_request).to be_merged }
it { expect(@fork_merge_request.notes.last.note).to include('changed to merged') }
+ it { expect(@build_failed_todo).to be_pending }
+ it { expect(@fork_build_failed_todo).to be_pending }
end
context 'push to fork repo source branch' do
@@ -101,6 +121,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request).to be_open }
it { expect(@fork_merge_request.notes.last.note).to include('Added 4 commits') }
it { expect(@fork_merge_request).to be_open }
+ it { expect(@build_failed_todo).to be_pending }
+ it { expect(@fork_build_failed_todo).to be_pending }
end
context 'push to fork repo target branch' do
@@ -113,6 +135,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
it { expect(@fork_merge_request).to be_open }
+ it { expect(@build_failed_todo).to be_pending }
+ it { expect(@fork_build_failed_todo).to be_pending }
end
context 'push to origin repo target branch after fork project was removed' do
@@ -126,6 +150,8 @@ describe MergeRequests::RefreshService, services: true do
it { expect(@merge_request).to be_merged }
it { expect(@fork_merge_request).to be_open }
it { expect(@fork_merge_request.notes).to be_empty }
+ it { expect(@build_failed_todo).to be_pending }
+ it { expect(@fork_build_failed_todo).to be_pending }
end
context 'push new branch that exists in a merge request' do
@@ -153,6 +179,8 @@ describe MergeRequests::RefreshService, services: true do
def reload_mrs
@merge_request.reload
@fork_merge_request.reload
+ @build_failed_todo.reload
+ @fork_build_failed_todo.reload
end
end
end
diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb
index 213e8c2eb3a..d4ebe28c276 100644
--- a/spec/services/merge_requests/update_service_spec.rb
+++ b/spec/services/merge_requests/update_service_spec.rb
@@ -39,7 +39,8 @@ describe MergeRequests::UpdateService, services: true do
assignee_id: user2.id,
state_event: 'close',
label_ids: [label.id],
- target_branch: 'target'
+ target_branch: 'target',
+ force_remove_source_branch: '1'
}
end
@@ -61,6 +62,7 @@ describe MergeRequests::UpdateService, services: true do
it { expect(@merge_request.labels.count).to eq(1) }
it { expect(@merge_request.labels.first.title).to eq(label.name) }
it { expect(@merge_request.target_branch).to eq('target') }
+ it { expect(@merge_request.merge_params['force_remove_source_branch']).to eq('1') }
it 'should execute hooks with update action' do
expect(service).to have_received(:execute_hooks).
@@ -90,10 +92,10 @@ describe MergeRequests::UpdateService, services: true do
end
it 'creates system note about title change' do
- note = find_note('Title changed')
+ note = find_note('Changed title:')
expect(note).not_to be_nil
- expect(note.note).to eq 'Title changed from **Old title** to **New title**'
+ expect(note.note).to eq 'Changed title: **{-Old-} title** → **{+New+} title**'
end
it 'creates system note about branch change' do
diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb
index 4bbc4ddc3ab..cef5e0d8659 100644
--- a/spec/services/notification_service_spec.rb
+++ b/spec/services/notification_service_spec.rb
@@ -66,6 +66,7 @@ describe NotificationService, services: true do
should_email(@subscriber)
should_email(@watcher_and_subscriber)
should_email(@subscribed_participant)
+ should_not_email(@u_guest_watcher)
should_not_email(note.author)
should_not_email(@u_participating)
should_not_email(@u_disabled)
@@ -100,6 +101,7 @@ describe NotificationService, services: true do
should_email(note.noteable.author)
should_email(note.noteable.assignee)
should_email(@u_mentioned)
+ should_not_email(@u_guest_watcher)
should_not_email(@u_watcher)
should_not_email(note.author)
should_not_email(@u_participating)
@@ -160,6 +162,7 @@ describe NotificationService, services: true do
should_email(member)
end
+ should_email(@u_guest_watcher)
should_email(note.noteable.author)
should_email(note.noteable.assignee)
should_not_email(note.author)
@@ -201,6 +204,7 @@ describe NotificationService, services: true do
should_email(member)
end
+ should_email(@u_guest_watcher)
should_email(note.noteable.author)
should_not_email(note.author)
should_email(@u_mentioned)
@@ -224,6 +228,7 @@ describe NotificationService, services: true do
it do
notification.new_note(note)
+ should_email(@u_guest_watcher)
should_email(@u_committer)
should_email(@u_watcher)
should_not_email(@u_mentioned)
@@ -236,6 +241,7 @@ describe NotificationService, services: true do
note.update_attribute(:note, '@mention referenced')
notification.new_note(note)
+ should_email(@u_guest_watcher)
should_email(@u_committer)
should_email(@u_watcher)
should_email(@u_mentioned)
@@ -269,6 +275,7 @@ describe NotificationService, services: true do
should_email(issue.assignee)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_not_email(@u_mentioned)
should_not_email(@u_participating)
@@ -328,6 +335,7 @@ describe NotificationService, services: true do
should_email(issue.assignee)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_not_email(@unsubscriber)
@@ -342,6 +350,7 @@ describe NotificationService, services: true do
should_email(@u_mentioned)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_not_email(@unsubscriber)
@@ -356,6 +365,7 @@ describe NotificationService, services: true do
expect(issue.assignee).to be @u_mentioned
should_email(issue.assignee)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_not_email(@unsubscriber)
@@ -370,6 +380,7 @@ describe NotificationService, services: true do
expect(issue.assignee).to be @u_mentioned
should_email(issue.assignee)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_not_email(@unsubscriber)
@@ -383,6 +394,7 @@ describe NotificationService, services: true do
expect(issue.assignee).to be @u_mentioned
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_not_email(issue.assignee)
@@ -411,6 +423,7 @@ describe NotificationService, services: true do
should_not_email(issue.assignee)
should_not_email(issue.author)
should_not_email(@u_watcher)
+ should_not_email(@u_guest_watcher)
should_not_email(@u_participant_mentioned)
should_not_email(@subscriber)
should_not_email(@watcher_and_subscriber)
@@ -459,6 +472,7 @@ describe NotificationService, services: true do
should_email(issue.assignee)
should_email(issue.author)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
@@ -475,6 +489,7 @@ describe NotificationService, services: true do
should_email(issue.assignee)
should_email(issue.author)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
@@ -502,6 +517,7 @@ describe NotificationService, services: true do
should_email(@u_watcher)
should_email(@watcher_and_subscriber)
should_email(@u_participant_mentioned)
+ should_email(@u_guest_watcher)
should_not_email(@u_participating)
should_not_email(@u_disabled)
end
@@ -525,6 +541,7 @@ describe NotificationService, services: true do
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
+ should_email(@u_guest_watcher)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
@@ -566,6 +583,7 @@ describe NotificationService, services: true do
should_email(merge_request.assignee)
should_email(@u_watcher)
+ should_email(@u_guest_watcher)
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
@@ -584,6 +602,7 @@ describe NotificationService, services: true do
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
+ should_email(@u_guest_watcher)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
@@ -599,6 +618,7 @@ describe NotificationService, services: true do
should_email(@u_participant_mentioned)
should_email(@subscriber)
should_email(@watcher_and_subscriber)
+ should_email(@u_guest_watcher)
should_not_email(@unsubscriber)
should_not_email(@u_participating)
should_not_email(@u_disabled)
@@ -620,6 +640,7 @@ describe NotificationService, services: true do
should_email(@u_watcher)
should_email(@u_participating)
+ should_not_email(@u_guest_watcher)
should_not_email(@u_disabled)
end
end
@@ -635,6 +656,8 @@ describe NotificationService, services: true do
@u_not_mentioned = create(:user, username: 'regular', notification_level: :participating)
@u_outsider_mentioned = create(:user, username: 'outsider')
+ create_guest_watcher
+
project.team << [@u_watcher, :master]
project.team << [@u_participating, :master]
project.team << [@u_participant_mentioned, :master]
@@ -644,6 +667,13 @@ describe NotificationService, services: true do
project.team << [@u_not_mentioned, :master]
end
+ def create_guest_watcher
+ @u_guest_watcher = create(:user, username: 'guest_watching')
+ setting = @u_guest_watcher.notification_settings_for(project)
+ setting.level = :watch
+ setting.save
+ end
+
def add_users_with_subscription(project, issuable)
@subscriber = create :user
@unsubscriber = create :user
diff --git a/spec/services/projects/create_service_spec.rb b/spec/services/projects/create_service_spec.rb
index e43903dbd3c..fd114359467 100644
--- a/spec/services/projects/create_service_spec.rb
+++ b/spec/services/projects/create_service_spec.rb
@@ -64,7 +64,7 @@ describe Projects::CreateService, services: true do
@path = ProjectWiki.new(@project, @user).send(:path_to_repo)
end
- it { expect(File.exists?(@path)).to be_truthy }
+ it { expect(File.exist?(@path)).to be_truthy }
end
context 'wiki_enabled false does not create wiki repository directory' do
@@ -74,7 +74,7 @@ describe Projects::CreateService, services: true do
@path = ProjectWiki.new(@project, @user).send(:path_to_repo)
end
- it { expect(File.exists?(@path)).to be_falsey }
+ it { expect(File.exist?(@path)).to be_falsey }
end
end
diff --git a/spec/services/projects/destroy_service_spec.rb b/spec/services/projects/destroy_service_spec.rb
index 1ec27077717..29341c5e57e 100644
--- a/spec/services/projects/destroy_service_spec.rb
+++ b/spec/services/projects/destroy_service_spec.rb
@@ -13,8 +13,8 @@ describe Projects::DestroyService, services: true do
end
it { expect(Project.all).not_to include(project) }
- it { expect(Dir.exists?(path)).to be_falsey }
- it { expect(Dir.exists?(remove_path)).to be_falsey }
+ it { expect(Dir.exist?(path)).to be_falsey }
+ it { expect(Dir.exist?(remove_path)).to be_falsey }
end
context 'Sidekiq fake' do
@@ -24,8 +24,31 @@ describe Projects::DestroyService, services: true do
end
it { expect(Project.all).not_to include(project) }
- it { expect(Dir.exists?(path)).to be_falsey }
- it { expect(Dir.exists?(remove_path)).to be_truthy }
+ it { expect(Dir.exist?(path)).to be_falsey }
+ it { expect(Dir.exist?(remove_path)).to be_truthy }
+ end
+
+ context 'container registry' do
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags('tag')
+ end
+
+ context 'tags deletion succeeds' do
+ it do
+ expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete).and_return(true)
+
+ destroy_project(project, user, {})
+ end
+ end
+
+ context 'tags deletion fails' do
+ before { expect_any_instance_of(ContainerRegistry::Tag).to receive(:delete).and_return(false) }
+
+ subject { destroy_project(project, user, {}) }
+
+ it { expect{subject}.to raise_error(Projects::DestroyService::DestroyError) }
+ end
end
def destroy_project(project, user, params)
diff --git a/spec/services/projects/transfer_service_spec.rb b/spec/services/projects/transfer_service_spec.rb
index 06017317339..d5aa115a074 100644
--- a/spec/services/projects/transfer_service_spec.rb
+++ b/spec/services/projects/transfer_service_spec.rb
@@ -26,6 +26,17 @@ describe Projects::TransferService, services: true do
it { expect(project.namespace).to eq(user.namespace) }
end
+ context 'disallow transfering of project with tags' do
+ before do
+ stub_container_registry_config(enabled: true)
+ stub_container_registry_tags('tag')
+ end
+
+ subject { transfer_project(project, user, group) }
+
+ it { is_expected.to be_falsey }
+ end
+
context 'namespace -> not allowed namespace' do
before do
@result = transfer_project(project, user, group)
diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb
index 5fbf2ae5247..ee976eb2926 100644
--- a/spec/services/system_note_service_spec.rb
+++ b/spec/services/system_note_service_spec.rb
@@ -241,15 +241,19 @@ describe SystemNoteService, services: true do
it 'sets the note text' do
expect(subject.note).
- to eq "Title changed from **Old title** to **#{noteable.title}**"
+ to eq "Changed title: **{-Old title-}** → **{+#{noteable.title}+}**"
end
end
+ end
- context 'when noteable does not respond to `title' do
- let(:noteable) { double('noteable') }
+ describe '.change_issue_confidentiality' do
+ subject { described_class.change_issue_confidentiality(noteable, project, author) }
- it 'returns nil' do
- expect(subject).to be_nil
+ context 'when noteable responds to `confidential`' do
+ it_behaves_like 'a system note'
+
+ it 'sets the note text' do
+ expect(subject.note).to eq 'Made the issue visible'
end
end
end
diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb
index a075496ee63..42147736532 100644
--- a/spec/services/todo_service_spec.rb
+++ b/spec/services/todo_service_spec.rb
@@ -305,6 +305,25 @@ describe TodoService, services: true do
expect(second_todo.reload).to be_done
end
end
+
+ describe '#merge_request_build_failed' do
+ it 'creates a pending todo for the merge request author' do
+ service.merge_request_build_failed(mr_unassigned)
+
+ should_create_todo(user: author, target: mr_unassigned, action: Todo::BUILD_FAILED)
+ end
+ end
+
+ describe '#merge_request_push' do
+ it 'marks related pending todos to the target for the user as done' do
+ first_todo = create(:todo, :build_failed, user: author, project: project, target: mr_assigned, author: john_doe)
+ second_todo = create(:todo, :build_failed, user: john_doe, project: project, target: mr_assigned, author: john_doe)
+ service.merge_request_push(mr_assigned, author)
+
+ expect(first_todo.reload).to be_done
+ expect(second_todo.reload).not_to be_done
+ end
+ end
end
def should_create_todo(attributes = {})
diff --git a/spec/support/jira_service_helper.rb b/spec/support/jira_service_helper.rb
index a3f496359b1..5ebe095743b 100644
--- a/spec/support/jira_service_helper.rb
+++ b/spec/support/jira_service_helper.rb
@@ -2,11 +2,11 @@ module JiraServiceHelper
def jira_service_settings
properties = {
- "title"=>"JIRA tracker",
- "project_url"=>"http://jira.example/issues/?jql=project=A",
- "issues_url"=>"http://jira.example/browse/JIRA-1",
- "new_issue_url"=>"http://jira.example/secure/CreateIssue.jspa",
- "api_url"=>"http://jira.example/rest/api/2"
+ "title" => "JIRA tracker",
+ "project_url" => "http://jira.example/issues/?jql=project=A",
+ "issues_url" => "http://jira.example/browse/JIRA-1",
+ "new_issue_url" => "http://jira.example/secure/CreateIssue.jspa",
+ "api_url" => "http://jira.example/rest/api/2"
}
jira_tracker.update_attributes(properties: properties, active: true)
diff --git a/spec/support/markdown_feature.rb b/spec/support/markdown_feature.rb
index b87cd6bbca2..7fc6d6fcc5e 100644
--- a/spec/support/markdown_feature.rb
+++ b/spec/support/markdown_feature.rb
@@ -63,8 +63,12 @@ class MarkdownFeature
@label ||= create(:label, name: 'awaiting feedback', project: project)
end
+ def simple_milestone
+ @simple_milestone ||= create(:milestone, name: 'gfm-milestone', project: project)
+ end
+
def milestone
- @milestone ||= create(:milestone, project: project)
+ @milestone ||= create(:milestone, name: 'next goal', project: project)
end
# Cross-references -----------------------------------------------------------
diff --git a/spec/support/matchers/markdown_matchers.rb b/spec/support/matchers/markdown_matchers.rb
index 43cb6ef43f2..e005058ba5b 100644
--- a/spec/support/matchers/markdown_matchers.rb
+++ b/spec/support/matchers/markdown_matchers.rb
@@ -154,7 +154,7 @@ module MarkdownMatchers
set_default_markdown_messages
match do |actual|
- expect(actual).to have_selector('a.gfm.gfm-milestone', count: 3)
+ expect(actual).to have_selector('a.gfm.gfm-milestone', count: 6)
end
end
@@ -168,6 +168,16 @@ module MarkdownMatchers
expect(actual).to have_selector('input[checked]', count: 3)
end
end
+
+ # InlineDiffFilter
+ matcher :parse_inline_diffs do
+ set_default_markdown_messages
+
+ match do |actual|
+ expect(actual).to have_selector('span.idiff.addition', count: 2)
+ expect(actual).to have_selector('span.idiff.deletion', count: 2)
+ end
+ end
end
# Monkeypatch the matcher DSL so that we can reduce some noisy duplication for
diff --git a/spec/support/stub_gitlab_calls.rb b/spec/support/stub_gitlab_calls.rb
index eec2e681117..f73416a3d0f 100644
--- a/spec/support/stub_gitlab_calls.rb
+++ b/spec/support/stub_gitlab_calls.rb
@@ -25,6 +25,23 @@ module StubGitlabCalls
allow_any_instance_of(Project).to receive(:builds_enabled?).and_return(false)
end
+ def stub_container_registry_config(registry_settings)
+ allow(Gitlab.config.registry).to receive_messages(registry_settings)
+ allow(Auth::ContainerRegistryAuthenticationService).to receive(:full_access_token).and_return('token')
+ end
+
+ def stub_container_registry_tags(*tags)
+ allow_any_instance_of(ContainerRegistry::Client).to receive(:repository_tags).and_return(
+ { "tags" => tags }
+ )
+ allow_any_instance_of(ContainerRegistry::Client).to receive(:repository_manifest).and_return(
+ JSON.load(File.read(Rails.root + 'spec/fixtures/container_registry/tag_manifest.json'))
+ )
+ allow_any_instance_of(ContainerRegistry::Client).to receive(:blob).and_return(
+ File.read(Rails.root + 'spec/fixtures/container_registry/config_blob.json')
+ )
+ end
+
private
def gitlab_url
@@ -36,20 +53,20 @@ module StubGitlabCalls
stub_request(:post, "#{gitlab_url}api/v3/session.json").
with(body: "{\"email\":\"test@test.com\",\"password\":\"123456\"}",
- headers: { 'Content-Type'=>'application/json' }).
- to_return(status: 201, body: f, headers: { 'Content-Type'=>'application/json' })
+ headers: { 'Content-Type' => 'application/json' }).
+ to_return(status: 201, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_user
f = File.read(Rails.root.join('spec/support/gitlab_stubs/user.json'))
stub_request(:get, "#{gitlab_url}api/v3/user?private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type'=>'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' })
+ with(headers: { 'Content-Type' => 'application/json' }).
+ to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
stub_request(:get, "#{gitlab_url}api/v3/user?access_token=some_token").
- with(headers: { 'Content-Type'=>'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' })
+ with(headers: { 'Content-Type' => 'application/json' }).
+ to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_project_8
@@ -66,19 +83,19 @@ module StubGitlabCalls
f = File.read(Rails.root.join('spec/support/gitlab_stubs/projects.json'))
stub_request(:get, "#{gitlab_url}api/v3/projects.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type'=>'application/json' }).
- to_return(status: 200, body: f, headers: { 'Content-Type'=>'application/json' })
+ with(headers: { 'Content-Type' => 'application/json' }).
+ to_return(status: 200, body: f, headers: { 'Content-Type' => 'application/json' })
end
def stub_projects_owned
stub_request(:get, "#{gitlab_url}api/v3/projects/owned.json?archived=false&ci_enabled_first=true&private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type'=>'application/json' }).
+ with(headers: { 'Content-Type' => 'application/json' }).
to_return(status: 200, body: "", headers: {})
end
def stub_ci_enable
stub_request(:put, "#{gitlab_url}api/v3/projects/2/services/gitlab-ci.json?private_token=Wvjy2Krpb7y8xi93owUz").
- with(headers: { 'Content-Type'=>'application/json' }).
+ with(headers: { 'Content-Type' => 'application/json' }).
to_return(status: 200, body: "", headers: {})
end
diff --git a/spec/tasks/gitlab/backup_rake_spec.rb b/spec/tasks/gitlab/backup_rake_spec.rb
index 05fc4c4554f..8aeb013eec6 100644
--- a/spec/tasks/gitlab/backup_rake_spec.rb
+++ b/spec/tasks/gitlab/backup_rake_spec.rb
@@ -21,7 +21,7 @@ describe 'gitlab:app namespace rake task' do
end
def reenable_backup_sub_tasks
- %w{db repo uploads builds artifacts lfs}.each do |subtask|
+ %w{db repo uploads builds artifacts lfs registry}.each do |subtask|
Rake::Task["gitlab:backup:#{subtask}:create"].reenable
end
end
@@ -65,6 +65,7 @@ describe 'gitlab:app namespace rake task' do
expect(Rake::Task['gitlab:backup:uploads:restore']).to receive(:invoke)
expect(Rake::Task['gitlab:backup:artifacts:restore']).to receive(:invoke)
expect(Rake::Task['gitlab:backup:lfs:restore']).to receive(:invoke)
+ expect(Rake::Task['gitlab:backup:registry:restore']).to receive(:invoke)
expect(Rake::Task['gitlab:shell:setup']).to receive(:invoke)
expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
end
@@ -122,7 +123,7 @@ describe 'gitlab:app namespace rake task' do
it 'should set correct permissions on the tar contents' do
tar_contents, exit_status = Gitlab::Popen.popen(
- %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz}
+ %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz registry.tar.gz}
)
expect(exit_status).to eq(0)
expect(tar_contents).to match('db/')
@@ -131,12 +132,13 @@ describe 'gitlab:app namespace rake task' do
expect(tar_contents).to match('builds.tar.gz')
expect(tar_contents).to match('artifacts.tar.gz')
expect(tar_contents).to match('lfs.tar.gz')
- expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz)\/$/)
+ expect(tar_contents).to match('registry.tar.gz')
+ expect(tar_contents).not_to match(/^.{4,9}[rwx].* (database.sql.gz|uploads.tar.gz|repositories|builds.tar.gz|artifacts.tar.gz|registry.tar.gz)\/$/)
end
it 'should delete temp directories' do
temp_dirs = Dir.glob(
- File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts,lfs}')
+ File.join(Gitlab.config.backup.path, '{db,repositories,uploads,builds,artifacts,lfs,registry}')
)
expect(temp_dirs).to be_empty
@@ -172,7 +174,7 @@ describe 'gitlab:app namespace rake task' do
it "does not contain skipped item" do
tar_contents, _exit_status = Gitlab::Popen.popen(
- %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz}
+ %W{tar -tvf #{@backup_tar} db uploads.tar.gz repositories builds.tar.gz artifacts.tar.gz lfs.tar.gz registry.tar.gz}
)
expect(tar_contents).to match('db/')
@@ -180,6 +182,7 @@ describe 'gitlab:app namespace rake task' do
expect(tar_contents).to match('builds.tar.gz')
expect(tar_contents).to match('artifacts.tar.gz')
expect(tar_contents).to match('lfs.tar.gz')
+ expect(tar_contents).to match('registry.tar.gz')
expect(tar_contents).not_to match('repositories/')
end
@@ -195,6 +198,7 @@ describe 'gitlab:app namespace rake task' do
expect(Rake::Task['gitlab:backup:builds:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:artifacts:restore']).to receive :invoke
expect(Rake::Task['gitlab:backup:lfs:restore']).to receive :invoke
+ expect(Rake::Task['gitlab:backup:registry:restore']).to receive :invoke
expect(Rake::Task['gitlab:shell:setup']).to receive :invoke
expect { run_rake_task('gitlab:backup:restore') }.not_to raise_error
end
diff --git a/spec/workers/emails_on_push_worker_spec.rb b/spec/workers/emails_on_push_worker_spec.rb
index 3600c771075..439da765c2c 100644
--- a/spec/workers/emails_on_push_worker_spec.rb
+++ b/spec/workers/emails_on_push_worker_spec.rb
@@ -6,29 +6,66 @@ describe EmailsOnPushWorker do
let(:project) { create(:project) }
let(:user) { create(:user) }
let(:data) { Gitlab::PushDataBuilder.build_sample(project, user) }
+ let(:recipients) { user.email }
+ let(:perform) { subject.perform(project.id, recipients, data.stringify_keys) }
subject { EmailsOnPushWorker.new }
- before do
- allow(Project).to receive(:find).and_return(project)
- end
-
describe "#perform" do
- it "sends mail" do
- subject.perform(project.id, user.email, data.stringify_keys)
+ context "when there are no errors in sending" do
+ let(:email) { ActionMailer::Base.deliveries.last }
+
+ before { perform }
- email = ActionMailer::Base.deliveries.last
- expect(email.subject).to include('Change some files')
- expect(email.to).to eq([user.email])
+ it "sends a mail with the correct subject" do
+ expect(email.subject).to include('Change some files')
+ end
+
+ it "sends the mail to the correct recipient" do
+ expect(email.to).to eq([user.email])
+ end
end
- it "gracefully handles an input SMTP error" do
- ActionMailer::Base.deliveries.clear
- allow(Notify).to receive(:repository_push_email).and_raise(Net::SMTPFatalError)
+ context "when there is an SMTP error" do
+ before do
+ ActionMailer::Base.deliveries.clear
+ allow(Notify).to receive(:repository_push_email).and_raise(Net::SMTPFatalError)
+ perform
+ end
+
+ it "gracefully handles an input SMTP error" do
+ expect(ActionMailer::Base.deliveries.count).to eq(0)
+ end
+ end
+
+ context "when there are multiple recipients" do
+ let(:recipients) do
+ 1.upto(5).map { |i| user.email.sub('@', "+#{i}@") }.join("\n")
+ end
+
+ before do
+ # This is a hack because we modify the mail object before sending, for efficency,
+ # but the TestMailer adapter just appends the objects to an array. To clone a mail
+ # object, create a new one!
+ # https://github.com/mikel/mail/issues/314#issuecomment-12750108
+ allow_any_instance_of(Mail::TestMailer).to receive(:deliver!).and_wrap_original do |original, mail|
+ original.call(Mail.new(mail.encoded))
+ end
+
+ ActionMailer::Base.deliveries.clear
+ end
- subject.perform(project.id, user.email, data.stringify_keys)
+ it "sends the mail to each of the recipients" do
+ perform
+ expect(ActionMailer::Base.deliveries.count).to eq(5)
+ expect(ActionMailer::Base.deliveries.map(&:to).flatten).to contain_exactly(*recipients.split)
+ end
- expect(ActionMailer::Base.deliveries.count).to eq(0)
+ it "only generates the mail once" do
+ expect(Notify).to receive(:repository_push_email).once.and_call_original
+ expect(Premailer::Rails::CustomizedPremailer).to receive(:new).once.and_call_original
+ perform
+ end
end
end
end
diff --git a/spec/workers/post_receive_spec.rb b/spec/workers/post_receive_spec.rb
index 94ff3457902..2f465bcf1e3 100644
--- a/spec/workers/post_receive_spec.rb
+++ b/spec/workers/post_receive_spec.rb
@@ -48,6 +48,22 @@ describe PostReceive do
PostReceive.new.perform(pwd(project), key_id, base64_changes)
end
end
+
+ context "gitlab-ci.yml" do
+ subject { PostReceive.new.perform(pwd(project), key_id, base64_changes) }
+
+ context "creates a Ci::Commit for every change" do
+ before { stub_ci_commit_to_return_yaml_file }
+
+ it { expect{ subject }.to change{ Ci::Commit.count }.by(2) }
+ end
+
+ context "does not create a Ci::Commit" do
+ before { stub_ci_commit_yaml_file(nil) }
+
+ it { expect{ subject }.to_not change{ Ci::Commit.count } }
+ end
+ end
end
context "webhook" do
diff --git a/spec/workers/repository_import_worker_spec.rb b/spec/workers/repository_import_worker_spec.rb
index 6739063543b..f1b1574abf4 100644
--- a/spec/workers/repository_import_worker_spec.rb
+++ b/spec/workers/repository_import_worker_spec.rb
@@ -6,14 +6,28 @@ describe RepositoryImportWorker do
subject { described_class.new }
describe '#perform' do
- it 'imports a project' do
- expect_any_instance_of(Projects::ImportService).to receive(:execute).
- and_return({ status: :ok })
+ context 'when the import was successful' do
+ it 'imports a project' do
+ expect_any_instance_of(Projects::ImportService).to receive(:execute).
+ and_return({ status: :ok })
- expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
- expect_any_instance_of(Project).to receive(:import_finish)
+ expect_any_instance_of(Repository).to receive(:expire_emptiness_caches)
+ expect_any_instance_of(Project).to receive(:import_finish)
- subject.perform(project.id)
+ subject.perform(project.id)
+ end
+ end
+
+ context 'when the import has failed' do
+ it 'hide the credentials that were used in the import URL' do
+ error = %Q{remote: Not Found fatal: repository 'https://user:pass@test.com/root/repoC.git/' not found }
+ expect_any_instance_of(Projects::ImportService).to receive(:execute).
+ and_return({ status: :error, message: error })
+
+ subject.perform(project.id)
+
+ expect(project.reload.import_error).to include("https://*****:*****@test.com/root/repoC.git/")
+ end
end
end
end