diff options
author | Zeger-Jan van de Weg <zegerjan@gitlab.com> | 2016-05-11 08:47:04 +0200 |
---|---|---|
committer | Zeger-Jan van de Weg <zegerjan@gitlab.com> | 2016-05-11 08:47:04 +0200 |
commit | 7e6dcf9cd0626c6d0cbbe96ae5327048d2c6849f (patch) | |
tree | 33f22befc61e52178098bf77e57bba7a96aaae86 /spec | |
parent | dccf8a9fc8d4dde91942944f6b47387bfb13c380 (diff) | |
parent | 98d8e3fe9ff4d120469378490c41381ae751597e (diff) | |
download | gitlab-ce-7e6dcf9cd0626c6d0cbbe96ae5327048d2c6849f.tar.gz |
Merge branch 'master' into awardables
Diffstat (limited to 'spec')
198 files changed, 5172 insertions, 1732 deletions
diff --git a/spec/controllers/admin/impersonation_controller_spec.rb b/spec/controllers/admin/impersonation_controller_spec.rb deleted file mode 100644 index d7a7ba1c5b6..00000000000 --- a/spec/controllers/admin/impersonation_controller_spec.rb +++ /dev/null @@ -1,19 +0,0 @@ -require 'spec_helper' - -describe Admin::ImpersonationController do - let(:admin) { create(:admin) } - - before do - sign_in(admin) - end - - describe 'CREATE #impersonation when blocked' do - let(:blocked_user) { create(:user, state: :blocked) } - - it 'does not allow impersonation' do - post :create, id: blocked_user.username - - expect(flash[:alert]).to eq 'You cannot impersonate a blocked user' - end - end -end diff --git a/spec/controllers/admin/impersonations_controller_spec.rb b/spec/controllers/admin/impersonations_controller_spec.rb new file mode 100644 index 00000000000..eb82476b179 --- /dev/null +++ b/spec/controllers/admin/impersonations_controller_spec.rb @@ -0,0 +1,95 @@ +require 'spec_helper' + +describe Admin::ImpersonationsController do + let(:impersonator) { create(:admin) } + let(:user) { create(:user) } + + describe "DELETE destroy" do + context "when not signed in" do + it "redirects to the sign in page" do + delete :destroy + + expect(response).to redirect_to(new_user_session_path) + end + end + + context "when signed in" do + before do + sign_in(user) + end + + context "when not impersonating" do + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when impersonating" do + before do + session[:impersonator_id] = impersonator.id + end + + context "when the impersonator is not admin (anymore)" do + before do + impersonator.admin = false + impersonator.save + end + + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when the impersonator is admin" do + context "when the impersonator is blocked" do + before do + impersonator.block! + end + + it "responds with status 404" do + delete :destroy + + expect(response.status).to eq(404) + end + + it "doesn't sign us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(user) + end + end + + context "when the impersonator is not blocked" do + it "redirects to the impersonated user's page" do + delete :destroy + + expect(response).to redirect_to(admin_user_path(user)) + end + + it "signs us in as the impersonator" do + delete :destroy + + expect(warden.user).to eq(impersonator) + end + end + end + end + end + end +end diff --git a/spec/controllers/admin/users_controller_spec.rb b/spec/controllers/admin/users_controller_spec.rb index 9ef8ba1b097..ce2a62ae1fd 100644 --- a/spec/controllers/admin/users_controller_spec.rb +++ b/spec/controllers/admin/users_controller_spec.rb @@ -2,9 +2,10 @@ require 'spec_helper' describe Admin::UsersController do let(:user) { create(:user) } + let(:admin) { create(:admin) } before do - sign_in(create(:admin)) + sign_in(admin) end describe 'DELETE #user with projects' do @@ -112,4 +113,50 @@ describe Admin::UsersController do patch :disable_two_factor, id: user.to_param end end + + describe "POST impersonate" do + context "when the user is blocked" do + before do + user.block! + end + + it "shows a notice" do + post :impersonate, id: user.username + + expect(flash[:alert]).to eq("You cannot impersonate a blocked user") + end + + it "doesn't sign us in as the user" do + post :impersonate, id: user.username + + expect(warden.user).to eq(admin) + end + end + + context "when the user is not blocked" do + it "stores the impersonator in the session" do + post :impersonate, id: user.username + + expect(session[:impersonator_id]).to eq(admin.id) + end + + it "signs us in as the user" do + post :impersonate, id: user.username + + expect(warden.user).to eq(user) + end + + it "redirects to root" do + post :impersonate, id: user.username + + expect(response).to redirect_to(root_path) + end + + it "shows a notice" do + post :impersonate, id: user.username + + expect(flash[:alert]).to eq("You are now impersonating #{user.username}") + end + end + end end diff --git a/spec/controllers/commit_controller_spec.rb b/spec/controllers/commit_controller_spec.rb index f09e4fcb154..cf5c606c723 100644 --- a/spec/controllers/commit_controller_spec.rb +++ b/spec/controllers/commit_controller_spec.rb @@ -4,6 +4,8 @@ describe Projects::CommitController do let(:project) { create(:project) } let(:user) { create(:user) } let(:commit) { project.commit("master") } + let(:master_pickable_sha) { '7d3b0f7cff5f37573aea97cebfd5692ea1689924' } + let(:master_pickable_commit) { project.commit(master_pickable_sha) } before do sign_in(user) @@ -192,4 +194,53 @@ describe Projects::CommitController do end end end + + describe '#cherry_pick' do + context 'when target branch is not provided' do + it 'should render the 404 page' do + post(:cherry_pick, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: master_pickable_commit.id) + + expect(response).not_to be_success + expect(response.status).to eq(404) + end + end + + context 'when the cherry-pick was successful' do + it 'should redirect to the commits page' do + post(:cherry_pick, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + target_branch: 'master', + id: master_pickable_commit.id) + + expect(response).to redirect_to namespace_project_commits_path(project.namespace, project, 'master') + expect(flash[:notice]).to eq('The commit has been successfully cherry-picked.') + end + end + + context 'when the cherry_pick failed' do + before do + post(:cherry_pick, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + target_branch: 'master', + id: master_pickable_commit.id) + end + + it 'should redirect to the commit page' do + # Cherry-picking a commit that has been already cherry-picked. + post(:cherry_pick, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + target_branch: 'master', + id: master_pickable_commit.id) + + expect(response).to redirect_to namespace_project_commit_path(project.namespace, project, master_pickable_commit.id) + expect(flash[:alert]).to match('Sorry, we cannot cherry-pick this commit automatically.') + end + end + end end diff --git a/spec/controllers/groups/group_members_controller_spec.rb b/spec/controllers/groups/group_members_controller_spec.rb new file mode 100644 index 00000000000..a5986598715 --- /dev/null +++ b/spec/controllers/groups/group_members_controller_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +describe Groups::GroupMembersController do + let(:user) { create(:user) } + let(:group) { create(:group) } + + context "index" do + before do + group.add_owner(user) + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + it 'renders index with group members' do + get :index, group_id: group.path + + expect(response.status).to eq(200) + expect(response).to render_template(:index) + end + end +end diff --git a/spec/controllers/import/github_controller_spec.rb b/spec/controllers/import/github_controller_spec.rb index bbf8adef534..bcc713dce2a 100644 --- a/spec/controllers/import/github_controller_spec.rb +++ b/spec/controllers/import/github_controller_spec.rb @@ -22,6 +22,8 @@ describe Import::GithubController do token = "asdasd12345" allow_any_instance_of(Gitlab::GithubImport::Client). to receive(:get_token).and_return(token) + allow_any_instance_of(Gitlab::GithubImport::Client). + to receive(:github_options).and_return({}) stub_omniauth_provider('github') get :callback diff --git a/spec/controllers/projects/group_links_controller_spec.rb b/spec/controllers/projects/group_links_controller_spec.rb new file mode 100644 index 00000000000..40bd83af861 --- /dev/null +++ b/spec/controllers/projects/group_links_controller_spec.rb @@ -0,0 +1,50 @@ +require 'spec_helper' + +describe Projects::GroupLinksController do + let(:project) { create(:project, :private) } + let(:group) { create(:group, :private) } + let(:user) { create(:user) } + + before do + project.team << [user, :master] + sign_in(user) + end + + describe '#create' do + shared_context 'link project to group' do + before do + post(:create, namespace_id: project.namespace.to_param, + project_id: project.to_param, + link_group_id: group.id, + link_group_access: ProjectGroupLink.default_access) + end + end + + context 'when user has access to group he want to link project to' do + before { group.add_developer(user) } + include_context 'link project to group' + + it 'links project with selected group' do + expect(group.shared_projects).to include project + end + + it 'redirects to project group links page'do + expect(response).to redirect_to( + namespace_project_group_links_path(project.namespace, project) + ) + end + end + + context 'when user doers not have access to group he want to link to' do + include_context 'link project to group' + + it 'renders 404' do + expect(response.status).to eq 404 + end + + it 'does not share project with that group' do + expect(group.shared_projects).to_not include project + end + end + end +end diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb index f7cb7ca8a40..30d296fdad0 100644 --- a/spec/controllers/projects/issues_controller_spec.rb +++ b/spec/controllers/projects/issues_controller_spec.rb @@ -40,6 +40,45 @@ describe Projects::IssuesController do end end + describe 'PUT #update' do + context 'when moving issue to another private project' do + let(:another_project) { create(:project, :private) } + + before do + sign_in(user) + project.team << [user, :developer] + end + + context 'when user has access to move issue' do + before { another_project.team << [user, :reporter] } + + it 'moves issue to another project' do + move_issue + + expect(response).to have_http_status :found + expect(another_project.issues).to_not be_empty + end + end + + context 'when user does not have access to move issue' do + it 'responds with 404' do + move_issue + + expect(response).to have_http_status :not_found + end + end + + def move_issue + put :update, + namespace_id: project.namespace.to_param, + project_id: project.to_param, + id: issue.iid, + issue: { title: 'New title' }, + move_to_project_id: another_project.id + end + end + end + describe 'Confidential Issues' do let(:project) { create(:project_empty_repo, :public) } let(:assignee) { create(:assignee) } diff --git a/spec/controllers/projects/merge_requests_controller_spec.rb b/spec/controllers/projects/merge_requests_controller_spec.rb index c54e83339a1..c0a1f45195f 100644 --- a/spec/controllers/projects/merge_requests_controller_spec.rb +++ b/spec/controllers/projects/merge_requests_controller_spec.rb @@ -300,14 +300,6 @@ describe Projects::MergeRequestsController do expect(response.cookies['diff_view']).to eq('parallel') end - - it 'assigns :view param based on cookie' do - request.cookies['diff_view'] = 'parallel' - - go - - expect(controller.params[:view]).to eq 'parallel' - end end describe 'GET commits' do diff --git a/spec/controllers/projects/project_members_controller_spec.rb b/spec/controllers/projects/project_members_controller_spec.rb index d47e4ab9a4f..ed64e7cf9af 100644 --- a/spec/controllers/projects/project_members_controller_spec.rb +++ b/spec/controllers/projects/project_members_controller_spec.rb @@ -46,4 +46,20 @@ describe Projects::ProjectMembersController do end end end + + describe '#index' do + let(:project) { create(:project, :private) } + + context 'when user is member' do + let(:member) { create(:user) } + + before do + project.team << [member, :guest] + sign_in(member) + get :index, namespace_id: project.namespace.to_param, project_id: project.to_param + end + + it { expect(response.status).to eq(200) } + end + end end diff --git a/spec/controllers/users_controller_spec.rb b/spec/controllers/users_controller_spec.rb index 7337ff58be1..8045c8b940d 100644 --- a/spec/controllers/users_controller_spec.rb +++ b/spec/controllers/users_controller_spec.rb @@ -33,7 +33,30 @@ describe UsersController do it 'renders the show template' do get :show, username: user.username - expect(response).to be_success + expect(response.status).to eq(200) + expect(response).to render_template('show') + end + end + end + + context 'when public visibility level is restricted' do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + end + + context 'when logged out' do + it 'renders 404' do + get :show, username: user.username + expect(response.status).to eq(404) + end + end + + context 'when logged in' do + before { sign_in(user) } + + it 'renders show' do + get :show, username: user.username + expect(response.status).to eq(200) expect(response).to render_template('show') end end diff --git a/spec/factories/abuse_reports.rb b/spec/factories/abuse_reports.rb index d0e8c778518..8f6422a7825 100644 --- a/spec/factories/abuse_reports.rb +++ b/spec/factories/abuse_reports.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: abuse_reports -# -# id :integer not null, primary key -# reporter_id :integer -# user_id :integer -# message :text -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :abuse_report do reporter factory: :user diff --git a/spec/factories/broadcast_messages.rb b/spec/factories/broadcast_messages.rb index c80e7366551..efe9803b1a7 100644 --- a/spec/factories/broadcast_messages.rb +++ b/spec/factories/broadcast_messages.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: broadcast_messages -# -# id :integer not null, primary key -# message :text not null -# starts_at :datetime -# ends_at :datetime -# created_at :datetime -# updated_at :datetime -# color :string(255) -# font :string(255) -# - FactoryGirl.define do factory :broadcast_message do message "MyText" diff --git a/spec/factories/forked_project_links.rb b/spec/factories/forked_project_links.rb index 19a54946fe0..b16c1272e68 100644 --- a/spec/factories/forked_project_links.rb +++ b/spec/factories/forked_project_links.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: forked_project_links -# -# id :integer not null, primary key -# forked_to_project_id :integer not null -# forked_from_project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :forked_project_link do association :forked_to_project, factory: :project diff --git a/spec/factories/label_links.rb b/spec/factories/label_links.rb index 2939d4307c5..3580174e873 100644 --- a/spec/factories/label_links.rb +++ b/spec/factories/label_links.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: label_links -# -# id :integer not null, primary key -# label_id :integer -# target_id :integer -# target_type :string(255) -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :label_link do label diff --git a/spec/factories/labels.rb b/spec/factories/labels.rb index ea2be8928d5..eb489099854 100644 --- a/spec/factories/labels.rb +++ b/spec/factories/labels.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: labels -# -# id :integer not null, primary key -# title :string(255) -# color :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# template :boolean default(FALSE) -# - FactoryGirl.define do factory :label do sequence(:title) { |n| "label#{n}" } diff --git a/spec/factories/lfs_objects.rb b/spec/factories/lfs_objects.rb index 327858ce435..a81645acd2b 100644 --- a/spec/factories/lfs_objects.rb +++ b/spec/factories/lfs_objects.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: lfs_objects -# -# id :integer not null, primary key -# oid :string(255) not null -# size :integer not null -# created_at :datetime -# updated_at :datetime -# file :string(255) -# - include ActionDispatch::TestProcess FactoryGirl.define do diff --git a/spec/factories/lfs_objects_projects.rb b/spec/factories/lfs_objects_projects.rb index 50b45843c99..1ed0355c8e4 100644 --- a/spec/factories/lfs_objects_projects.rb +++ b/spec/factories/lfs_objects_projects.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: lfs_objects_projects -# -# id :integer not null, primary key -# lfs_object_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :lfs_objects_project do lfs_object diff --git a/spec/factories/merge_requests.rb b/spec/factories/merge_requests.rb index e281e2f227b..c6a08d78b78 100644 --- a/spec/factories/merge_requests.rb +++ b/spec/factories/merge_requests.rb @@ -1,32 +1,3 @@ -# == Schema Information -# -# Table name: merge_requests -# -# id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string(255) -# merge_status :string(255) -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string(255) -# merge_params :text -# merge_when_build_succeeds :boolean default(FALSE), not null -# merge_user_id :integer -# merge_commit_sha :string -# - FactoryGirl.define do factory :merge_request do title diff --git a/spec/factories/notes.rb b/spec/factories/notes.rb index 2bfc5effd78..e65204cbe7b 100644 --- a/spec/factories/notes.rb +++ b/spec/factories/notes.rb @@ -1,24 +1,3 @@ -# == Schema Information -# -# Table name: notes -# -# id :integer not null, primary key -# note :text -# noteable_type :string(255) -# author_id :integer -# created_at :datetime -# updated_at :datetime -# project_id :integer -# attachment :string(255) -# line_code :string(255) -# commit_id :string(255) -# noteable_id :integer -# system :boolean default(FALSE), not null -# st_diff :text -# updated_by_id :integer -# is_award :boolean default(FALSE), not null -# - require_relative '../support/repo_helpers' include ActionDispatch::TestProcess diff --git a/spec/factories/oauth_access_tokens.rb b/spec/factories/oauth_access_tokens.rb index 7700b15d538..ccf02d0719b 100644 --- a/spec/factories/oauth_access_tokens.rb +++ b/spec/factories/oauth_access_tokens.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: oauth_access_tokens -# -# id :integer not null, primary key -# resource_owner_id :integer -# application_id :integer -# token :string not null -# refresh_token :string -# expires_in :integer -# revoked_at :datetime -# created_at :datetime not null -# scopes :string -# - FactoryGirl.define do factory :oauth_access_token do resource_owner diff --git a/spec/factories/project_hooks.rb b/spec/factories/project_hooks.rb index 94dd935a039..3195fb3ddcc 100644 --- a/spec/factories/project_hooks.rb +++ b/spec/factories/project_hooks.rb @@ -1,5 +1,9 @@ FactoryGirl.define do factory :project_hook do url { FFaker::Internet.uri('http') } + + trait :token do + token { SecureRandom.hex(10) } + end end end diff --git a/spec/factories/project_wikis.rb b/spec/factories/project_wikis.rb new file mode 100644 index 00000000000..a3403fd76ae --- /dev/null +++ b/spec/factories/project_wikis.rb @@ -0,0 +1,7 @@ +FactoryGirl.define do + factory :project_wiki do + project factory: :empty_project + user factory: :user + initialize_with { new(project, user) } + end +end diff --git a/spec/factories/projects.rb b/spec/factories/projects.rb index c14b99606ba..da8d97c9f82 100644 --- a/spec/factories/projects.rb +++ b/spec/factories/projects.rb @@ -1,43 +1,3 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# description :text -# created_at :datetime -# updated_at :datetime -# creator_id :integer -# issues_enabled :boolean default(TRUE), not null -# wall_enabled :boolean default(TRUE), not null -# merge_requests_enabled :boolean default(TRUE), not null -# wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# issues_tracker :string(255) default("gitlab"), not null -# issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null -# last_activity_at :datetime -# import_url :string(255) -# visibility_level :integer default(0), not null -# archived :boolean default(FALSE), not null -# avatar :string(255) -# import_status :string(255) -# repository_size :float default(0.0) -# star_count :integer default(0), not null -# import_type :string(255) -# import_source :string(255) -# commit_count :integer default(0) -# import_error :text -# ci_id :integer -# builds_enabled :boolean default(TRUE), not null -# shared_runners_enabled :boolean default(TRUE), not null -# runners_token :string -# build_coverage_regex :string -# build_allow_git_fetch :boolean default(TRUE), not null -# build_timeout :integer default(3600), not null -# - FactoryGirl.define do # Project without repository # @@ -61,6 +21,12 @@ FactoryGirl.define do trait :private do visibility_level Gitlab::VisibilityLevel::PRIVATE end + + trait :empty_repo do + after(:create) do |project| + project.create_repository + end + end end # Project with empty repository @@ -68,9 +34,7 @@ FactoryGirl.define do # This is a case when you just created a project # but not pushed any code there yet factory :project_empty_repo, parent: :empty_project do - after :create do |project| - project.create_repository - end + empty_repo end # Project with test repository diff --git a/spec/factories/releases.rb b/spec/factories/releases.rb index 7f331c37256..74497dc82c0 100644 --- a/spec/factories/releases.rb +++ b/spec/factories/releases.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: releases -# -# id :integer not null, primary key -# tag :string(255) -# description :text -# project_id :integer -# created_at :datetime -# updated_at :datetime -# - FactoryGirl.define do factory :release do tag "v1.1.0" diff --git a/spec/factories/todos.rb b/spec/factories/todos.rb index 7ae06c27840..e3681ae93a5 100644 --- a/spec/factories/todos.rb +++ b/spec/factories/todos.rb @@ -1,21 +1,3 @@ -# == Schema Information -# -# Table name: todos -# -# id :integer not null, primary key -# user_id :integer not null -# project_id :integer not null -# target_id :integer -# target_type :string not null -# author_id :integer -# action :integer not null -# state :string not null -# created_at :datetime -# updated_at :datetime -# note_id :integer -# commit_id :string -# - FactoryGirl.define do factory :todo do project diff --git a/spec/factories/wiki_pages.rb b/spec/factories/wiki_pages.rb new file mode 100644 index 00000000000..938ccf2306b --- /dev/null +++ b/spec/factories/wiki_pages.rb @@ -0,0 +1,9 @@ +require 'ostruct' + +FactoryGirl.define do + factory :wiki_page do + page = OpenStruct.new(url_path: 'some-name') + association :wiki, factory: :project_wiki, strategy: :build + initialize_with { new(wiki, page, true) } + end +end diff --git a/spec/features/builds_spec.rb b/spec/features/builds_spec.rb index 6da3a857b3f..090a941958f 100644 --- a/spec/features/builds_spec.rb +++ b/spec/features/builds_spec.rb @@ -86,6 +86,20 @@ describe "Builds" do end end end + + context 'Build raw trace' do + before do + @build.run! + @build.trace = 'BUILD TRACE' + visit namespace_project_build_path(@project.namespace, @project, @build) + end + + it do + page.within('.build-controls') do + expect(page).to have_link 'Raw' + end + end + end end describe "POST /:project/builds/:id/cancel" do @@ -120,4 +134,20 @@ describe "Builds" do it { expect(page.response_headers['Content-Type']).to eq(artifacts_file.content_type) } end + + describe "GET /:project/builds/:id/raw" do + before do + Capybara.current_session.driver.header('X-Sendfile-Type', 'X-Sendfile') + @build.run! + @build.trace = 'BUILD TRACE' + visit namespace_project_build_path(@project.namespace, @project, @build) + end + + it 'sends the right headers' do + page.within('.build-controls') { click_link 'Raw' } + + expect(page.response_headers['Content-Type']).to eq('text/plain; charset=utf-8') + expect(page.response_headers['X-Sendfile']).to eq(@build.path_to_trace) + end + end end diff --git a/spec/features/dashboard/label_filter_spec.rb b/spec/features/dashboard/label_filter_spec.rb new file mode 100644 index 00000000000..24e83d44010 --- /dev/null +++ b/spec/features/dashboard/label_filter_spec.rb @@ -0,0 +1,29 @@ +require 'spec_helper' + +describe 'Dashboard > label filter', feature: true, js: true do + let(:user) { create(:user) } + let(:project) { create(:project, name: 'test', namespace: user.namespace) } + let(:project2) { create(:project, name: 'test2', path: 'test2', namespace: user.namespace) } + let(:label) { create(:label, title: 'bug', color: '#ff0000') } + let(:label2) { create(:label, title: 'bug') } + + before do + project.labels << label + project2.labels << label2 + + login_as(user) + visit issues_dashboard_path + end + + context 'duplicate labels' do + it 'should remove duplicate labels' do + page.within('.labels-filter') do + click_button 'Label' + end + + page.within('.dropdown-menu-labels') do + expect(page).to have_selector('.dropdown-content a', text: 'bug', count: 1) + end + end + end +end diff --git a/spec/features/dashboard/user_filters_projects_spec.rb b/spec/features/dashboard/user_filters_projects_spec.rb new file mode 100644 index 00000000000..cf86e2c85e9 --- /dev/null +++ b/spec/features/dashboard/user_filters_projects_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +describe "Dashboard > User filters projects", feature: true do + + describe 'filtering personal projects' do + before do + user = create(:user) + project = create(:project, name: "Victorialand", namespace: user.namespace) + project.team << [user, :master] + + user2 = create(:user) + project2 = create(:project, name: "Treasure", namespace: user2.namespace) + project2.team << [user, :developer] + + login_as(user) + visit dashboard_projects_path + end + + it 'filters by projects "Owned by me"' do + click_link "Owned by me" + + expect(page).to have_css('.is-active', text: 'Owned by me') + expect(page).to have_content('Victorialand') + expect(page).not_to have_content('Treasure') + end + end +end diff --git a/spec/features/issues/filter_by_labels_spec.rb b/spec/features/issues/filter_by_labels_spec.rb new file mode 100644 index 00000000000..7f654684143 --- /dev/null +++ b/spec/features/issues/filter_by_labels_spec.rb @@ -0,0 +1,167 @@ +require 'rails_helper' + +feature 'Issue filtering by Labels', feature: true do + include WaitForAjax + + let(:project) { create(:project, :public) } + let!(:user) { create(:user)} + let!(:label) { create(:label, project: project) } + + before do + bug = create(:label, project: project, title: 'bug') + feature = create(:label, project: project, title: 'feature') + enhancement = create(:label, project: project, title: 'enhancement') + + issue1 = create(:issue, title: "Bugfix1", project: project) + issue1.labels << bug + + issue2 = create(:issue, title: "Bugfix2", project: project) + issue2.labels << bug + issue2.labels << enhancement + + issue3 = create(:issue, title: "Feature1", project: project) + issue3.labels << feature + + project.team << [user, :master] + login_as(user) + + visit namespace_project_issues_path(project.namespace, project) + end + + context 'filter by label bug', js: true do + before do + page.find('.js-label-select').click + wait_for_ajax + execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()") + page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click + wait_for_ajax + end + + it 'should show issue "Bugfix1" and "Bugfix2" in issues list' do + expect(page).to have_content "Bugfix1" + expect(page).to have_content "Bugfix2" + end + + it 'should not show "Feature1" in issues list' do + expect(page).not_to have_content "Feature1" + end + + it 'should show label "bug" in filtered-labels' do + expect(find('.filtered-labels')).to have_content "bug" + end + + it 'should not show label "feature" and "enhancement" in filtered-labels' do + expect(find('.filtered-labels')).not_to have_content "feature" + expect(find('.filtered-labels')).not_to have_content "enhancement" + end + end + + context 'filter by label feature', js: true do + before do + page.find('.js-label-select').click + wait_for_ajax + execute_script("$('.dropdown-menu-labels li:contains(\"feature\") a').click()") + page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click + wait_for_ajax + end + + it 'should show issue "Feature1" in issues list' do + expect(page).to have_content "Feature1" + end + + it 'should not show "Bugfix1" and "Bugfix2" in issues list' do + expect(page).not_to have_content "Bugfix2" + expect(page).not_to have_content "Bugfix1" + end + + it 'should show label "feature" in filtered-labels' do + expect(find('.filtered-labels')).to have_content "feature" + end + + it 'should not show label "bug" and "enhancement" in filtered-labels' do + expect(find('.filtered-labels')).not_to have_content "bug" + expect(find('.filtered-labels')).not_to have_content "enhancement" + end + end + + context 'filter by label enhancement', js: true do + before do + page.find('.js-label-select').click + wait_for_ajax + execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()") + page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click + wait_for_ajax + end + + it 'should show issue "Bugfix2" in issues list' do + expect(page).to have_content "Bugfix2" + end + + it 'should not show "Feature1" and "Bugfix1" in issues list' do + expect(page).not_to have_content "Feature1" + expect(page).not_to have_content "Bugfix1" + end + + it 'should show label "enhancement" in filtered-labels' do + expect(find('.filtered-labels')).to have_content "enhancement" + end + + it 'should not show label "feature" and "bug" in filtered-labels' do + expect(find('.filtered-labels')).not_to have_content "bug" + expect(find('.filtered-labels')).not_to have_content "feature" + end + end + + context 'filter by label enhancement or feature', js: true do + before do + page.find('.js-label-select').click + wait_for_ajax + execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()") + execute_script("$('.dropdown-menu-labels li:contains(\"feature\") a').click()") + page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click + wait_for_ajax + end + + it 'should not show "Bugfix1" or "Feature1" in issues list' do + expect(page).not_to have_content "Bugfix1" + expect(page).not_to have_content "Feature1" + end + + it 'should show label "enhancement" and "feature" in filtered-labels' do + expect(find('.filtered-labels')).to have_content "enhancement" + expect(find('.filtered-labels')).to have_content "feature" + end + + it 'should not show label "bug" in filtered-labels' do + expect(find('.filtered-labels')).not_to have_content "bug" + end + end + + context 'filter by label enhancement and bug in issues list', js: true do + before do + page.find('.js-label-select').click + wait_for_ajax + execute_script("$('.dropdown-menu-labels li:contains(\"enhancement\") a').click()") + execute_script("$('.dropdown-menu-labels li:contains(\"bug\") a').click()") + page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click + wait_for_ajax + end + + it 'should show issue "Bugfix2" in issues list' do + expect(page).to have_content "Bugfix2" + end + + it 'should not show "Feature1"' do + expect(page).not_to have_content "Feature1" + end + + it 'should show label "bug" and "enhancement" in filtered-labels' do + expect(find('.filtered-labels')).to have_content "bug" + expect(find('.filtered-labels')).to have_content "enhancement" + end + + it 'should not show label "feature" in filtered-labels' do + expect(find('.filtered-labels')).not_to have_content "feature" + end + end +end diff --git a/spec/features/issues/filter_issues_spec.rb b/spec/features/issues/filter_issues_spec.rb index 69b22232f10..192e3619375 100644 --- a/spec/features/issues/filter_issues_spec.rb +++ b/spec/features/issues/filter_issues_spec.rb @@ -84,14 +84,20 @@ describe 'Filter issues', feature: true do it 'should filter by any label' do find('.dropdown-menu-labels a', text: 'Any Label').click + page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click + sleep 2 + page.within '.labels-filter' do expect(page).to have_content 'Any Label' end - expect(find('.js-label-select .dropdown-toggle-text')).to have_content('Label') + expect(find('.js-label-select .dropdown-toggle-text')).to have_content('Any Label') end it 'should filter by no label' do find('.dropdown-menu-labels a', text: 'No Label').click + page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click + sleep 2 + page.within '.labels-filter' do expect(page).to have_content 'No Label' end @@ -121,6 +127,7 @@ describe 'Filter issues', feature: true do find('.js-label-select').click find('.dropdown-menu-labels .dropdown-content a', text: label.title).click + page.first('.labels-filter .dropdown-title .dropdown-menu-close-icon').click sleep 2 end diff --git a/spec/features/issues/issue_sidebar_spec.rb b/spec/features/issues/issue_sidebar_spec.rb new file mode 100644 index 00000000000..5739bc64dfb --- /dev/null +++ b/spec/features/issues/issue_sidebar_spec.rb @@ -0,0 +1,79 @@ +require 'rails_helper' + +feature 'Issue Sidebar', feature: true do + let(:project) { create(:project) } + let(:issue) { create(:issue, project: project) } + let!(:user) { create(:user)} + + before do + create(:label, project: project, title: 'bug') + login_as(user) + end + + context 'as a allowed user' do + before do + project.team << [user, :developer] + visit_issue(project, issue) + end + + describe 'when clicking on edit labels', js: true do + it 'dropdown has an option to create a new label' do + find('.block.labels .edit-link').click + + page.within('.block.labels') do + expect(page).to have_content 'Create new' + end + end + end + + context 'creating a new label', js: true do + it 'option to crate a new label is present' do + page.within('.block.labels') do + find('.edit-link').click + + expect(page).to have_content 'Create new' + end + end + + it 'dropdown switches to "create label" section' do + page.within('.block.labels') do + find('.edit-link').click + click_link 'Create new' + + expect(page).to have_content 'Create new label' + end + end + + it 'new label is added' do + page.within('.block.labels') do + find('.edit-link').click + sleep 1 + click_link 'Create new' + + fill_in 'new_label_name', with: 'wontfix' + page.find(".suggest-colors a", match: :first).click + click_button 'Create' + + page.within('.dropdown-page-one') do + expect(page).to have_content 'wontfix' + end + end + end + end + end + + context 'as a guest' do + before do + project.team << [user, :guest] + visit_issue(project, issue) + end + + it 'does not have a option to edit labels' do + expect(page).not_to have_selector('.block.labels .edit-link') + end + end + + def visit_issue(project, issue) + visit namespace_project_issue_path(project.namespace, project, issue) + end +end diff --git a/spec/features/issues/move_spec.rb b/spec/features/issues/move_spec.rb index 6fda0c31866..84c8e20ebaa 100644 --- a/spec/features/issues/move_spec.rb +++ b/spec/features/issues/move_spec.rb @@ -42,11 +42,9 @@ feature 'issue move to another project' do expect(current_url).to include project_path(new_project) - page.within('.issue') do - expect(page).to have_content("Text with #{cross_reference}!1") - expect(page).to have_content("Moved from #{cross_reference}#1") - expect(page).to have_content(issue.title) - end + expect(page).to have_content("Text with #{cross_reference}!1") + expect(page).to have_content("Moved from #{cross_reference}#1") + expect(page).to have_content(issue.title) end context 'projects user does not have permission to move issue to exist' do @@ -74,7 +72,7 @@ feature 'issue move to another project' do def edit_issue(issue) visit issue_path(issue) - page.within('.issuable-header') { click_link 'Edit' } + page.within('.issuable-actions') { first(:link, 'Edit').click } end def issue_path(issue) diff --git a/spec/features/issues/new_branch_button_spec.rb b/spec/features/issues/new_branch_button_spec.rb index 9219b767547..16e188d2a8a 100644 --- a/spec/features/issues/new_branch_button_spec.rb +++ b/spec/features/issues/new_branch_button_spec.rb @@ -11,10 +11,10 @@ feature 'Start new branch from an issue', feature: true do login_as(user) end - it 'shown the new branch button', js: false do + it 'shows the new branch button', js: true do visit namespace_project_issue_path(project.namespace, project, issue) - expect(page).to have_link "New Branch" + expect(page).to have_css('#new-branch .available') end context "when there is a referenced merge request" do @@ -34,16 +34,17 @@ feature 'Start new branch from an issue', feature: true do end it "hides the new branch button", js: true do - expect(page).not_to have_link "New Branch" + expect(page).not_to have_css('#new-branch .available') expect(page).to have_content /1 Related Merge Request/ end end end context "for visiters" do - it 'no button is shown', js: false do + it 'no button is shown', js: true do visit namespace_project_issue_path(project.namespace, project, issue) - expect(page).not_to have_link "New Branch" + + expect(page).not_to have_css('#new-branch') end end end diff --git a/spec/features/issues/update_issues_spec.rb b/spec/features/issues/update_issues_spec.rb index 3eb903a93fe..b03dd0f666d 100644 --- a/spec/features/issues/update_issues_spec.rb +++ b/spec/features/issues/update_issues_spec.rb @@ -48,7 +48,7 @@ feature 'Multiple issue updating from issues#index', feature: true do click_update_issues_button page.within('.issue .controls') do - expect(find('.author_link')["data-original-title"]).to have_content(user.name) + expect(find('.author_link')["title"]).to have_content(user.name) end end diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb index 7da87c2d174..dfb554c05ca 100644 --- a/spec/features/issues_spec.rb +++ b/spec/features/issues_spec.rb @@ -112,7 +112,7 @@ describe 'Issues', feature: true do end describe 'filter issue' do - titles = ['foo','bar','baz'] + titles = %w[foo bar baz] titles.each_with_index do |title, index| let!(title.to_sym) do create(:issue, title: title, @@ -153,8 +153,107 @@ describe 'Issues', feature: true do expect(first_issue).to include('baz') end + describe 'sorting by due date' do + before do + foo.update(due_date: 1.day.from_now) + bar.update(due_date: 6.days.from_now) + end + + it 'sorts by recently due date' do + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_soon) + + expect(first_issue).to include('foo') + end + + it 'sorts by least recently due date' do + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_later) + + expect(first_issue).to include('bar') + end + + it 'sorts by least recently due date by excluding nil due dates' do + bar.update(due_date: nil) + + visit namespace_project_issues_path(project.namespace, project, sort: sort_value_due_date_later) + + expect(first_issue).to include('foo') + end + + context 'with a filter on labels' do + let(:label) { create(:label, project: project) } + before { create(:label_link, label: label, target: foo) } + + it 'sorts by least recently due date by excluding nil due dates' do + bar.update(due_date: nil) + + visit namespace_project_issues_path(project.namespace, project, label_names: [label.name], sort: sort_value_due_date_later) + + expect(first_issue).to include('foo') + end + end + end + + describe 'filtering by due date' do + before do + foo.update(due_date: 1.day.from_now) + bar.update(due_date: 6.days.from_now) + end + + it 'filters by none' do + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::NoDueDate.name) + + expect(page).not_to have_content('foo') + expect(page).not_to have_content('bar') + expect(page).to have_content('baz') + end + + it 'filters by any' do + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::AnyDueDate.name) + + expect(page).to have_content('foo') + expect(page).to have_content('bar') + expect(page).to have_content('baz') + end + + it 'filters by due this week' do + foo.update(due_date: Date.today.beginning_of_week + 2.days) + bar.update(due_date: Date.today.end_of_week) + baz.update(due_date: Date.today - 8.days) + + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisWeek.name) + + expect(page).to have_content('foo') + expect(page).to have_content('bar') + expect(page).not_to have_content('baz') + end + + it 'filters by due this month' do + foo.update(due_date: Date.today.beginning_of_month + 2.days) + bar.update(due_date: Date.today.end_of_month) + baz.update(due_date: Date.today - 50.days) + + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::DueThisMonth.name) + + expect(page).to have_content('foo') + expect(page).to have_content('bar') + expect(page).not_to have_content('baz') + end + + it 'filters by overdue' do + foo.update(due_date: Date.today + 2.days) + bar.update(due_date: Date.today + 20.days) + baz.update(due_date: Date.yesterday) + + visit namespace_project_issues_path(project.namespace, project, due_date: Issue::Overdue.name) + + expect(page).not_to have_content('foo') + expect(page).not_to have_content('bar') + expect(page).to have_content('baz') + end + end + describe 'sorting by milestone' do - before :each do + before do foo.milestone = newer_due_milestone foo.save bar.milestone = later_due_milestone @@ -165,19 +264,21 @@ describe 'Issues', feature: true do visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_soon) expect(first_issue).to include('foo') + expect(last_issue).to include('baz') end it 'sorts by least recently due milestone' do visit namespace_project_issues_path(project.namespace, project, sort: sort_value_milestone_later) expect(first_issue).to include('bar') + expect(last_issue).to include('baz') end end describe 'combine filter and sort' do let(:user2) { create(:user) } - before :each do + before do foo.assignee = user2 foo.save bar.assignee = user2 @@ -218,13 +319,34 @@ describe 'Issues', feature: true do expect(issue.reload.assignee).to be_nil end + + it 'allows user to select an assignee', js: true do + issue2 = create(:issue, project: project, author: @user) + visit namespace_project_issue_path(project.namespace, project, issue2) + + page.within('.assignee') do + expect(page).to have_content "No assignee" + end + + page.within '.assignee' do + click_link 'Edit' + end + + page.within '.dropdown-menu-user' do + click_link @user.name + end + + page.within('.assignee') do + expect(page).to have_content @user.name + end + end end context 'by unauthorized user' do let(:guest) { create(:user) } - before :each do + before do project.team << [[guest], :guest] end @@ -267,7 +389,7 @@ describe 'Issues', feature: true do context 'by unauthorized user' do let(:guest) { create(:user) } - before :each do + before do project.team << [guest, :guest] issue.milestone = milestone issue.save @@ -285,13 +407,30 @@ describe 'Issues', feature: true do describe 'removing assignee' do let(:user2) { create(:user) } - before :each do + before do issue.assignee = user2 issue.save end end end + describe 'new issue' do + context 'dropzone upload file', js: true do + before do + visit new_namespace_project_issue_path(project.namespace, project) + end + + it 'should upload file when dragging into textarea' do + drop_in_dropzone test_image_file + + # Wait for the file to upload + sleep 1 + + expect(page.find_field("issue_description").value).to have_content 'banana_sample' + end + end + end + def first_issue page.all('ul.issues-list > li').first.text end @@ -299,4 +438,25 @@ describe 'Issues', feature: true do def last_issue page.all('ul.issues-list > li').last.text end + + def drop_in_dropzone(file_path) + # Generate a fake input selector + page.execute_script <<-JS + var fakeFileInput = window.$('<input/>').attr( + {id: 'fakeFileInput', type: 'file'} + ).appendTo('body'); + JS + # Attach the file to the fake input selector with Capybara + attach_file("fakeFileInput", file_path) + # Add the file to a fileList array and trigger the fake drop event + page.execute_script <<-JS + var fileList = [$('#fakeFileInput')[0].files[0]]; + var e = jQuery.Event('drop', { dataTransfer : { files : fileList } }); + $('.div-dropzone')[0].dropzone.listeners[0].events.drop(e); + JS + end + + def test_image_file + File.join(Rails.root, 'spec', 'fixtures', 'banana_sample.gif') + end end diff --git a/spec/features/login_spec.rb b/spec/features/login_spec.rb index 4433ef2d6f1..8c38dd5b122 100644 --- a/spec/features/login_spec.rb +++ b/spec/features/login_spec.rb @@ -37,7 +37,7 @@ feature 'Login', feature: true do end def enter_code(code) - fill_in 'Two-factor authentication code', with: code + fill_in 'Two-factor Authentication code', with: code click_button 'Verify code' end diff --git a/spec/features/markdown_spec.rb b/spec/features/markdown_spec.rb index 3d0d0e59fd7..0148c87084a 100644 --- a/spec/features/markdown_spec.rb +++ b/spec/features/markdown_spec.rb @@ -165,7 +165,12 @@ describe 'GitLab Markdown', feature: true do describe 'ExternalLinkFilter' do it 'adds nofollow to external link' do link = doc.at_css('a:contains("Google")') - expect(link.attr('rel')).to match 'nofollow' + expect(link.attr('rel')).to include('nofollow') + end + + it 'adds noreferrer to external link' do + link = doc.at_css('a:contains("Google")') + expect(link.attr('rel')).to include('noreferrer') end it 'ignores internal link' do diff --git a/spec/features/merge_requests/cherry_pick_spec.rb b/spec/features/merge_requests/cherry_pick_spec.rb new file mode 100644 index 00000000000..82bc5226d07 --- /dev/null +++ b/spec/features/merge_requests/cherry_pick_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +describe 'Cherry-pick Merge Requests' do + let(:user) { create(:user) } + let(:project) { create(:project) } + let(:merge_request) { create(:merge_request_with_diffs, source_project: project, author: user) } + + before do + login_as user + project.team << [user, :master] + end + + context "Viewing a merged merge request" do + before do + service = MergeRequests::MergeService.new(project, user) + + perform_enqueued_jobs do + service.execute(merge_request) + end + end + + # Fast-forward merge, or merged before GitLab 8.5. + context "Without a merge commit" do + before do + merge_request.merge_commit_sha = nil + merge_request.save + end + + it "doesn't show a Cherry-pick button" do + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + + expect(page).not_to have_link "Cherry-pick" + end + end + + context "With a merge commit" do + it "shows a Cherry-pick button" do + visit namespace_project_merge_request_path(project.namespace, project, merge_request) + + expect(page).to have_link "Cherry-pick" + end + end + end +end diff --git a/spec/features/merge_requests/create_new_mr_spec.rb b/spec/features/merge_requests/create_new_mr_spec.rb index 00b60bd0e75..e296078bad8 100644 --- a/spec/features/merge_requests/create_new_mr_spec.rb +++ b/spec/features/merge_requests/create_new_mr_spec.rb @@ -30,4 +30,14 @@ feature 'Create New Merge Request', feature: true, js: true do expect(page).to have_content 'git checkout -b orphaned-branch origin/orphaned-branch' end + + context 'when target project cannot be viewed by the current user' do + it 'does not leak the private project name & namespace' do + private_project = create(:project, :private) + + visit new_namespace_project_merge_request_path(project.namespace, project, merge_request: { target_project_id: private_project.id }) + + expect(page).not_to have_content private_project.to_reference + end + end end diff --git a/spec/features/merge_requests/filter_by_milestone_spec.rb b/spec/features/merge_requests/filter_by_milestone_spec.rb index c57ab5f3b03..e3ecd60a5f3 100644 --- a/spec/features/merge_requests/filter_by_milestone_spec.rb +++ b/spec/features/merge_requests/filter_by_milestone_spec.rb @@ -2,8 +2,14 @@ require 'rails_helper' feature 'Merge Request filtering by Milestone', feature: true do let(:project) { create(:project, :public) } + let!(:user) { create(:user)} let(:milestone) { create(:milestone, project: project) } + before do + project.team << [user, :master] + login_as(user) + end + scenario 'filters by no Milestone', js: true do create(:merge_request, :with_diffs, source_project: project) create(:merge_request, :simple, source_project: project, milestone: milestone) diff --git a/spec/features/merge_requests/toggle_whitespace_changes.rb b/spec/features/merge_requests/toggle_whitespace_changes.rb new file mode 100644 index 00000000000..0f98737b700 --- /dev/null +++ b/spec/features/merge_requests/toggle_whitespace_changes.rb @@ -0,0 +1,22 @@ +require 'spec_helper' + +feature 'Toggle Whitespace Changes', js: true, feature: true do + before do + login_as :admin + merge_request = create(:merge_request) + project = merge_request.source_project + visit diffs_namespace_project_merge_request_path(project.namespace, project, merge_request) + end + + it 'has a button to toggle whitespace changes' do + expect(page).to have_content 'Hide whitespace changes' + end + + describe 'clicking "Hide whitespace changes" button' do + it 'toggles the "Hide whitespace changes" button' do + click_link 'Hide whitespace changes' + + expect(page).to have_content 'Show whitespace changes' + end + end +end diff --git a/spec/features/milestone_spec.rb b/spec/features/milestone_spec.rb new file mode 100644 index 00000000000..c2c7acff3e8 --- /dev/null +++ b/spec/features/milestone_spec.rb @@ -0,0 +1,35 @@ +require 'rails_helper' + +feature 'Milestone', feature: true do + include WaitForAjax + + let(:project) { create(:project, :public) } + let(:user) { create(:user) } + let(:milestone) { create(:milestone, project: project, title: 8.7) } + + before do + project.team << [user, :master] + login_as(user) + end + + feature 'Create a milestone' do + scenario 'should show an informative message for a new issue' do + visit new_namespace_project_milestone_path(project.namespace, project) + page.within '.milestone-form' do + fill_in "milestone_title", with: '8.7' + end + find('input[name="commit"]').click + + expect(find('.alert-success')).to have_content('Assign some issues to this milestone.') + end + end + + feature 'Open a milestone with closed issues' do + scenario 'should show an informative message' do + create(:issue, title: "Bugfix1", project: project, milestone: milestone, state: "closed") + visit namespace_project_milestone_path(project.namespace, project, milestone) + + expect(find('.alert-success')).to have_content('All issues for this milestone are closed. You may close this milestone now.') + end + end +end diff --git a/spec/features/project/shortcuts_spec.rb b/spec/features/project/shortcuts_spec.rb new file mode 100644 index 00000000000..2595c4181e5 --- /dev/null +++ b/spec/features/project/shortcuts_spec.rb @@ -0,0 +1,21 @@ +require 'spec_helper' + +feature 'Project shortcuts', feature: true do + let(:project) { create(:project) } + let(:user) { create(:user) } + + describe 'On a project', js: true do + before do + project.team << [user, :master] + login_as user + visit namespace_project_path(project.namespace, project) + end + + describe 'pressing "i"' do + it 'redirects to new issue page' do + find('body').native.send_key('i') + expect(page).to have_content('New Issue') + end + end + end +end diff --git a/spec/features/projects/commit/builds_spec.rb b/spec/features/projects/commit/builds_spec.rb new file mode 100644 index 00000000000..40ba0bdc115 --- /dev/null +++ b/spec/features/projects/commit/builds_spec.rb @@ -0,0 +1,27 @@ +require 'spec_helper' + +feature 'project commit builds' do + given(:project) { create(:project) } + + background do + user = create(:user) + project.team << [user, :master] + login_as(user) + end + + context 'when no builds triggered yet' do + background do + create(:ci_commit, project: project, + sha: project.commit.sha, + ref: 'master') + end + + scenario 'user views commit builds page' do + visit builds_namespace_project_commit_path(project.namespace, + project, project.commit.sha) + + + expect(page).to have_content('Builds') + end + end +end diff --git a/spec/features/projects/commits/cherry_pick_spec.rb b/spec/features/projects/commits/cherry_pick_spec.rb new file mode 100644 index 00000000000..0559b02f321 --- /dev/null +++ b/spec/features/projects/commits/cherry_pick_spec.rb @@ -0,0 +1,67 @@ +require 'spec_helper' + +describe 'Cherry-pick Commits' do + let(:project) { create(:project) } + let(:master_pickable_commit) { project.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } + let(:master_pickable_merge) { project.commit('e56497bb5f03a90a51293fc6d516788730953899') } + + + before do + login_as :user + project.team << [@user, :master] + visit namespace_project_commits_path(project.namespace, project, project.repository.root_ref, { limit: 5 }) + end + + context "I cherry-pick a commit" do + it do + visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id) + find("a[href='#modal-cherry-pick-commit']").click + page.within('#modal-cherry-pick-commit') do + uncheck 'create_merge_request' + click_button 'Cherry-pick' + end + expect(page).to have_content('The commit has been successfully cherry-picked.') + end + end + + context "I cherry-pick a merge commit" do + it do + visit namespace_project_commit_path(project.namespace, project, master_pickable_merge.id) + find("a[href='#modal-cherry-pick-commit']").click + page.within('#modal-cherry-pick-commit') do + uncheck 'create_merge_request' + click_button 'Cherry-pick' + end + expect(page).to have_content('The commit has been successfully cherry-picked.') + end + end + + context "I cherry-pick a commit that was previously cherry-picked" do + it do + visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id) + find("a[href='#modal-cherry-pick-commit']").click + page.within('#modal-cherry-pick-commit') do + uncheck 'create_merge_request' + click_button 'Cherry-pick' + end + visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id) + find("a[href='#modal-cherry-pick-commit']").click + page.within('#modal-cherry-pick-commit') do + uncheck 'create_merge_request' + click_button 'Cherry-pick' + end + expect(page).to have_content('Sorry, we cannot cherry-pick this commit automatically.') + end + end + + context "I cherry-pick a commit in a new merge request" do + it do + visit namespace_project_commit_path(project.namespace, project, master_pickable_commit.id) + find("a[href='#modal-cherry-pick-commit']").click + page.within('#modal-cherry-pick-commit') do + click_button 'Cherry-pick' + end + expect(page).to have_content('The commit has been successfully cherry-picked. You can now submit a merge request to get this change into the original branch.') + end + end +end diff --git a/spec/features/projects/developer_views_empty_project_instructions_spec.rb b/spec/features/projects/developer_views_empty_project_instructions_spec.rb new file mode 100644 index 00000000000..0c51fe72ca4 --- /dev/null +++ b/spec/features/projects/developer_views_empty_project_instructions_spec.rb @@ -0,0 +1,63 @@ +require 'rails_helper' + +feature 'Developer views empty project instructions', feature: true do + let(:project) { create(:empty_project, :empty_repo) } + let(:developer) { create(:user) } + + background do + project.team << [developer, :developer] + + login_as(developer) + end + + context 'without an SSH key' do + scenario 'defaults to HTTP' do + visit_project + + expect_instructions_for('http') + end + + scenario 'switches to SSH', js: true do + visit_project + + select_protocol('SSH') + + expect_instructions_for('ssh') + end + end + + context 'with an SSH key' do + background do + create(:personal_key, user: developer) + end + + scenario 'defaults to SSH' do + visit_project + + expect_instructions_for('ssh') + end + + scenario 'switches to HTTP', js: true do + visit_project + + select_protocol('HTTP') + + expect_instructions_for('http') + end + end + + def visit_project + visit namespace_project_path(project.namespace, project) + end + + def select_protocol(protocol) + find('#clone-dropdown').click + find(".#{protocol.downcase}-selector").click + end + + def expect_instructions_for(protocol) + msg = :"#{protocol.downcase}_url_to_repo" + + expect(page).to have_content("git clone #{project.send(msg)}") + 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 new file mode 100644 index 00000000000..3d6ffbc4c6b --- /dev/null +++ b/spec/features/projects/files/project_owner_creates_license_file_spec.rb @@ -0,0 +1,61 @@ +require 'spec_helper' + +feature 'project owner creates a license file', feature: true, js: true do + include Select2Helper + + let(:project_master) { create(:user) } + let(:project) { create(:project) } + background do + project.repository.remove_file(project_master, 'LICENSE', 'Remove LICENSE', 'master') + project.team << [project_master, :master] + login_as(project_master) + visit namespace_project_path(project.namespace, project) + end + + scenario 'project master creates a license file manually from a template' do + visit namespace_project_tree_path(project.namespace, project, project.repository.root_ref) + find('.add-to-tree').click + click_link 'New file' + + fill_in :file_name, with: 'LICENSE' + + expect(page).to have_selector('.license-selector') + + select2('mit', from: '#license_type') + + 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}") + + fill_in :commit_message, with: 'Add a LICENSE file', visible: true + click_button 'Commit Changes' + + 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}") + end + + scenario 'project master creates a license file from the "Add license" link' do + click_link 'Add License' + + expect(current_path).to eq( + namespace_project_new_blob_path(project.namespace, project, 'master')) + expect(find('#file_name').value).to eq('LICENSE') + expect(page).to have_selector('.license-selector') + + select2('mit', from: '#license_type') + + 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}") + + fill_in :commit_message, with: 'Add a LICENSE file', visible: true + click_button 'Commit Changes' + + 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}") + 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 new file mode 100644 index 00000000000..3268e240200 --- /dev/null +++ b/spec/features/projects/files/project_owner_sees_link_to_create_license_file_in_empty_project_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +feature 'project owner sees a link to create a license file in empty project', feature: true, js: true do + include Select2Helper + + let(:project_master) { create(:user) } + let(:project) { create(:empty_project) } + background do + project.team << [project_master, :master] + login_as(project_master) + end + + scenario 'project master creates a license file from a template' do + visit namespace_project_path(project.namespace, project) + click_link 'Create empty bare repository' + click_on 'LICENSE' + + expect(current_path).to eq( + namespace_project_new_blob_path(project.namespace, project, 'master')) + expect(find('#file_name').value).to eq('LICENSE') + expect(page).to have_selector('.license-selector') + + select2('mit', from: '#license_type') + + 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}") + + fill_in :commit_message, with: 'Add a LICENSE file', visible: true + # Remove pre-receive hook so we can push without auth + FileUtils.rm_f(File.join(project.repository.path, 'hooks', 'pre-receive')) + click_button 'Commit Changes' + + 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}") + end +end diff --git a/spec/features/projects/members/anonymous_user_sees_members_spec.rb b/spec/features/projects/members/anonymous_user_sees_members_spec.rb new file mode 100644 index 00000000000..c5e3d143d91 --- /dev/null +++ b/spec/features/projects/members/anonymous_user_sees_members_spec.rb @@ -0,0 +1,20 @@ +require 'spec_helper' + +feature 'Projects > Members > Anonymous user sees members', feature: true do + let(:user) { create(:user) } + let(:group) { create(:group, :public) } + let(:project) { create(:empty_project, :public) } + + background do + project.team << [user, :master] + create(:project_group_link, project: project, group: group) + end + + scenario "anonymous user visits the project's members page and sees the list of members" do + visit namespace_project_project_members_path(project.namespace, project) + + expect(current_path).to eq( + namespace_project_project_members_path(project.namespace, project)) + expect(page).to have_content(user.name) + end +end diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb new file mode 100644 index 00000000000..7e6eef65873 --- /dev/null +++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb @@ -0,0 +1,83 @@ +require 'spec_helper' + +feature 'Projects > Wiki > User creates wiki page', feature: true do + let(:user) { create(:user) } + + background do + project.team << [user, :master] + login_as(user) + + visit namespace_project_path(project.namespace, project) + click_link 'Wiki' + end + + context 'in the user namespace' do + let(:project) { create(:project, namespace: user.namespace) } + + context 'when wiki is empty' do + scenario 'directly from the wiki home page' do + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Create page' + + expect(page).to have_content('Home') + expect(page).to have_content("last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + + context 'when wiki is not empty' do + before do + WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute + end + + scenario 'via the "new wiki page" page', js: true do + click_link 'New Page' + + fill_in :new_wiki_path, with: 'foo' + click_button 'Create Page' + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Create page' + + expect(page).to have_content('Foo') + expect(page).to have_content("last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + end + + context 'in a group namespace' do + let(:project) { create(:project, namespace: create(:group, :public)) } + + context 'when wiki is empty' do + scenario 'directly from the wiki home page' do + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Create page' + + expect(page).to have_content('Home') + expect(page).to have_content("last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + + context 'when wiki is not empty' do + before do + WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute + end + + scenario 'via the "new wiki page" page', js: true do + click_link 'New Page' + + fill_in :new_wiki_path, with: 'foo' + click_button 'Create Page' + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Create page' + + expect(page).to have_content('Foo') + expect(page).to have_content("last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + end +end diff --git a/spec/features/projects/wiki/user_updates_wiki_page_spec.rb b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb new file mode 100644 index 00000000000..ef82d2375dd --- /dev/null +++ b/spec/features/projects/wiki/user_updates_wiki_page_spec.rb @@ -0,0 +1,44 @@ +require 'spec_helper' + +feature 'Projects > Wiki > User updates wiki page', feature: true do + let(:user) { create(:user) } + + background do + project.team << [user, :master] + login_as(user) + + visit namespace_project_path(project.namespace, project) + WikiPages::CreateService.new(project, user, title: 'home', content: 'Home page').execute + click_link 'Wiki' + end + + context 'in the user namespace' do + let(:project) { create(:project, namespace: user.namespace) } + + scenario 'the home page' do + click_link 'Edit' + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Save changes' + + expect(page).to have_content('Home') + expect(page).to have_content("last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end + + context 'in a group namespace' do + let(:project) { create(:project, namespace: create(:group, :public)) } + + scenario 'the home page' do + click_link 'Edit' + + fill_in :wiki_content, with: 'My awesome wiki!' + click_button 'Save changes' + + expect(page).to have_content('Home') + expect(page).to have_content("last edited by #{user.name}") + expect(page).to have_content('My awesome wiki!') + end + end +end diff --git a/spec/features/projects_spec.rb b/spec/features/projects_spec.rb index 782c0bfe666..9dd0378d165 100644 --- a/spec/features/projects_spec.rb +++ b/spec/features/projects_spec.rb @@ -104,6 +104,33 @@ feature 'Project', feature: true do end end + describe 'project title' do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + let(:project2) { create(:project, namespace: user.namespace, path: 'test') } + let(:issue) { create(:issue, project: project) } + + context 'on issues page', js: true do + before do + login_with(user) + project.team.add_user(user, Gitlab::Access::MASTER) + project2.team.add_user(user, Gitlab::Access::MASTER) + visit namespace_project_issue_path(project.namespace, project, issue) + end + + it 'click toggle and show dropdown' do + find('.js-projects-dropdown-toggle').click + expect(page).to have_css('.dropdown-menu-projects .dropdown-content li', count: 2) + + page.within '.dropdown-menu-projects' do + click_link project.name_with_namespace + end + + expect(page).to have_content project.name + end + end + end + def remove_with_confirm(button_text, confirm_with) click_button button_text fill_in 'confirm_name_input', with: confirm_with diff --git a/spec/features/runners_spec.rb b/spec/features/runners_spec.rb index e8886e7edf9..8edeb8d18af 100644 --- a/spec/features/runners_spec.rb +++ b/spec/features/runners_spec.rb @@ -80,6 +80,22 @@ describe "Runners" do end end + describe "shared runners description" do + let(:shared_runners_text) { 'custom **shared** runners description' } + let(:shared_runners_html) { 'custom shared runners description' } + + before do + stub_application_setting(shared_runners_text: shared_runners_text) + project = FactoryGirl.create :empty_project, shared_runners_enabled: false + project.team << [user, :master] + visit runners_path(project) + end + + it "sees shared runners description" do + expect(page.find(".shared-runners-description")).to have_content(shared_runners_html) + end + end + describe "show page" do before do @project = FactoryGirl.create :empty_project diff --git a/spec/features/security/project/internal_access_spec.rb b/spec/features/security/project/internal_access_spec.rb index 79d5bf4cf06..8625ea6bc10 100644 --- a/spec/features/security/project/internal_access_spec.rb +++ b/spec/features/security/project/internal_access_spec.rb @@ -101,12 +101,12 @@ describe "Internal Project Access", feature: true do it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } - it { is_expected.to be_denied_for developer } - it { is_expected.to be_denied_for reporter } - it { is_expected.to be_denied_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :external } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_denied_for :external } end describe "GET /:project_path/blob" do diff --git a/spec/features/security/project/private_access_spec.rb b/spec/features/security/project/private_access_spec.rb index 0a89193eb67..544270b4037 100644 --- a/spec/features/security/project/private_access_spec.rb +++ b/spec/features/security/project/private_access_spec.rb @@ -101,9 +101,9 @@ describe "Private Project Access", feature: true do it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } - it { is_expected.to be_denied_for developer } - it { is_expected.to be_denied_for reporter } - it { is_expected.to be_denied_for guest } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } it { is_expected.to be_denied_for :user } it { is_expected.to be_denied_for :external } it { is_expected.to be_denied_for :visitor } diff --git a/spec/features/security/project/public_access_spec.rb b/spec/features/security/project/public_access_spec.rb index 40daac89d40..4def4f99bc0 100644 --- a/spec/features/security/project/public_access_spec.rb +++ b/spec/features/security/project/public_access_spec.rb @@ -101,12 +101,12 @@ describe "Public Project Access", feature: true do it { is_expected.to be_allowed_for :admin } it { is_expected.to be_allowed_for owner } it { is_expected.to be_allowed_for master } - it { is_expected.to be_denied_for developer } - it { is_expected.to be_denied_for reporter } - it { is_expected.to be_denied_for guest } - it { is_expected.to be_denied_for :user } - it { is_expected.to be_denied_for :external } - it { is_expected.to be_denied_for :visitor } + it { is_expected.to be_allowed_for developer } + it { is_expected.to be_allowed_for reporter } + it { is_expected.to be_allowed_for guest } + it { is_expected.to be_allowed_for :user } + it { is_expected.to be_allowed_for :visitor } + it { is_expected.to be_allowed_for :external } end describe "GET /:project_path/builds" do diff --git a/spec/features/signup_spec.rb b/spec/features/signup_spec.rb new file mode 100644 index 00000000000..58aabd913eb --- /dev/null +++ b/spec/features/signup_spec.rb @@ -0,0 +1,55 @@ +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 + + 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") + end + end + + describe 'signup with errors' do + it "displays the errors" do + existing_user = create(:user) + 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: existing_user.email + fill_in 'new_user_password', with: user.password + click_button "Sign up" + + expect(current_path).to eq user_registration_path + expect(page).to have_content("error prohibited this user from being saved") + expect(page).to have_content("Email has already been taken") + end + + it 'does not redisplay the password' do + existing_user = create(:user) + 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: existing_user.email + fill_in 'new_user_password', with: user.password + click_button "Sign up" + + expect(current_path).to eq user_registration_path + expect(page.body).not_to match(/#{user.password}/) + end + end +end diff --git a/spec/features/tags/master_creates_tag_spec.rb b/spec/features/tags/master_creates_tag_spec.rb new file mode 100644 index 00000000000..08a97085a9c --- /dev/null +++ b/spec/features/tags/master_creates_tag_spec.rb @@ -0,0 +1,62 @@ +require 'spec_helper' + +feature 'Master creates tag', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + project.team << [user, :master] + login_with(user) + visit namespace_project_tags_path(project.namespace, project) + end + + scenario 'with an invalid name displays an error' do + create_tag_in_form(tag: 'v 1.0', ref: 'master') + + expect(page).to have_content 'Tag name invalid' + end + + scenario 'with an invalid reference displays an error' do + create_tag_in_form(tag: 'v2.0', ref: 'foo') + + expect(page).to have_content 'Target foo is invalid' + end + + scenario 'that already exists displays an error' do + create_tag_in_form(tag: 'v1.1.0', ref: 'master') + + expect(page).to have_content 'Tag v1.1.0 already exists' + end + + scenario 'with multiline message displays the message in a <pre> block' do + create_tag_in_form(tag: 'v3.0', ref: 'master', message: "Awesome tag message\n\n- hello\n- world") + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v3.0')) + expect(page).to have_content 'v3.0' + page.within 'pre.body' do + expect(page).to have_content "Awesome tag message\n\n- hello\n- world" + end + end + + scenario 'with multiline release notes parses the release note as Markdown' do + create_tag_in_form(tag: 'v4.0', ref: 'master', desc: "Awesome release notes\n\n- hello\n- world") + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v4.0')) + expect(page).to have_content 'v4.0' + page.within '.description' do + expect(page).to have_content 'Awesome release notes' + expect(page).to have_selector('ul li', count: 2) + end + end + + def create_tag_in_form(tag:, ref:, message: nil, desc: nil) + click_link 'New tag' + fill_in 'tag_name', with: tag + fill_in 'ref', with: ref + fill_in 'message', with: message unless message.nil? + fill_in 'release_description', with: desc unless desc.nil? + click_button 'Create tag' + end +end diff --git a/spec/features/tags/master_deletes_tag_spec.rb b/spec/features/tags/master_deletes_tag_spec.rb new file mode 100644 index 00000000000..f0990118e3c --- /dev/null +++ b/spec/features/tags/master_deletes_tag_spec.rb @@ -0,0 +1,41 @@ +require 'spec_helper' + +feature 'Master deletes tag', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + project.team << [user, :master] + login_with(user) + visit namespace_project_tags_path(project.namespace, project) + end + + context 'from the tags list page' do + scenario 'deletes the tag' do + expect(page).to have_content 'v1.1.0' + + page.within('.content') do + first('.btn-remove').click + end + + expect(current_path).to eq( + namespace_project_tags_path(project.namespace, project)) + expect(page).not_to have_content 'v1.1.0' + end + + end + + context 'from a specific tag page' do + scenario 'deletes the tag' do + click_on 'v1.0.0' + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.0.0')) + + click_on 'Delete tag' + + expect(current_path).to eq( + namespace_project_tags_path(project.namespace, project)) + expect(page).not_to have_content 'v1.0.0' + end + end +end diff --git a/spec/features/tags/master_updates_tag_spec.rb b/spec/features/tags/master_updates_tag_spec.rb new file mode 100644 index 00000000000..c926e9841f3 --- /dev/null +++ b/spec/features/tags/master_updates_tag_spec.rb @@ -0,0 +1,42 @@ +require 'spec_helper' + +feature 'Master updates tag', feature: true do + let(:user) { create(:user) } + let(:project) { create(:project, namespace: user.namespace) } + + before do + project.team << [user, :master] + login_with(user) + visit namespace_project_tags_path(project.namespace, project) + end + + context 'from the tags list page' do + scenario 'updates the release notes' do + page.within(first('.controls')) do + click_link 'Edit release notes' + end + + fill_in 'release_description', with: 'Awesome release notes' + click_button 'Save changes' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.1.0')) + expect(page).to have_content 'v1.1.0' + expect(page).to have_content 'Awesome release notes' + end + end + + context 'from a specific tag page' do + scenario 'updates the release notes' do + click_on 'v1.1.0' + click_link 'Edit release notes' + fill_in 'release_description', with: 'Awesome release notes' + click_button 'Save changes' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.1.0')) + expect(page).to have_content 'v1.1.0' + expect(page).to have_content 'Awesome release notes' + end + end +end diff --git a/spec/features/tags/master_views_tags_spec.rb b/spec/features/tags/master_views_tags_spec.rb new file mode 100644 index 00000000000..29d2c244720 --- /dev/null +++ b/spec/features/tags/master_views_tags_spec.rb @@ -0,0 +1,73 @@ +require 'spec_helper' + +feature 'Master views tags', feature: true do + let(:user) { create(:user) } + + before do + project.team << [user, :master] + login_with(user) + end + + context 'when project has no tags' do + let(:project) { create(:project_empty_repo) } + before do + visit namespace_project_path(project.namespace, project) + click_on 'README' + fill_in :commit_message, with: 'Add a README file', visible: true + # Remove pre-receive hook so we can push without auth + FileUtils.rm_f(File.join(project.repository.path, 'hooks', 'pre-receive')) + click_button 'Commit Changes' + visit namespace_project_tags_path(project.namespace, project) + end + + scenario 'displays a specific message' do + expect(page).to have_content 'Repository has no tags yet.' + end + end + + context 'when project has tags' do + let(:project) { create(:project, namespace: user.namespace) } + before do + visit namespace_project_tags_path(project.namespace, project) + end + + scenario 'views the tags list page' do + expect(page).to have_content 'v1.0.0' + end + + scenario 'views a specific tag page' do + click_on 'v1.0.0' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.0.0')) + expect(page).to have_content 'v1.0.0' + expect(page).to have_content 'This tag has no release notes.' + end + + describe 'links on the tag page' do + scenario 'has a button to browse files' do + click_on 'v1.0.0' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.0.0')) + + click_on 'Browse files' + + expect(current_path).to eq( + namespace_project_tree_path(project.namespace, project, 'v1.0.0')) + end + + scenario 'has a button to browse commits' do + click_on 'v1.0.0' + + expect(current_path).to eq( + namespace_project_tag_path(project.namespace, project, 'v1.0.0')) + + click_on 'Browse commits' + + expect(current_path).to eq( + namespace_project_commits_path(project.namespace, project, 'v1.0.0')) + end + end + end +end diff --git a/spec/features/todos/todos_spec.rb b/spec/features/todos/todos_spec.rb new file mode 100644 index 00000000000..3354f529295 --- /dev/null +++ b/spec/features/todos/todos_spec.rb @@ -0,0 +1,81 @@ +require 'spec_helper' + +describe 'Dashboard Todos', feature: true do + let(:user) { create(:user) } + let(:author) { create(:user) } + let(:project) { create(:project) } + let(:issue) { create(:issue) } + + describe 'GET /dashboard/todos' do + context 'User does not have todos' do + before do + login_as(user) + visit dashboard_todos_path + end + it 'shows "All done" message' do + expect(page).to have_content "You're all done!" + end + end + + context 'User has a todo', js: true do + before do + create(:todo, :mentioned, user: user, project: project, target: issue, author: author) + login_as(user) + visit dashboard_todos_path + end + + it 'todo is present' do + expect(page).to have_selector('.todos-list .todo', count: 1) + end + + describe 'deleting the todo' do + before do + first('.done-todo').click + end + + it 'is removed from the list' do + expect(page).not_to have_selector('.todos-list .todo') + end + + it 'shows "All done" message' do + expect(page).to have_content("You're all done!") + end + end + end + + context 'User has multiple pages of Todos' do + before do + allow(Todo).to receive(:default_per_page).and_return(1) + + # Create just enough records to cause us to paginate + create_list(:todo, 2, :mentioned, user: user, project: project, target: issue, author: author) + + login_as(user) + end + + it 'is paginated' do + visit dashboard_todos_path + + expect(page).to have_selector('.gl-pagination') + end + + it 'is has the right number of pages' do + visit dashboard_todos_path + + expect(page).to have_selector('.gl-pagination .page', count: 2) + end + + describe 'completing last todo from last page', js: true do + it 'redirects to the previous page' do + visit dashboard_todos_path(page: 2) + expect(page).to have_css("#todo_#{Todo.last.id}") + + click_link('Done') + + expect(current_path).to eq dashboard_todos_path + expect(page).to have_css("#todo_#{Todo.first.id}") + end + end + end + end +end diff --git a/spec/features/users_spec.rb b/spec/features/users_spec.rb index c1248162031..cf116040394 100644 --- a/spec/features/users_spec.rb +++ b/spec/features/users_spec.rb @@ -5,10 +5,10 @@ feature 'Users', feature: true do scenario 'GET /users/sign_in creates a new user account' do visit new_user_session_path - fill_in 'user_name', with: 'Name Surname' - fill_in 'user_username', with: 'Great' - fill_in 'user_email', with: 'name@mail.com' - fill_in 'user_password_sign_up', with: 'password1234' + fill_in 'new_user_name', with: 'Name Surname' + fill_in 'new_user_username', with: 'Great' + fill_in 'new_user_email', with: 'name@mail.com' + fill_in 'new_user_password', with: 'password1234' expect { click_button 'Sign up' }.to change { User.count }.by(1) end @@ -31,10 +31,10 @@ feature 'Users', feature: true do scenario 'Should show one error if email is already taken' do visit new_user_session_path - fill_in 'user_name', with: 'Another user name' - fill_in 'user_username', with: 'anotheruser' - fill_in 'user_email', with: user.email - fill_in 'user_password_sign_up', with: '12341234' + fill_in 'new_user_name', with: 'Another user name' + fill_in 'new_user_username', with: 'anotheruser' + fill_in 'new_user_email', with: user.email + fill_in 'new_user_password', with: '12341234' expect { click_button 'Sign up' }.to change { User.count }.by(0) expect(page).to have_text('Email has already been taken') expect(number_of_errors_on_page(page)).to be(1), 'errors on page:\n #{errors_on_page page}' diff --git a/spec/finders/issues_finder_spec.rb b/spec/finders/issues_finder_spec.rb index b1648055462..bc607a29751 100644 --- a/spec/finders/issues_finder_spec.rb +++ b/spec/finders/issues_finder_spec.rb @@ -62,6 +62,22 @@ describe IssuesFinder do expect(issues).to eq([issue2]) end + it 'returns unique issues when filtering by multiple labels' do + label2 = create(:label, project: project2) + + create(:label_link, label: label2, target: issue2) + + params = { + scope: 'all', + label_name: [label.title, label2.title].join(','), + state: 'opened' + } + + issues = IssuesFinder.new(user, params).execute + + expect(issues).to eq([issue2]) + 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 diff --git a/spec/fixtures/sanitized.svg b/spec/fixtures/sanitized.svg new file mode 100644 index 00000000000..8f84b8f5e20 --- /dev/null +++ b/spec/fixtures/sanitized.svg @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 622 682"> + + <defs> + <style>.cls-1{fill:#30353e;}.cls-2{fill:#8c929d;}.cls-3{fill:#fc6d26;}.cls-4{fill:#e24329;}.cls-5{fill:#fca326;}</style> + </defs> + <title>stacked_wm</title> + <path id="bg" class="cls-1" d="M622,681H0V-1H622V681h0Z"/> + <g id="g12"> + <path id="path14" class="cls-2" d="M316.89,497.72h-19l0.06,141.74H375V621.93h-58l-0.06-124.22h0Z"/> + </g> + <g id="g24"> + <path id="path26" class="cls-2" d="M448.32,614.57a32.46,32.46,0,0,1-23.59,10c-14.5,0-20.35-7.14-20.35-16.45,0-14.07,9.74-20.77,30.52-20.77a86.46,86.46,0,0,1,13.42,1.08v26.19h0Zm-19.7-85.91a63.45,63.45,0,0,0-40.5,14.53l6.73,11.66c7.79-4.54,17.32-9.09,31-9.09,15.58,0,22.51,8,22.51,21.42v6.93a81.48,81.48,0,0,0-13.2-1.08c-33.33,0-50.22,11.69-50.22,36.14,0,21.86,13.42,32.89,33.76,32.89,13.71,0,26.84-6.28,31.38-16.45l3.46,13.85h13.42V567c0-22.94-10-38.3-38.31-38.3h0Z"/> + </g> + <g id="g28"> + <path id="path30" class="cls-2" d="M528.4,625.18c-7.14,0-13.42-.87-18.18-3V556.58c6.49-5.41,14.5-9.31,24.68-9.31,18.4,0,25.54,13,25.54,34,0,29.86-11.47,43.93-32,43.93m8-96.52a34.88,34.88,0,0,0-26.19,11.58V522l-0.06-24.24H491.54L491.6,636c9.31,3.9,22.08,6.06,35.93,6.06,35.5,0,52.6-22.72,52.6-61.89,0-30.95-15.8-51.51-43.73-51.51"/> + </g> + <g id="g32"> + <path id="path34" class="cls-2" d="M109.84,513.08c16.88,0,27.7,5.63,34.85,11.25l8.19-14.18c-11.16-9.78-26.16-15-42.17-15-40.47,0-68.83,24.67-68.83,74.44,0,52.15,30.59,72.5,65.58,72.5a111,111,0,0,0,42.21-8.22l-0.4-55.72V560.58H97.32v17.53h33.12l0.4,42.31c-4.33,2.16-11.9,3.9-22.08,3.9-28.14,0-47-17.7-47-55,0-37.87,19.48-56.26,48.05-56.26"/> + </g> + <g id="g36"> + <path id="path38" class="cls-2" d="M243.79,497.72H225.17l0.06,23.8v82.23c0,22.94,10,38.3,38.31,38.3A64.16,64.16,0,0,0,275,641V624.31a57,57,0,0,1-8.66.65c-15.58,0-22.51-8-22.51-21.42v-56.7H275V531.26H243.85l-0.06-33.54h0Z"/> + </g> + <path id="path40" class="cls-2" d="M177.94,639.46h18.61V531.26H177.94v108.2h0Z"/> + <path id="path42" class="cls-2" d="M177.94,516.33h18.61V497.72H177.94v18.61h0Z"/> + <g id="g44"> + <path id="path46" class="cls-3" d="M525.05,266.23l-24-74L453.36,45.6a8.19,8.19,0,0,0-15.58,0L390.12,192.24H231.88L184.22,45.6a8.19,8.19,0,0,0-15.58,0L121,192.24l-24,74a16.38,16.38,0,0,0,6,18.31L311,435.71,519.1,284.54a16.38,16.38,0,0,0,6-18.31"/> + </g> + <g id="g48"> + <path id="path50" class="cls-4" d="M311,435.71h0l79.12-243.47H231.88L311,435.71h0Z"/> + </g> + <g id="g56"> + <path id="path58" class="cls-3" d="M311,435.71L231.88,192.24H121L311,435.71h0Z"/> + </g> + <g id="g64"> + <path id="path66" class="cls-5" d="M121,192.24h0l-24,74a16.37,16.37,0,0,0,6,18.31L311,435.7,121,192.24h0Z"/> + </g> + <g id="g72"> + <path id="path74" class="cls-4" d="M121,192.24H231.88L184.22,45.6a8.19,8.19,0,0,0-15.58,0L121,192.24h0Z"/> + </g> + <g id="g76"> + <path id="path78" class="cls-3" d="M311,435.71l79.12-243.47H501L311,435.71h0Z"/> + </g> + <g id="g80"> + <path id="path82" class="cls-5" d="M501,192.24h0l24,74a16.37,16.37,0,0,1-6,18.31L311,435.7,501,192.24h0Z"/> + </g> + <g id="g84"> + <path id="path86" class="cls-4" d="M501,192.24H390.12L437.78,45.6a8.19,8.19,0,0,1,15.58,0L501,192.24h0Z"/> + </g> +</svg> diff --git a/spec/fixtures/unsanitized.svg b/spec/fixtures/unsanitized.svg new file mode 100644 index 00000000000..3957557334b --- /dev/null +++ b/spec/fixtures/unsanitized.svg @@ -0,0 +1,50 @@ +<?xml version="1.0"?> +<svg xmlns="http://www.w3.org/2000/svg" id="Layer_1" data-name="Layer 1" viewBox="0 0 622 682" filterMe="test"> + <iframe src="http://www.google.com"></iframe> + <defs> + <style>.cls-1{fill:#30353e;}.cls-2{fill:#8c929d;}.cls-3{fill:#fc6d26;}.cls-4{fill:#e24329;}.cls-5{fill:#fca326;}</style> + </defs> + <title>stacked_wm</title> + <path id="bg" class="cls-1" d="M622,681H0V-1H622V681h0Z"/> + <g id="g12"> + <path id="path14" class="cls-2" d="M316.89,497.72h-19l0.06,141.74H375V621.93h-58l-0.06-124.22h0Z"/> + </g> + <g id="g24"> + <path id="path26" class="cls-2" d="M448.32,614.57a32.46,32.46,0,0,1-23.59,10c-14.5,0-20.35-7.14-20.35-16.45,0-14.07,9.74-20.77,30.52-20.77a86.46,86.46,0,0,1,13.42,1.08v26.19h0Zm-19.7-85.91a63.45,63.45,0,0,0-40.5,14.53l6.73,11.66c7.79-4.54,17.32-9.09,31-9.09,15.58,0,22.51,8,22.51,21.42v6.93a81.48,81.48,0,0,0-13.2-1.08c-33.33,0-50.22,11.69-50.22,36.14,0,21.86,13.42,32.89,33.76,32.89,13.71,0,26.84-6.28,31.38-16.45l3.46,13.85h13.42V567c0-22.94-10-38.3-38.31-38.3h0Z"/> + </g> + <g id="g28"> + <path id="path30" class="cls-2" d="M528.4,625.18c-7.14,0-13.42-.87-18.18-3V556.58c6.49-5.41,14.5-9.31,24.68-9.31,18.4,0,25.54,13,25.54,34,0,29.86-11.47,43.93-32,43.93m8-96.52a34.88,34.88,0,0,0-26.19,11.58V522l-0.06-24.24H491.54L491.6,636c9.31,3.9,22.08,6.06,35.93,6.06,35.5,0,52.6-22.72,52.6-61.89,0-30.95-15.8-51.51-43.73-51.51"/> + </g> + <g id="g32"> + <path id="path34" class="cls-2" d="M109.84,513.08c16.88,0,27.7,5.63,34.85,11.25l8.19-14.18c-11.16-9.78-26.16-15-42.17-15-40.47,0-68.83,24.67-68.83,74.44,0,52.15,30.59,72.5,65.58,72.5a111,111,0,0,0,42.21-8.22l-0.4-55.72V560.58H97.32v17.53h33.12l0.4,42.31c-4.33,2.16-11.9,3.9-22.08,3.9-28.14,0-47-17.7-47-55,0-37.87,19.48-56.26,48.05-56.26"/> + </g> + <g id="g36"> + <path id="path38" class="cls-2" d="M243.79,497.72H225.17l0.06,23.8v82.23c0,22.94,10,38.3,38.31,38.3A64.16,64.16,0,0,0,275,641V624.31a57,57,0,0,1-8.66.65c-15.58,0-22.51-8-22.51-21.42v-56.7H275V531.26H243.85l-0.06-33.54h0Z"/> + </g> + <path id="path40" class="cls-2" d="M177.94,639.46h18.61V531.26H177.94v108.2h0Z"/> + <path id="path42" class="cls-2" d="M177.94,516.33h18.61V497.72H177.94v18.61h0Z"/> + <g id="g44"> + <path id="path46" class="cls-3" d="M525.05,266.23l-24-74L453.36,45.6a8.19,8.19,0,0,0-15.58,0L390.12,192.24H231.88L184.22,45.6a8.19,8.19,0,0,0-15.58,0L121,192.24l-24,74a16.38,16.38,0,0,0,6,18.31L311,435.71,519.1,284.54a16.38,16.38,0,0,0,6-18.31"/> + </g> + <g id="g48"> + <path id="path50" class="cls-4" d="M311,435.71h0l79.12-243.47H231.88L311,435.71h0Z"/> + </g> + <g id="g56"> + <path id="path58" class="cls-3" d="M311,435.71L231.88,192.24H121L311,435.71h0Z"/> + </g> + <g id="g64"> + <path id="path66" class="cls-5" d="M121,192.24h0l-24,74a16.37,16.37,0,0,0,6,18.31L311,435.7,121,192.24h0Z"/> + </g> + <g id="g72"> + <path id="path74" class="cls-4" d="M121,192.24H231.88L184.22,45.6a8.19,8.19,0,0,0-15.58,0L121,192.24h0Z"/> + </g> + <g id="g76"> + <path id="path78" class="cls-3" d="M311,435.71l79.12-243.47H501L311,435.71h0Z"/> + </g> + <g id="g80"> + <path id="path82" class="cls-5" d="M501,192.24h0l24,74a16.37,16.37,0,0,1-6,18.31L311,435.7,501,192.24h0Z"/> + </g> + <g id="g84"> + <path id="path86" class="cls-4" d="M501,192.24H390.12L437.78,45.6a8.19,8.19,0,0,1,15.58,0L501,192.24h0Z"/> + </g> +</svg> diff --git a/spec/helpers/blob_helper_spec.rb b/spec/helpers/blob_helper_spec.rb index 87849230dbe..6d1c02db297 100644 --- a/spec/helpers/blob_helper_spec.rb +++ b/spec/helpers/blob_helper_spec.rb @@ -67,4 +67,16 @@ describe BlobHelper do expect(result).to eq(expected) end end + + describe "#sanitize_svg" do + let(:input_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'unsanitized.svg') } + let(:data) { open(input_svg_path).read } + let(:expected_svg_path) { File.join(Rails.root, 'spec', 'fixtures', 'sanitized.svg') } + let(:expected) { open(expected_svg_path).read } + + it 'should retain essential elements' do + blob = OpenStruct.new(data: data) + expect(sanitize_svg(blob).data).to eq(expected) + end + end end diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb index 4f8d9c67262..f942695b6f0 100644 --- a/spec/helpers/ci_status_helper_spec.rb +++ b/spec/helpers/ci_status_helper_spec.rb @@ -6,8 +6,8 @@ describe CiStatusHelper do let(:success_commit) { double("Ci::Commit", status: 'success') } let(:failed_commit) { double("Ci::Commit", status: 'failed') } - describe 'ci_status_icon' do - it { expect(helper.ci_status_icon(success_commit)).to include('fa-check') } - it { expect(helper.ci_status_icon(failed_commit)).to include('fa-close') } + describe 'ci_icon_for_status' do + it { expect(helper.ci_icon_for_status(success_commit.status)).to include('fa-check') } + it { expect(helper.ci_icon_for_status(failed_commit.status)).to include('fa-close') } end end diff --git a/spec/helpers/commits_helper_spec.rb b/spec/helpers/commits_helper_spec.rb new file mode 100644 index 00000000000..727c25ff529 --- /dev/null +++ b/spec/helpers/commits_helper_spec.rb @@ -0,0 +1,29 @@ +require 'rails_helper' + +describe CommitsHelper do + describe 'commit_author_link' do + it 'escapes the author email' do + commit = double( + author: nil, + author_name: 'Persistent XSS', + author_email: 'my@email.com" onmouseover="alert(1)' + ) + + expect(helper.commit_author_link(commit)). + not_to include('onmouseover="alert(1)"') + end + end + + describe 'commit_committer_link' do + it 'escapes the committer email' do + commit = double( + committer: nil, + committer_name: 'Persistent XSS', + committer_email: 'my@email.com" onmouseover="alert(1)' + ) + + expect(helper.commit_committer_link(commit)). + not_to include('onmouseover="alert(1)"') + end + end +end diff --git a/spec/helpers/diff_helper_spec.rb b/spec/helpers/diff_helper_spec.rb index 982c113e84b..b7810185d16 100644 --- a/spec/helpers/diff_helper_spec.rb +++ b/spec/helpers/diff_helper_spec.rb @@ -11,6 +11,26 @@ describe DiffHelper do let(:diff_refs) { [commit.parent, commit] } let(:diff_file) { Gitlab::Diff::File.new(diff, diff_refs) } + describe 'diff_view' do + it 'returns a valid value when cookie is set' do + helper.request.cookies[:diff_view] = 'parallel' + + expect(helper.diff_view).to eq 'parallel' + end + + it 'returns a default value when cookie is invalid' do + helper.request.cookies[:diff_view] = 'invalid' + + expect(helper.diff_view).to eq 'inline' + end + + it 'returns a default value when cookie is nil' do + expect(helper.request.cookies).to be_empty + + expect(helper.diff_view).to eq 'inline' + end + end + describe 'diff_hard_limit_enabled?' do it 'should return true if param is provided' do allow(controller).to receive(:params) { { force_show_diff: true } } diff --git a/spec/helpers/import_helper_spec.rb b/spec/helpers/import_helper_spec.rb new file mode 100644 index 00000000000..3391234e9f5 --- /dev/null +++ b/spec/helpers/import_helper_spec.rb @@ -0,0 +1,25 @@ +require 'rails_helper' + +describe ImportHelper do + describe '#github_project_link' do + context 'when provider does not specify a custom URL' do + it 'uses default GitHub URL' do + allow(Gitlab.config.omniauth).to receive(:providers). + and_return([Settingslogic.new('name' => 'github')]) + + expect(helper.github_project_link('octocat/Hello-World')). + to include('href="https://github.com/octocat/Hello-World"') + end + end + + context 'when provider specify a custom URL' do + it 'uses custom URL' do + allow(Gitlab.config.omniauth).to receive(:providers). + and_return([Settingslogic.new('name' => 'github', 'url' => 'https://github.company.com')]) + + expect(helper.github_project_link('octocat/Hello-World')). + to include('href="https://github.company.com/octocat/Hello-World"') + end + end + end +end diff --git a/spec/helpers/issues_helper_spec.rb b/spec/helpers/issues_helper_spec.rb index 2d4d9c18c9d..eae61a54dfc 100644 --- a/spec/helpers/issues_helper_spec.rb +++ b/spec/helpers/issues_helper_spec.rb @@ -30,6 +30,18 @@ describe IssuesHelper do expect(url_for_project_issues).to eq "" end + it 'returns an empty string if project_url is invalid' do + expect(project).to receive_message_chain('issues_tracker.project_url') { 'javascript:alert("foo");' } + + expect(url_for_project_issues(project)).to eq '' + end + + it 'returns an empty string if project_path is invalid' do + expect(project).to receive_message_chain('issues_tracker.project_path') { 'javascript:alert("foo");' } + + expect(url_for_project_issues(project, only_path: true)).to eq '' + end + describe "when external tracker was enabled and then config removed" do before do @project = ext_project @@ -68,6 +80,18 @@ describe IssuesHelper do expect(url_for_issue(issue.iid)).to eq "" end + it 'returns an empty string if issue_url is invalid' do + expect(project).to receive_message_chain('issues_tracker.issue_url') { 'javascript:alert("foo");' } + + expect(url_for_issue(issue.iid, project)).to eq '' + end + + it 'returns an empty string if issue_path is invalid' do + expect(project).to receive_message_chain('issues_tracker.issue_path') { 'javascript:alert("foo");' } + + expect(url_for_issue(issue.iid, project, only_path: true)).to eq '' + end + describe "when external tracker was enabled and then config removed" do before do @project = ext_project @@ -105,6 +129,18 @@ describe IssuesHelper do expect(url_for_new_issue).to eq "" end + it 'returns an empty string if issue_url is invalid' do + expect(project).to receive_message_chain('issues_tracker.new_issue_url') { 'javascript:alert("foo");' } + + expect(url_for_new_issue(project)).to eq '' + end + + it 'returns an empty string if issue_path is invalid' do + expect(project).to receive_message_chain('issues_tracker.new_issue_path') { 'javascript:alert("foo");' } + + expect(url_for_new_issue(project, only_path: true)).to eq '' + end + describe "when external tracker was enabled and then config removed" do before do @project = ext_project diff --git a/spec/helpers/labels_helper_spec.rb b/spec/helpers/labels_helper_spec.rb index 39042ff7e91..501f150cfda 100644 --- a/spec/helpers/labels_helper_spec.rb +++ b/spec/helpers/labels_helper_spec.rb @@ -11,13 +11,13 @@ describe LabelsHelper do end it 'uses the instance variable' do - expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name=#{label.name}"><span class="[\w\s\-]*has-tooltip".*</span></a>} + expect(link_to_label(label)).to match %r{<a href="/#{@project.to_reference}/issues\?label_name%5B%5D=#{label.name}"><span class="[\w\s\-]*has-tooltip".*</span></a>} end end context 'without @project set' do it "uses the label's project" do - expect(link_to_label(label)).to match %r{<a href="/#{label.project.to_reference}/issues\?label_name=#{label.name}">.*</a>} + expect(link_to_label(label)).to match %r{<a href="/#{label.project.to_reference}/issues\?label_name%5B%5D=#{label.name}">.*</a>} end end @@ -25,7 +25,7 @@ describe LabelsHelper do let(:another_project) { double('project', namespace: 'foo3', to_param: 'bar3') } it 'links to merge requests page' do - expect(link_to_label(label, project: another_project)).to match %r{<a href="/foo3/bar3/issues\?label_name=#{label.name}">.*</a>} + expect(link_to_label(label, project: another_project)).to match %r{<a href="/foo3/bar3/issues\?label_name%5B%5D=#{label.name}">.*</a>} end end @@ -33,7 +33,7 @@ describe LabelsHelper do ['issue', :issue, 'merge_request', :merge_request].each do |type| context "set to #{type}" do it 'links to correct page' do - expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.to_reference}/#{type.to_s.pluralize}\?label_name=#{label.name}">.*</a>} + expect(link_to_label(label, type: type)).to match %r{<a href="/#{label.project.to_reference}/#{type.to_s.pluralize}\?label_name%5B%5D=#{label.name}">.*</a>} end end end diff --git a/spec/helpers/projects_helper_spec.rb b/spec/helpers/projects_helper_spec.rb index c258cfebd73..ac5af8740dc 100644 --- a/spec/helpers/projects_helper_spec.rb +++ b/spec/helpers/projects_helper_spec.rb @@ -88,21 +88,56 @@ describe ProjectsHelper do end describe 'default_clone_protocol' do - describe 'using HTTP' do + context 'when user is not logged in and gitlab protocol is HTTP' do it 'returns HTTP' do - expect(helper).to receive(:current_user).and_return(nil) + allow(helper).to receive(:current_user).and_return(nil) expect(helper.send(:default_clone_protocol)).to eq('http') end end - describe 'using HTTPS' do + context 'when user is not logged in and gitlab protocol is HTTPS' do it 'returns HTTPS' do - allow(Gitlab.config.gitlab).to receive(:protocol).and_return('https') - expect(helper).to receive(:current_user).and_return(nil) + stub_config_setting(protocol: 'https') + allow(helper).to receive(:current_user).and_return(nil) expect(helper.send(:default_clone_protocol)).to eq('https') end end end + + describe '#license_short_name' do + let(:project) { create(:project) } + + context 'when project.repository has a license_key' do + it 'returns the nickname of the license if present' do + allow(project.repository).to receive(:license_key).and_return('agpl-3.0') + + expect(helper.license_short_name(project)).to eq('GNU AGPLv3') + end + + it 'returns the name of the license if nickname is not present' do + allow(project.repository).to receive(:license_key).and_return('mit') + + expect(helper.license_short_name(project)).to eq('MIT License') + end + end + + context 'when project.repository has no license_key but a license_blob' do + it 'returns LICENSE' do + allow(project.repository).to receive(:license_key).and_return(nil) + + expect(helper.license_short_name(project)).to eq('LICENSE') + end + end + end + + describe '#sanitized_import_error' do + it 'removes the repo path' do + repo = File.join(Gitlab.config.gitlab_shell.repos_path, '/namespace/test.git') + import_error = "Could not clone #{repo}\n" + + expect(sanitize_repo_path(import_error)).to eq('Could not clone [REPOS PATH]/namespace/test.git') + end + end end diff --git a/spec/initializers/trusted_proxies_spec.rb b/spec/initializers/trusted_proxies_spec.rb new file mode 100644 index 00000000000..4bb149f25ff --- /dev/null +++ b/spec/initializers/trusted_proxies_spec.rb @@ -0,0 +1,51 @@ +require 'spec_helper' + +describe 'trusted_proxies', lib: true do + context 'with default config' do + before do + set_trusted_proxies([]) + end + + it 'preserves private IPs as remote_ip' do + request = stub_request('HTTP_X_FORWARDED_FOR' => '10.1.5.89') + expect(request.remote_ip).to eq('10.1.5.89') + end + + it 'filters out localhost from remote_ip' do + request = stub_request('HTTP_X_FORWARDED_FOR' => '1.1.1.1, 10.1.5.89, 127.0.0.1') + expect(request.remote_ip).to eq('10.1.5.89') + end + end + + context 'with private IP ranges added' do + before do + set_trusted_proxies([ "10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16" ]) + end + + it 'filters out private and local IPs from remote_ip' do + request = stub_request('HTTP_X_FORWARDED_FOR' => '1.2.3.6, 1.1.1.1, 10.1.5.89, 127.0.0.1') + expect(request.remote_ip).to eq('1.1.1.1') + end + end + + context 'with proxy IP added' do + before do + set_trusted_proxies([ "60.98.25.47" ]) + end + + it 'filters out proxy IP from remote_ip' do + request = stub_request('HTTP_X_FORWARDED_FOR' => '1.2.3.6, 1.1.1.1, 60.98.25.47, 127.0.0.1') + expect(request.remote_ip).to eq('1.1.1.1') + end + end + + def stub_request(headers = {}) + ActionDispatch::RemoteIp.new(Proc.new { }, false, Rails.application.config.action_dispatch.trusted_proxies).call(headers) + ActionDispatch::Request.new(headers) + end + + def set_trusted_proxies(proxies = []) + stub_config_setting('trusted_proxies' => proxies) + load File.join(__dir__, '../../config/initializers/trusted_proxies.rb') + end +end diff --git a/spec/javascripts/merge_request_widget_spec.js.coffee b/spec/javascripts/merge_request_widget_spec.js.coffee new file mode 100644 index 00000000000..92b7eeb1116 --- /dev/null +++ b/spec/javascripts/merge_request_widget_spec.js.coffee @@ -0,0 +1,55 @@ +#= require merge_request_widget + +describe 'MergeRequestWidget', -> + + beforeEach -> + window.notifyPermissions = () -> + window.notify = () -> + @opts = { + ci_status_url:"http://sampledomain.local/ci/getstatus", + ci_status:"", + ci_message: { + normal: "Build {{status}} for \"{{title}}\"", + preparing: "{{status}} build for \"{{title}}\"" + }, + ci_title: { + preparing: "{{status}} build", + normal: "Build {{status}}" + }, + gitlab_icon:"gitlab_logo.png", + builds_path:"http://sampledomain.local/sampleBuildsPath" + } + @class = new MergeRequestWidget(@opts) + @ciStatusData = {"title":"Sample MR title","sha":"12a34bc5","status":"success","coverage":98} + + describe 'getCIStatus', -> + beforeEach -> + spyOn(jQuery, 'getJSON').and.callFake (req, cb) => + cb(@ciStatusData) + + it 'should call showCIStatus even if a notification should not be displayed', -> + spy = spyOn(@class, 'showCIStatus').and.stub() + @class.getCIStatus(false) + expect(spy).toHaveBeenCalledWith(@ciStatusData.status) + + it 'should call showCIStatus when a notification should be displayed', -> + spy = spyOn(@class, 'showCIStatus').and.stub() + @class.getCIStatus(true) + expect(spy).toHaveBeenCalledWith(@ciStatusData.status) + + it 'should call showCICoverage when the coverage rate is set', -> + spy = spyOn(@class, 'showCICoverage').and.stub() + @class.getCIStatus(false) + expect(spy).toHaveBeenCalledWith(@ciStatusData.coverage) + + it 'should not call showCICoverage when the coverage rate is not set', -> + @ciStatusData.coverage = null + spy = spyOn(@class, 'showCICoverage').and.stub() + @class.getCIStatus(false) + expect(spy).not.toHaveBeenCalled() + + it 'should not display a notification on the first check after the widget has been created', -> + spy = spyOn(window, 'notify') + @class = new MergeRequestWidget(@opts) + @class.getCIStatus(true) + expect(spy).not.toHaveBeenCalled() diff --git a/spec/lib/banzai/filter/external_link_filter_spec.rb b/spec/lib/banzai/filter/external_link_filter_spec.rb index e3a8e15330e..f4c5c621bd0 100644 --- a/spec/lib/banzai/filter/external_link_filter_spec.rb +++ b/spec/lib/banzai/filter/external_link_filter_spec.rb @@ -24,6 +24,14 @@ describe Banzai::Filter::ExternalLinkFilter, lib: true do doc = filter(act) expect(doc.at_css('a')).to have_attribute('rel') - expect(doc.at_css('a')['rel']).to eq 'nofollow' + expect(doc.at_css('a')['rel']).to include 'nofollow' + end + + it 'adds rel="noreferrer" to external links' do + act = %q(<a href="https://google.com/">Google</a>) + doc = filter(act) + + expect(doc.at_css('a')).to have_attribute('rel') + expect(doc.at_css('a')['rel']).to include 'noreferrer' end end diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb index 94468abcbb3..b0a38e7c251 100644 --- a/spec/lib/banzai/filter/label_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb @@ -178,27 +178,37 @@ describe Banzai::Filter::LabelReferenceFilter, lib: true do end describe 'cross project label references' do - let(:another_project) { create(:empty_project, :public) } - let(:project_name) { another_project.name_with_namespace } - let(:label) { create(:label, project: another_project, color: '#00ff00') } - let(:reference) { label.to_reference(project) } + context 'valid project referenced' do + let(:another_project) { create(:empty_project, :public) } + let(:project_name) { another_project.name_with_namespace } + let(:label) { create(:label, project: another_project, color: '#00ff00') } + let(:reference) { label.to_reference(project) } - let!(:result) { reference_filter("See #{reference}") } + let!(:result) { reference_filter("See #{reference}") } - it 'points to referenced project issues page' do - expect(result.css('a').first.attr('href')) - .to eq urls.namespace_project_issues_url(another_project.namespace, - another_project, - label_name: label.name) - end + it 'points to referenced project issues page' do + expect(result.css('a').first.attr('href')) + .to eq urls.namespace_project_issues_url(another_project.namespace, + another_project, + label_name: label.name) + end + + it 'has valid color' do + expect(result.css('a span').first.attr('style')) + .to match /background-color: #00ff00/ + end - it 'has valid color' do - expect(result.css('a span').first.attr('style')) - .to match /background-color: #00ff00/ + it 'contains cross project content' do + expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}" + end end - it 'contains cross project content' do - expect(result.css('a').first.text).to eq "#{label.name} in #{project_name}" + context 'project that does not exist referenced' do + let(:result) { reference_filter('aaa/bbb~ccc') } + + it 'does not link reference' do + expect(result.to_html).to eq 'aaa/bbb~ccc' + end end 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 ebf3d7489b5..5beb61dac5c 100644 --- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb +++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb @@ -43,7 +43,7 @@ describe Banzai::Filter::MilestoneReferenceFilter, lib: true do milestone.update_attribute(:title, %{"></a>whatever<a title="}) doc = reference_filter("milestone #{reference}") - expect(doc.text).to eq "milestone #{milestone.title}" + expect(doc.text).to eq "milestone \">whatever" end it 'includes default classes' do diff --git a/spec/lib/banzai/filter/sanitization_filter_spec.rb b/spec/lib/banzai/filter/sanitization_filter_spec.rb index 27ce312b11c..b38e3b17e64 100644 --- a/spec/lib/banzai/filter/sanitization_filter_spec.rb +++ b/spec/lib/banzai/filter/sanitization_filter_spec.rb @@ -22,6 +22,12 @@ describe Banzai::Filter::SanitizationFilter, lib: true do expect(filter(act).to_html).to eq exp end + it 'sanitizes mixed-cased javascript in attributes' do + act = %q(<a href="javaScript:alert('foo')">Text</a>) + exp = '<a>Text</a>' + expect(filter(act).to_html).to eq exp + end + it 'allows whitelisted HTML tags from the user' do exp = act = "<dl>\n<dt>Term</dt>\n<dd>Definition</dd>\n</dl>" expect(filter(act).to_html).to eq exp diff --git a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb index dcb8a3451bd..c7ab3185378 100644 --- a/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb +++ b/spec/lib/ci/gitlab_ci_yaml_processor_spec.rb @@ -286,6 +286,81 @@ module Ci end end + + describe "Scripts handling" do + let(:config_data) { YAML.dump(config) } + let(:config_processor) { GitlabCiYamlProcessor.new(config_data, path) } + + subject { config_processor.builds_for_stage_and_ref("test", "master").first } + + describe "before_script" do + context "in global context" do + let(:config) do + { + before_script: ["global script"], + test: { script: ["script"] } + } + end + + it "return commands with scripts concencaced" do + expect(subject[:commands]).to eq("global script\nscript") + end + end + + context "overwritten in local context" do + let(:config) do + { + before_script: ["global script"], + test: { before_script: ["local script"], script: ["script"] } + } + end + + it "return commands with scripts concencaced" do + expect(subject[:commands]).to eq("local script\nscript") + end + end + end + + describe "script" do + let(:config) do + { + test: { script: ["script"] } + } + end + + it "return commands with scripts concencaced" do + expect(subject[:commands]).to eq("script") + end + end + + describe "after_script" do + context "in global context" do + let(:config) do + { + after_script: ["after_script"], + test: { script: ["script"] } + } + end + + it "return after_script in options" do + expect(subject[:options][:after_script]).to eq(["after_script"]) + end + end + + context "overwritten in local context" do + let(:config) do + { + after_script: ["local after_script"], + test: { after_script: ["local after_script"], script: ["script"] } + } + end + + it "return after_script in options" do + expect(subject[:options][:after_script]).to eq(["local after_script"]) + end + end + end + end describe "Image and service handling" do it "returns image and service when defined" do @@ -345,20 +420,76 @@ module Ci end end - describe "Variables" do - it "returns variables when defined" do - variables = { - var1: "value1", - var2: "value2", - } - config = YAML.dump({ - variables: variables, - before_script: ["pwd"], - rspec: { script: "rspec" } - }) + describe 'Variables' do + context 'when global variables are defined' do + it 'returns global variables' do + variables = { + VAR1: 'value1', + VAR2: 'value2', + } - config_processor = GitlabCiYamlProcessor.new(config, path) - expect(config_processor.variables).to eq(variables) + config = YAML.dump({ + variables: variables, + before_script: ['pwd'], + rspec: { script: 'rspec' } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.global_variables).to eq(variables) + end + end + + context 'when job variables are defined' do + context 'when syntax is correct' do + it 'returns job variables' do + variables = { + KEY1: 'value1', + SOME_KEY_2: 'value2' + } + + config = YAML.dump( + { before_script: ['pwd'], + rspec: { + variables: variables, + script: 'rspec' } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.job_variables(:rspec)).to eq variables + end + end + + context 'when syntax is incorrect' do + it 'raises error' do + variables = [:KEY1, 'value1', :KEY2, 'value2'] + + config = YAML.dump( + { before_script: ['pwd'], + rspec: { + variables: variables, + script: 'rspec' } + }) + + expect { GitlabCiYamlProcessor.new(config, path) } + .to raise_error(GitlabCiYamlProcessor::ValidationError, + /job: variables should be a map/) + end + end + end + + context 'when job variables are not defined' do + it 'returns empty array' do + config = YAML.dump({ + before_script: ['pwd'], + rspec: { script: 'rspec' } + }) + + config_processor = GitlabCiYamlProcessor.new(config, path) + + expect(config_processor.job_variables(:rspec)).to eq [] + end end end @@ -517,70 +648,131 @@ module Ci end describe "Hidden jobs" do - let(:config) do - YAML.dump({ - '.hidden_job' => { script: 'test' }, - 'normal_job' => { script: 'test' } - }) + let(:config_processor) { GitlabCiYamlProcessor.new(config) } + subject { config_processor.builds_for_stage_and_ref("test", "master") } + + shared_examples 'hidden_job_handling' do + it "doesn't create jobs that start with dot" do + expect(subject.size).to eq(1) + expect(subject.first).to eq({ + except: nil, + stage: "test", + stage_idx: 1, + name: :normal_job, + only: nil, + commands: "test", + tag_list: [], + options: {}, + when: "on_success", + allow_failure: false + }) + end end - let(:config_processor) { GitlabCiYamlProcessor.new(config) } + context 'when hidden job have a script definition' do + let(:config) do + YAML.dump({ + '.hidden_job' => { image: 'ruby:2.1', script: 'test' }, + 'normal_job' => { script: 'test' } + }) + end - subject { config_processor.builds_for_stage_and_ref("test", "master") } + it_behaves_like 'hidden_job_handling' + end - it "doesn't create jobs that starts with dot" do - expect(subject.size).to eq(1) - expect(subject.first).to eq({ - except: nil, - stage: "test", - stage_idx: 1, - name: :normal_job, - only: nil, - commands: "\ntest", - tag_list: [], - options: {}, - when: "on_success", - allow_failure: false - }) + context "when hidden job doesn't have a script definition" do + let(:config) do + YAML.dump({ + '.hidden_job' => { image: 'ruby:2.1' }, + 'normal_job' => { script: 'test' } + }) + end + + it_behaves_like 'hidden_job_handling' end end describe "YAML Alias/Anchor" do - it "is correctly supported for jobs" do - config = <<EOT + let(:config_processor) { GitlabCiYamlProcessor.new(config) } + subject { config_processor.builds_for_stage_and_ref("build", "master") } + + shared_examples 'job_templates_handling' do + it "is correctly supported for jobs" do + expect(subject.size).to eq(2) + expect(subject.first).to eq({ + except: nil, + stage: "build", + stage_idx: 0, + name: :job1, + only: nil, + commands: "execute-script-for-job", + tag_list: [], + options: {}, + when: "on_success", + allow_failure: false + }) + expect(subject.second).to eq({ + except: nil, + stage: "build", + stage_idx: 0, + name: :job2, + only: nil, + commands: "execute-script-for-job", + tag_list: [], + options: {}, + when: "on_success", + allow_failure: false + }) + end + end + + context 'when template is a job' do + let(:config) do + <<EOT job1: &JOBTMPL + stage: build script: execute-script-for-job job2: *JOBTMPL EOT + end - config_processor = GitlabCiYamlProcessor.new(config) + it_behaves_like 'job_templates_handling' + end - expect(config_processor.builds_for_stage_and_ref("test", "master").size).to eq(2) - expect(config_processor.builds_for_stage_and_ref("test", "master").first).to eq({ - except: nil, - stage: "test", - stage_idx: 1, - name: :job1, - only: nil, - commands: "\nexecute-script-for-job", - tag_list: [], - options: {}, - when: "on_success", - allow_failure: false - }) - expect(config_processor.builds_for_stage_and_ref("test", "master").second).to eq({ - except: nil, - stage: "test", - stage_idx: 1, - name: :job2, - only: nil, - commands: "\nexecute-script-for-job", - tag_list: [], - options: {}, - when: "on_success", - allow_failure: false - }) + context 'when template is a hidden job' do + let(:config) do + <<EOT +.template: &JOBTMPL + stage: build + script: execute-script-for-job + +job1: *JOBTMPL + +job2: *JOBTMPL +EOT + end + + it_behaves_like 'job_templates_handling' + end + + context 'when job adds its own keys to a template definition' do + let(:config) do + <<EOT +.template: &JOBTMPL + stage: build + +job1: + <<: *JOBTMPL + script: execute-script-for-job + +job2: + <<: *JOBTMPL + script: execute-script-for-job +EOT + end + + it_behaves_like 'job_templates_handling' end end @@ -607,6 +799,27 @@ EOT end.to raise_error(GitlabCiYamlProcessor::ValidationError, "before_script should be an array of strings") end + it "returns errors if job before_script parameter is not an array of strings" do + config = YAML.dump({ rspec: { script: "test", before_script: [10, "test"] } }) + expect do + GitlabCiYamlProcessor.new(config, path) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: before_script should be an array of strings") + end + + it "returns errors if after_script parameter is invalid" do + config = YAML.dump({ after_script: "bundle update", rspec: { script: "test" } }) + expect do + GitlabCiYamlProcessor.new(config, path) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "after_script should be an array of strings") + end + + it "returns errors if job after_script parameter is not an array of strings" do + config = YAML.dump({ rspec: { script: "test", after_script: [10, "test"] } }) + expect do + GitlabCiYamlProcessor.new(config, path) + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "rspec job: after_script should be an array of strings") + end + it "returns errors if image parameter is invalid" do config = YAML.dump({ image: ["test"], rspec: { script: "test" } }) expect do @@ -730,14 +943,14 @@ EOT config = YAML.dump({ variables: "test", rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings") end - it "returns errors if variables is not a map of key-valued strings" do + it "returns errors if variables is not a map of key-value strings" do config = YAML.dump({ variables: { test: false }, rspec: { script: "test" } }) expect do GitlabCiYamlProcessor.new(config, path) - end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-valued strings") + end.to raise_error(GitlabCiYamlProcessor::ValidationError, "variables should be a map of key-value strings") end it "returns errors if job when is not on_success, on_failure or always" do diff --git a/spec/lib/gitlab/akismet_helper_spec.rb b/spec/lib/gitlab/akismet_helper_spec.rb index 9858935180a..53f5d6c5c80 100644 --- a/spec/lib/gitlab/akismet_helper_spec.rb +++ b/spec/lib/gitlab/akismet_helper_spec.rb @@ -24,7 +24,7 @@ describe Gitlab::AkismetHelper, type: :helper do describe '#is_spam?' do it 'returns true for spam' do environment = { - 'REMOTE_ADDR' => '127.0.0.1', + 'action_dispatch.remote_ip' => '127.0.0.1', 'HTTP_USER_AGENT' => 'Test User Agent' } diff --git a/spec/lib/gitlab/badge/build_spec.rb b/spec/lib/gitlab/badge/build_spec.rb index 329792bb685..b6f7a2e7ec4 100644 --- a/spec/lib/gitlab/badge/build_spec.rb +++ b/spec/lib/gitlab/badge/build_spec.rb @@ -42,7 +42,7 @@ describe Gitlab::Badge::Build do end context 'build exists' do - let(:ci_commit) { create(:ci_commit, project: project, sha: sha) } + let(:ci_commit) { create(:ci_commit, project: project, sha: sha, ref: branch) } let!(:build) { create(:ci_build, commit: ci_commit) } @@ -57,7 +57,7 @@ describe Gitlab::Badge::Build do describe '#data' do let(:data) { badge.data } - it 'contains infromation about success' do + it 'contains information about success' do expect(status_node(data, 'success')).to be_truthy end end @@ -74,7 +74,7 @@ describe Gitlab::Badge::Build do describe '#data' do let(:data) { badge.data } - it 'contains infromation about failure' do + it 'contains information about failure' do expect(status_node(data, 'failed')).to be_truthy end end diff --git a/spec/lib/gitlab/bitbucket_import/client_spec.rb b/spec/lib/gitlab/bitbucket_import/client_spec.rb index aa0699f2ebf..af839f42421 100644 --- a/spec/lib/gitlab/bitbucket_import/client_spec.rb +++ b/spec/lib/gitlab/bitbucket_import/client_spec.rb @@ -34,18 +34,32 @@ describe Gitlab::BitbucketImport::Client, lib: true do it 'retrieves issues over a number of pages' do stub_request(:get, "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=0"). - to_return(status: 200, - body: first_sample_data.to_json, - headers: {}) + to_return(status: 200, + body: first_sample_data.to_json, + headers: {}) stub_request(:get, "https://bitbucket.org/api/1.0/repositories/#{project_id}/issues?limit=50&sort=utc_created_on&start=50"). - to_return(status: 200, - body: second_sample_data.to_json, - headers: {}) + to_return(status: 200, + body: second_sample_data.to_json, + headers: {}) issues = client.issues(project_id) expect(issues.count).to eq(95) end end + + context 'project import' do + it 'calls .from_project with no errors' do + project = create(:empty_project) + project.create_or_update_import_data(credentials: + { user: "git", + password: nil, + bb_session: { bitbucket_access_token: "test", + bitbucket_access_token_secret: "test" } }) + project.import_url = "ssh://git@bitbucket.org/test/test.git" + + expect { described_class.from_project(project) }.to_not raise_error + end + end end diff --git a/spec/lib/gitlab/github_import/client_spec.rb b/spec/lib/gitlab/github_import/client_spec.rb index 49d8cdf4314..7c21cbe96d9 100644 --- a/spec/lib/gitlab/github_import/client_spec.rb +++ b/spec/lib/gitlab/github_import/client_spec.rb @@ -2,15 +2,49 @@ require 'spec_helper' describe Gitlab::GithubImport::Client, lib: true do let(:token) { '123456' } - let(:client) { Gitlab::GithubImport::Client.new(token) } + let(:github_provider) { Settingslogic.new('app_id' => 'asd123', 'app_secret' => 'asd123', 'name' => 'github', 'args' => { 'client_options' => {} }) } + + subject(:client) { described_class.new(token) } before do - Gitlab.config.omniauth.providers << OpenStruct.new(app_id: "asd123", app_secret: "asd123", name: "github") + allow(Gitlab.config.omniauth).to receive(:providers).and_return([github_provider]) end - it 'all OAuth2 client options are symbols' do + it 'convert OAuth2 client options to symbols' do client.client.options.keys.each do |key| expect(key).to be_kind_of(Symbol) end end + + it 'does not crash (e.g. Settingslogic::MissingSetting) when verify_ssl config is not present' do + expect { client.api }.not_to raise_error + end + + context 'allow SSL verification to be configurable on API' do + before do + github_provider['verify_ssl'] = false + end + + it 'uses supplied value' do + expect(client.client.options[:connection_opts][:ssl]).to eq({ verify: false }) + expect(client.api.connection_options[:ssl]).to eq({ verify: false }) + end + end + + context 'when provider does not specity an API endpoint' do + it 'uses GitHub root API endpoint' do + expect(client.api.api_endpoint).to eq 'https://api.github.com/' + end + end + + context 'when provider specify a custom API endpoint' do + before do + github_provider['args']['client_options']['site'] = 'https://github.company.com/' + end + + it 'uses the custom API endpoint' do + expect(OmniAuth::Strategies::GitHub).not_to receive(:default_options) + expect(client.api.api_endpoint).to eq 'https://github.company.com/' + end + end end diff --git a/spec/lib/gitlab/github_import/comment_formatter_spec.rb b/spec/lib/gitlab/github_import/comment_formatter_spec.rb index a324a82e69f..55e86d4ceac 100644 --- a/spec/lib/gitlab/github_import/comment_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/comment_formatter_spec.rb @@ -2,23 +2,25 @@ require 'spec_helper' describe Gitlab::GithubImport::CommentFormatter, lib: true do let(:project) { create(:project) } - let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:octocat) { double(id: 123456, login: 'octocat') } let(:created_at) { DateTime.strptime('2013-04-10T20:09:31Z') } let(:updated_at) { DateTime.strptime('2014-03-03T18:58:10Z') } - let(:base_data) do + let(:base) do { body: "I'm having a problem with this.", user: octocat, + commit_id: nil, + diff_hunk: nil, created_at: created_at, updated_at: updated_at } end - subject(:comment) { described_class.new(project, raw_data)} + subject(:comment) { described_class.new(project, raw)} describe '#attributes' do context 'when do not reference a portion of the diff' do - let(:raw_data) { OpenStruct.new(base_data) } + let(:raw) { double(base) } it 'returns formatted attributes' do expected = { @@ -36,24 +38,23 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do end context 'when on a portion of the diff' do - let(:diff_data) do + let(:diff) do { body: 'Great stuff', commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', - diff_hunk: '@@ -16,33 +16,40 @@ public class Connection : IConnection...', - path: 'file1.txt', - position: 1 + diff_hunk: "@@ -1,5 +1,9 @@\n class User\n def name\n- 'John Doe'\n+ 'Jane Doe'", + path: 'file1.txt' } end - let(:raw_data) { OpenStruct.new(base_data.merge(diff_data)) } + let(:raw) { double(base.merge(diff)) } it 'returns formatted attributes' do expected = { project: project, note: "*Created by: octocat*\n\nGreat stuff", commit_id: '6dcb09b5b57875f334f61aebed695e2e4193db5e', - line_code: 'ce1be0ff4065a6e9415095c95f25f47a633cef2b_0_1', + line_code: 'ce1be0ff4065a6e9415095c95f25f47a633cef2b_4_3', author_id: project.creator_id, created_at: created_at, updated_at: updated_at @@ -64,15 +65,10 @@ describe Gitlab::GithubImport::CommentFormatter, lib: true do end context 'when author is a GitLab user' do - let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + let(:raw) { double(base.merge(user: octocat)) } - it 'returns project#creator_id as author_id when is not a GitLab user' do - expect(comment.attributes.fetch(:author_id)).to eq project.creator_id - end - - it 'returns GitLab user id as author_id when is a GitLab user' do + it 'returns GitLab user id as author_id' do gl_user = create(:omniauth_user, extern_uid: octocat.id, provider: 'github') - expect(comment.attributes.fetch(:author_id)).to eq gl_user.id end end diff --git a/spec/lib/gitlab/github_import/issue_formatter_spec.rb b/spec/lib/gitlab/github_import/issue_formatter_spec.rb index fd05428b322..0e7ffbe9b8e 100644 --- a/spec/lib/gitlab/github_import/issue_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/issue_formatter_spec.rb @@ -2,13 +2,14 @@ require 'spec_helper' describe Gitlab::GithubImport::IssueFormatter, lib: true do let!(:project) { create(:project, namespace: create(:namespace, path: 'octocat')) } - let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + 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') } let(:base_data) do { number: 1347, + milestone: nil, state: 'open', title: 'Found a bug', body: "I'm having a problem with this.", @@ -26,11 +27,13 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do describe '#attributes' do context 'when issue is open' do - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } + let(:raw_data) { double(base_data.merge(state: 'open')) } it 'returns formatted attributes' do expected = { + iid: 1347, project: project, + milestone: nil, title: 'Found a bug', description: "*Created by: octocat*\n\nI'm having a problem with this.", state: 'opened', @@ -46,11 +49,13 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do context 'when issue is closed' do let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } it 'returns formatted attributes' do expected = { + iid: 1347, project: project, + milestone: nil, title: 'Found a bug', description: "*Created by: octocat*\n\nI'm having a problem with this.", state: 'closed', @@ -65,7 +70,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when it is assigned to someone' do - let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } + let(:raw_data) { double(base_data.merge(assignee: octocat)) } it 'returns nil as assignee_id when is not a GitLab user' do expect(issue.attributes.fetch(:assignee_id)).to be_nil @@ -78,8 +83,23 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end end + context 'when it has a milestone' do + let(:milestone) { double(number: 45) } + let(:raw_data) { double(base_data.merge(milestone: milestone)) } + + it 'returns nil when milestone does not exist' do + expect(issue.attributes.fetch(:milestone)).to be_nil + end + + it 'returns milestone when it exists' do + milestone = create(:milestone, project: project, iid: 45) + + expect(issue.attributes.fetch(:milestone)).to eq milestone + end + end + context 'when author is a GitLab user' do - let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + let(:raw_data) { double(base_data.merge(user: octocat)) } it 'returns project#creator_id as author_id when is not a GitLab user' do expect(issue.attributes.fetch(:author_id)).to eq project.creator_id @@ -95,7 +115,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do describe '#has_comments?' do context 'when number of comments is greater than zero' do - let(:raw_data) { OpenStruct.new(base_data.merge(comments: 1)) } + let(:raw_data) { double(base_data.merge(comments: 1)) } it 'returns true' do expect(issue.has_comments?).to eq true @@ -103,7 +123,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when number of comments is equal to zero' do - let(:raw_data) { OpenStruct.new(base_data.merge(comments: 0)) } + let(:raw_data) { double(base_data.merge(comments: 0)) } it 'returns false' do expect(issue.has_comments?).to eq false @@ -112,7 +132,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end describe '#number' do - let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } + let(:raw_data) { double(base_data.merge(number: 1347)) } it 'returns pull request number' do expect(issue.number).to eq 1347 @@ -121,7 +141,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do describe '#valid?' do context 'when mention a pull request' do - let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: OpenStruct.new)) } + let(:raw_data) { double(base_data.merge(pull_request: double)) } it 'returns false' do expect(issue.valid?).to eq false @@ -129,7 +149,7 @@ describe Gitlab::GithubImport::IssueFormatter, lib: true do end context 'when does not mention a pull request' do - let(:raw_data) { OpenStruct.new(base_data.merge(pull_request: nil)) } + let(:raw_data) { double(base_data.merge(pull_request: nil)) } it 'returns true' do expect(issue.valid?).to eq true diff --git a/spec/lib/gitlab/github_import/label_formatter_spec.rb b/spec/lib/gitlab/github_import/label_formatter_spec.rb new file mode 100644 index 00000000000..e94440a7fb0 --- /dev/null +++ b/spec/lib/gitlab/github_import/label_formatter_spec.rb @@ -0,0 +1,19 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::LabelFormatter, lib: true do + + describe '#attributes' do + it 'returns formatted attributes' do + project = create(:project) + raw = double(name: 'improvements', color: 'e6e6e6') + + formatter = described_class.new(project, raw) + + expect(formatter.attributes).to eq({ + project: project, + title: 'improvements', + color: '#e6e6e6' + }) + end + end +end diff --git a/spec/lib/gitlab/github_import/milestone_formatter_spec.rb b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb new file mode 100644 index 00000000000..5a421e50581 --- /dev/null +++ b/spec/lib/gitlab/github_import/milestone_formatter_spec.rb @@ -0,0 +1,82 @@ +require 'spec_helper' + +describe Gitlab::GithubImport::MilestoneFormatter, lib: true do + let(:project) { create(:empty_project) } + let(:created_at) { DateTime.strptime('2011-01-26T19:01:12Z') } + let(:updated_at) { DateTime.strptime('2011-01-27T19:01:12Z') } + let(:base_data) do + { + number: 1347, + state: 'open', + title: '1.0', + description: 'Version 1.0', + due_on: nil, + created_at: created_at, + updated_at: updated_at, + closed_at: nil + } + end + + subject(:formatter) { described_class.new(project, raw_data)} + + describe '#attributes' do + context 'when milestone is open' do + let(:raw_data) { double(base_data.merge(state: 'open')) } + + it 'returns formatted attributes' do + expected = { + iid: 1347, + project: project, + title: '1.0', + description: 'Version 1.0', + state: 'active', + due_date: nil, + created_at: created_at, + updated_at: updated_at + } + + expect(formatter.attributes).to eq(expected) + end + end + + context 'when milestone is closed' do + let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } + let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } + + it 'returns formatted attributes' do + expected = { + iid: 1347, + project: project, + title: '1.0', + description: 'Version 1.0', + state: 'closed', + due_date: nil, + created_at: created_at, + updated_at: closed_at + } + + expect(formatter.attributes).to eq(expected) + end + end + + context 'when milestone has a due date' do + let(:due_date) { DateTime.strptime('2011-01-28T19:01:12Z') } + let(:raw_data) { double(base_data.merge(due_on: due_date)) } + + it 'returns formatted attributes' do + expected = { + iid: 1347, + project: project, + title: '1.0', + description: 'Version 1.0', + state: 'active', + due_date: due_date, + created_at: created_at, + updated_at: updated_at + } + + expect(formatter.attributes).to eq(expected) + end + 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 e49dcb42342..e59c0ca110e 100644 --- a/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb +++ b/spec/lib/gitlab/github_import/pull_request_formatter_spec.rb @@ -2,17 +2,18 @@ require 'spec_helper' describe Gitlab::GithubImport::PullRequestFormatter, lib: true do let(:project) { create(:project) } - let(:repository) { OpenStruct.new(id: 1, fork: false) } + let(:repository) { double(id: 1, fork: false) } let(:source_repo) { repository } - let(:source_branch) { OpenStruct.new(ref: 'feature', repo: source_repo) } + let(:source_branch) { double(ref: 'feature', repo: source_repo) } let(:target_repo) { repository } - let(:target_branch) { OpenStruct.new(ref: 'master', repo: target_repo) } - let(:octocat) { OpenStruct.new(id: 123456, login: 'octocat') } + let(:target_branch) { double(ref: 'master', repo: target_repo) } + 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') } let(:base_data) do { number: 1347, + milestone: nil, state: 'open', title: 'New feature', body: 'Please pull these awesome changes', @@ -31,10 +32,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do describe '#attributes' do context 'when pull request is open' do - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'open')) } + let(:raw_data) { double(base_data.merge(state: 'open')) } it 'returns formatted attributes' do expected = { + iid: 1347, title: 'New feature', description: "*Created by: octocat*\n\nPlease pull these awesome changes", source_project: project, @@ -42,6 +44,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do target_project: project, target_branch: 'master', state: 'opened', + milestone: nil, author_id: project.creator_id, assignee_id: nil, created_at: created_at, @@ -54,10 +57,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do context 'when pull request is closed' do let(:closed_at) { DateTime.strptime('2011-01-28T19:01:12Z') } - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', closed_at: closed_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed', closed_at: closed_at)) } it 'returns formatted attributes' do expected = { + iid: 1347, title: 'New feature', description: "*Created by: octocat*\n\nPlease pull these awesome changes", source_project: project, @@ -65,6 +69,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do target_project: project, target_branch: 'master', state: 'closed', + milestone: nil, author_id: project.creator_id, assignee_id: nil, created_at: created_at, @@ -77,10 +82,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do context 'when pull request is merged' do let(:merged_at) { DateTime.strptime('2011-01-28T13:01:12Z') } - let(:raw_data) { OpenStruct.new(base_data.merge(state: 'closed', merged_at: merged_at)) } + let(:raw_data) { double(base_data.merge(state: 'closed', merged_at: merged_at)) } it 'returns formatted attributes' do expected = { + iid: 1347, title: 'New feature', description: "*Created by: octocat*\n\nPlease pull these awesome changes", source_project: project, @@ -88,6 +94,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do target_project: project, target_branch: 'master', state: 'merged', + milestone: nil, author_id: project.creator_id, assignee_id: nil, created_at: created_at, @@ -99,7 +106,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when it is assigned to someone' do - let(:raw_data) { OpenStruct.new(base_data.merge(assignee: octocat)) } + let(:raw_data) { double(base_data.merge(assignee: octocat)) } it 'returns nil as assignee_id when is not a GitLab user' do expect(pull_request.attributes.fetch(:assignee_id)).to be_nil @@ -113,7 +120,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when author is a GitLab user' do - let(:raw_data) { OpenStruct.new(base_data.merge(user: octocat)) } + let(:raw_data) { double(base_data.merge(user: octocat)) } it 'returns project#creator_id as author_id when is not a GitLab user' do expect(pull_request.attributes.fetch(:author_id)).to eq project.creator_id @@ -125,10 +132,25 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do expect(pull_request.attributes.fetch(:author_id)).to eq gl_user.id end end + + context 'when it has a milestone' do + let(:milestone) { double(number: 45) } + let(:raw_data) { double(base_data.merge(milestone: milestone)) } + + it 'returns nil when milestone does not exists' do + expect(pull_request.attributes.fetch(:milestone)).to be_nil + end + + it 'returns milestone when is exists' do + milestone = create(:milestone, project: project, iid: 45) + + expect(pull_request.attributes.fetch(:milestone)).to eq milestone + end + end end describe '#number' do - let(:raw_data) { OpenStruct.new(base_data.merge(number: 1347)) } + let(:raw_data) { double(base_data.merge(number: 1347)) } it 'returns pull request number' do expect(pull_request.number).to eq 1347 @@ -136,11 +158,11 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end describe '#valid?' do - let(:invalid_branch) { OpenStruct.new(ref: 'invalid-branch') } + 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) { OpenStruct.new(base_data.merge(head: source_branch, base: target_branch)) } + let(:raw_data) { double(base_data.merge(head: source_branch, base: target_branch)) } it 'returns true' do expect(pull_request.valid?).to eq true @@ -148,7 +170,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'and source branch doesn not exists' do - let(:raw_data) { OpenStruct.new(base_data.merge(head: invalid_branch, base: target_branch)) } + let(:raw_data) { double(base_data.merge(head: invalid_branch, base: target_branch)) } it 'returns false' do expect(pull_request.valid?).to eq false @@ -156,7 +178,7 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'and target branch doesn not exists' do - let(:raw_data) { OpenStruct.new(base_data.merge(head: source_branch, base: invalid_branch)) } + let(:raw_data) { double(base_data.merge(head: source_branch, base: invalid_branch)) } it 'returns false' do expect(pull_request.valid?).to eq false @@ -165,8 +187,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when source repo is a fork' do - let(:source_repo) { OpenStruct.new(id: 2, fork: true) } - let(:raw_data) { OpenStruct.new(base_data) } + let(:source_repo) { double(id: 2, fork: true) } + let(:raw_data) { double(base_data) } it 'returns false' do expect(pull_request.valid?).to eq false @@ -174,8 +196,8 @@ describe Gitlab::GithubImport::PullRequestFormatter, lib: true do end context 'when target repo is a fork' do - let(:target_repo) { OpenStruct.new(id: 2, fork: true) } - let(:raw_data) { OpenStruct.new(base_data) } + let(:target_repo) { double(id: 2, fork: true) } + let(:raw_data) { double(base_data) } it 'returns false' do expect(pull_request.valid?).to eq false diff --git a/spec/lib/gitlab/metrics/instrumentation_spec.rb b/spec/lib/gitlab/metrics/instrumentation_spec.rb index ad4290c43bb..5c885a7a982 100644 --- a/spec/lib/gitlab/metrics/instrumentation_spec.rb +++ b/spec/lib/gitlab/metrics/instrumentation_spec.rb @@ -33,8 +33,16 @@ describe Gitlab::Metrics::Instrumentation do described_class.instrument_method(@dummy, :foo) end - it 'renames the original method' do - expect(@dummy).to respond_to(:_original_foo) + it 'instruments the Class' do + target = @dummy.singleton_class + + expect(described_class.instrumented?(target)).to eq(true) + end + + it 'defines a proxy method' do + mod = described_class.proxy_module(@dummy.singleton_class) + + expect(mod.method_defined?(:foo)).to eq(true) end it 'calls the instrumented method with the correct arguments' do @@ -76,6 +84,14 @@ describe Gitlab::Metrics::Instrumentation do expect(dummy.method(:test).arity).to eq(0) end + + describe 'when a module is instrumented multiple times' do + it 'calls the instrumented method with the correct arguments' do + described_class.instrument_method(@dummy, :foo) + + expect(@dummy.foo).to eq('foo') + end + end end describe 'with metrics disabled' do @@ -86,7 +102,9 @@ describe Gitlab::Metrics::Instrumentation do it 'does not instrument the method' do described_class.instrument_method(@dummy, :foo) - expect(@dummy).to_not respond_to(:_original_foo) + target = @dummy.singleton_class + + expect(described_class.instrumented?(target)).to eq(false) end end end @@ -100,8 +118,14 @@ describe Gitlab::Metrics::Instrumentation do instrument_instance_method(@dummy, :bar) end - it 'renames the original method' do - expect(@dummy.method_defined?(:_original_bar)).to eq(true) + it 'instruments instances of the Class' do + expect(described_class.instrumented?(@dummy)).to eq(true) + end + + it 'defines a proxy method' do + mod = described_class.proxy_module(@dummy) + + expect(mod.method_defined?(:bar)).to eq(true) end it 'calls the instrumented method with the correct arguments' do @@ -144,7 +168,7 @@ describe Gitlab::Metrics::Instrumentation do described_class. instrument_instance_method(@dummy, :bar) - expect(@dummy.method_defined?(:_original_bar)).to eq(false) + expect(described_class.instrumented?(@dummy)).to eq(false) end end end @@ -167,18 +191,17 @@ describe Gitlab::Metrics::Instrumentation do it 'recursively instruments a class hierarchy' do described_class.instrument_class_hierarchy(@dummy) - expect(@child1).to respond_to(:_original_child1_foo) - expect(@child2).to respond_to(:_original_child2_foo) + expect(described_class.instrumented?(@child1.singleton_class)).to eq(true) + expect(described_class.instrumented?(@child2.singleton_class)).to eq(true) - expect(@child1.method_defined?(:_original_child1_bar)).to eq(true) - expect(@child2.method_defined?(:_original_child2_bar)).to eq(true) + expect(described_class.instrumented?(@child1)).to eq(true) + expect(described_class.instrumented?(@child2)).to eq(true) end it 'does not instrument the root module' do described_class.instrument_class_hierarchy(@dummy) - expect(@dummy).to_not respond_to(:_original_foo) - expect(@dummy.method_defined?(:_original_bar)).to eq(false) + expect(described_class.instrumented?(@dummy)).to eq(false) end end @@ -190,7 +213,7 @@ describe Gitlab::Metrics::Instrumentation do it 'instruments all public class methods' do described_class.instrument_methods(@dummy) - expect(@dummy).to respond_to(:_original_foo) + expect(described_class.instrumented?(@dummy.singleton_class)).to eq(true) end it 'only instruments methods directly defined in the module' do @@ -223,7 +246,7 @@ describe Gitlab::Metrics::Instrumentation do it 'instruments all public instance methods' do described_class.instrument_instance_methods(@dummy) - expect(@dummy.method_defined?(:_original_bar)).to eq(true) + expect(described_class.instrumented?(@dummy)).to eq(true) end it 'only instruments methods directly defined in the module' do diff --git a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb index 7bc070a4d09..e3293a01207 100644 --- a/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb +++ b/spec/lib/gitlab/metrics/subscribers/active_record_spec.rb @@ -28,6 +28,9 @@ describe Gitlab::Metrics::Subscribers::ActiveRecord do expect(transaction).to receive(:increment). with(:sql_duration, 0.2) + expect(transaction).to receive(:increment). + with(:sql_count, 1) + subscriber.sql(event) end end diff --git a/spec/lib/gitlab/metrics_spec.rb b/spec/lib/gitlab/metrics_spec.rb index 10177c0e8dd..96f7eabbca6 100644 --- a/spec/lib/gitlab/metrics_spec.rb +++ b/spec/lib/gitlab/metrics_spec.rb @@ -123,4 +123,28 @@ describe Gitlab::Metrics do end end end + + describe '.action=' do + context 'without a transaction' do + it 'does nothing' do + expect_any_instance_of(Gitlab::Metrics::Transaction). + not_to receive(:action=) + + Gitlab::Metrics.action = 'foo' + end + end + + context 'with a transaction' do + it 'sets the action of a transaction' do + trans = Gitlab::Metrics::Transaction.new + + expect(Gitlab::Metrics).to receive(:current_transaction). + and_return(trans) + + expect(trans).to receive(:action=).with('foo') + + Gitlab::Metrics.action = 'foo' + end + end + end end diff --git a/spec/lib/gitlab/push_data_builder_spec.rb b/spec/lib/gitlab/push_data_builder_spec.rb index 961022b9d12..7fc34139eff 100644 --- a/spec/lib/gitlab/push_data_builder_spec.rb +++ b/spec/lib/gitlab/push_data_builder_spec.rb @@ -14,11 +14,11 @@ describe Gitlab::PushDataBuilder, lib: true do it { expect(data[:ref]).to eq('refs/heads/master') } it { expect(data[:commits].size).to eq(3) } it { expect(data[:total_commits_count]).to eq(3) } - it { expect(data[:commits].first[:added]).to eq(["gitlab-grack"]) } - it { expect(data[:commits].first[:modified]).to eq([".gitmodules"]) } + it { expect(data[:commits].first[:added]).to eq(['gitlab-grack']) } + it { expect(data[:commits].first[:modified]).to eq(['.gitmodules']) } it { expect(data[:commits].first[:removed]).to eq([]) } - include_examples 'project hook data' + include_examples 'project hook data with deprecateds' include_examples 'deprecated repository hook data' end @@ -34,9 +34,18 @@ describe Gitlab::PushDataBuilder, lib: true do it { expect(data[:checkout_sha]).to eq('5937ac0a7beb003549fc5fd26fc247adbce4a52e') } it { expect(data[:after]).to eq('8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b') } it { expect(data[:ref]).to eq('refs/tags/v1.1.0') } + it { expect(data[:user_id]).to eq(user.id) } + it { expect(data[:user_name]).to eq(user.name) } + it { expect(data[:user_email]).to eq(user.email) } + it { expect(data[:user_avatar]).to eq(user.avatar_url) } + it { expect(data[:project_id]).to eq(project.id) } + it { expect(data[:project]).to be_a(Hash) } it { expect(data[:commits]).to be_empty } it { expect(data[:total_commits_count]).to be_zero } + include_examples 'project hook data with deprecateds' + include_examples 'deprecated repository hook data' + it 'does not raise an error when given nil commits' do expect { described_class.build(spy, spy, spy, spy, spy, nil) }. not_to raise_error diff --git a/spec/lib/gitlab/url_builder_spec.rb b/spec/lib/gitlab/url_builder_spec.rb index 6ffc0d6e658..bf11472407a 100644 --- a/spec/lib/gitlab/url_builder_spec.rb +++ b/spec/lib/gitlab/url_builder_spec.rb @@ -106,5 +106,14 @@ describe Gitlab::UrlBuilder, lib: true do end end end + + context 'when passing a WikiPage' do + it 'returns a proper URL' do + wiki_page = build(:wiki_page) + url = described_class.build(wiki_page) + + expect(url).to eq "#{Gitlab.config.gitlab.url}#{wiki_page.wiki.wiki_base_path}/#{wiki_page.slug}" + end + end end end diff --git a/spec/mailers/notify_spec.rb b/spec/mailers/notify_spec.rb index 631b5094f42..495c5cbac00 100644 --- a/spec/mailers/notify_spec.rb +++ b/spec/mailers/notify_spec.rb @@ -213,7 +213,7 @@ describe Notify do it_behaves_like 'an unsubscribeable thread' it 'has the correct subject' do - is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/ end it 'contains a link to the new merge request' do @@ -268,7 +268,7 @@ describe Notify do end it 'has the correct subject' do - is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/ end it 'contains the name of the previous assignee' do @@ -302,7 +302,7 @@ describe Notify do end it 'has the correct subject' do - is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/ end it 'contains the names of the added labels' do @@ -331,7 +331,7 @@ describe Notify do end it 'has the correct subject' do - is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/i + is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/i end it 'contains the new status' do @@ -364,7 +364,7 @@ describe Notify do end it 'has the correct subject' do - is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/ end it 'contains the new status' do @@ -502,7 +502,7 @@ describe Notify do it_behaves_like 'an unsubscribeable thread' it 'has the correct subject' do - is_expected.to have_subject /#{merge_request.title} \(##{merge_request.iid}\)/ + is_expected.to have_subject /#{merge_request.title} \(#{merge_request.to_reference}\)/ end it 'contains a link to the merge request note' do diff --git a/spec/mailers/repository_check_mailer_spec.rb b/spec/mailers/repository_check_mailer_spec.rb index 583bf15176f..00613c7b671 100644 --- a/spec/mailers/repository_check_mailer_spec.rb +++ b/spec/mailers/repository_check_mailer_spec.rb @@ -15,7 +15,7 @@ describe RepositoryCheckMailer do it 'mentions the number of failed checks' do mail = described_class.notify(3) - expect(mail).to have_subject '3 projects failed their last repository check' + expect(mail).to have_subject 'GitLab Admin | 3 projects failed their last repository check' end end end diff --git a/spec/models/abuse_report_spec.rb b/spec/models/abuse_report_spec.rb index ac12ab6c757..305f8bc88cc 100644 --- a/spec/models/abuse_report_spec.rb +++ b/spec/models/abuse_report_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: abuse_reports -# -# id :integer not null, primary key -# reporter_id :integer -# user_id :integer -# message :text -# created_at :datetime -# updated_at :datetime -# - require 'rails_helper' RSpec.describe AbuseReport, type: :model do diff --git a/spec/models/application_setting_spec.rb b/spec/models/application_setting_spec.rb index 520cf1b75de..1ce22feed5c 100644 --- a/spec/models/application_setting_spec.rb +++ b/spec/models/application_setting_spec.rb @@ -1,49 +1,3 @@ -# == Schema Information -# -# Table name: application_settings -# -# id :integer not null, primary key -# default_projects_limit :integer -# signup_enabled :boolean -# signin_enabled :boolean -# gravatar_enabled :boolean -# sign_in_text :text -# created_at :datetime -# updated_at :datetime -# home_page_url :string(255) -# default_branch_protection :integer default(2) -# restricted_visibility_levels :text -# version_check_enabled :boolean default(TRUE) -# max_attachment_size :integer default(10), not null -# default_project_visibility :integer -# default_snippet_visibility :integer -# restricted_signup_domains :text -# user_oauth_applications :boolean default(TRUE) -# after_sign_out_path :string(255) -# session_expire_delay :integer default(10080), not null -# import_sources :text -# help_page_text :text -# admin_notification_email :string(255) -# shared_runners_enabled :boolean default(TRUE), not null -# max_artifacts_size :integer default(100), not null -# runners_registration_token :string -# require_two_factor_authentication :boolean default(FALSE) -# two_factor_grace_period :integer default(48) -# metrics_enabled :boolean default(FALSE) -# metrics_host :string default("localhost") -# metrics_username :string -# metrics_password :string -# metrics_pool_size :integer default(16) -# metrics_timeout :integer default(10) -# metrics_method_call_threshold :integer default(10) -# recaptcha_enabled :boolean default(FALSE) -# recaptcha_site_key :string -# recaptcha_private_key :string -# metrics_port :integer default(8089) -# sentry_enabled :boolean default(FALSE) -# sentry_dsn :string -# - require 'spec_helper' describe ApplicationSetting, models: true do diff --git a/spec/models/broadcast_message_spec.rb b/spec/models/broadcast_message_spec.rb index f6f84db57e6..6ad8bfef4f2 100644 --- a/spec/models/broadcast_message_spec.rb +++ b/spec/models/broadcast_message_spec.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: broadcast_messages -# -# id :integer not null, primary key -# message :text not null -# starts_at :datetime -# ends_at :datetime -# created_at :datetime -# updated_at :datetime -# color :string(255) -# font :string(255) -# - require 'spec_helper' describe BroadcastMessage, models: true do diff --git a/spec/models/build_spec.rb b/spec/models/build_spec.rb index b7457808040..b5d356aa066 100644 --- a/spec/models/build_spec.rb +++ b/spec/models/build_spec.rb @@ -238,6 +238,22 @@ describe Ci::Build, models: true do it { is_expected.to eq(predefined_variables + predefined_trigger_variable + yaml_variables + secure_variables + trigger_variables) } end + + context 'when job variables are defined' do + ## + # Job-level variables are defined in gitlab_ci.yml fixture + # + context 'when job variables are unique' do + let(:build) { create(:ci_build, name: 'staging') } + + it 'includes job variables' do + expect(subject).to include( + { key: :KEY1, value: 'value1', public: true }, + { key: :KEY2, value: 'value2', public: true } + ) + end + end + end end end end diff --git a/spec/models/ci/commit_spec.rb b/spec/models/ci/commit_spec.rb index 412842337ba..dc071ad1c90 100644 --- a/spec/models/ci/commit_spec.rb +++ b/spec/models/ci/commit_spec.rb @@ -1,21 +1,3 @@ -# == Schema Information -# -# Table name: ci_commits -# -# id :integer not null, primary key -# project_id :integer -# ref :string(255) -# sha :string(255) -# before_sha :string(255) -# push_data :text -# created_at :datetime -# updated_at :datetime -# tag :boolean default(FALSE) -# yaml_errors :text -# committed_at :datetime -# gl_project_id :integer -# - require 'spec_helper' describe Ci::Commit, models: true do @@ -27,6 +9,8 @@ describe Ci::Commit, models: true do it { is_expected.to have_many(:trigger_requests) } 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 } @@ -52,57 +36,9 @@ describe Ci::Commit, models: true do it { expect(commit.sha).to start_with(subject) } end - describe :stage do - subject { commit.stage } - - before do - @second = FactoryGirl.create :commit_status, commit: commit, name: 'deploy', stage: 'deploy', stage_idx: 1, status: 'pending' - @first = FactoryGirl.create :commit_status, commit: commit, name: 'test', stage: 'test', stage_idx: 0, status: 'pending' - end - - it 'returns first running stage' do - is_expected.to eq('test') - end - - context 'first build succeeded' do - before do - @first.success - end - - it 'returns last running stage' do - is_expected.to eq('deploy') - end - end - - context 'all builds succeeded' do - before do - @first.success - @second.success - end - - it 'returns nil' do - is_expected.to be_nil - end - end - end - describe :create_next_builds do end - describe :refs do - subject { commit.refs } - - before do - FactoryGirl.create :commit_status, commit: commit, name: 'deploy' - FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'develop' - FactoryGirl.create :commit_status, commit: commit, name: 'deploy', ref: 'master' - end - - it 'returns all refs' do - is_expected.to contain_exactly('master', 'develop', nil) - end - end - describe :retried do subject { commit.retried } @@ -117,10 +53,10 @@ describe Ci::Commit, models: true do end describe :create_builds do - let!(:commit) { FactoryGirl.create :ci_commit, project: project } + let!(:commit) { FactoryGirl.create :ci_commit, project: project, ref: 'master', tag: false } def create_builds(trigger_request = nil) - commit.create_builds('master', false, nil, trigger_request) + commit.create_builds(nil, trigger_request) end def create_next_builds @@ -143,67 +79,6 @@ describe Ci::Commit, models: true do expect(create_next_builds).to be_falsey end - context 'for different ref' do - def create_develop_builds - commit.create_builds('develop', false, nil, nil) - end - - it 'creates builds' do - expect(create_builds).to be_truthy - commit.builds.update_all(status: "success") - expect(commit.builds.count(:all)).to eq(2) - - expect(create_develop_builds).to be_truthy - commit.builds.update_all(status: "success") - expect(commit.builds.count(:all)).to eq(4) - expect(commit.refs.size).to eq(2) - expect(commit.builds.pluck(:name).uniq.size).to eq(2) - end - end - - context 'for build triggers' do - let(:trigger) { FactoryGirl.create :ci_trigger, project: project } - let(:trigger_request) { FactoryGirl.create :ci_trigger_request, commit: commit, trigger: trigger } - - it 'creates builds' do - expect(create_builds(trigger_request)).to be_truthy - expect(commit.builds.count(:all)).to eq(2) - end - - it 'rebuilds commit' do - expect(create_builds).to be_truthy - expect(commit.builds.count(:all)).to eq(2) - - expect(create_builds(trigger_request)).to be_truthy - expect(commit.builds.count(:all)).to eq(4) - end - - it 'creates next builds' do - expect(create_builds(trigger_request)).to be_truthy - expect(commit.builds.count(:all)).to eq(2) - commit.builds.update_all(status: "success") - - expect(create_next_builds).to be_truthy - expect(commit.builds.count(:all)).to eq(4) - end - - context 'for [ci skip]' do - before do - allow(commit).to receive(:git_commit_message) { 'message [ci skip]' } - end - - it 'rebuilds commit' do - expect(commit.status).to eq('skipped') - expect(create_builds).to be_truthy - - # since everything in Ci::Commit is cached we need to fetch a new object - new_commit = Ci::Commit.find_by_id(commit.id) - expect(new_commit.status).to eq('pending') - end - end - end - - context 'custom stage with first job allowed to fail' do let(:yaml) do { @@ -265,93 +140,123 @@ describe Ci::Commit, models: true do stub_ci_commit_yaml_file(YAML.dump(yaml)) end - it 'properly creates builds' do - expect(create_builds).to be_truthy - expect(commit.builds.pluck(:name)).to contain_exactly('build') - expect(commit.builds.pluck(:status)).to contain_exactly('pending') - commit.builds.running_or_pending.each(&:success) + context 'when builds are successful' do + it 'properly creates builds' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') - commit.builds.running_or_pending.each(&:success) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending') - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy') - commit.builds.running_or_pending.each(&:success) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'pending') - commit.builds.running_or_pending.each(&:success) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success') - expect(commit.status).to eq('success') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'success', 'success') + commit.reload + expect(commit.status).to eq('success') + end end - it 'properly creates builds when test fails' do - expect(create_builds).to be_truthy - expect(commit.builds.pluck(:name)).to contain_exactly('build') - expect(commit.builds.pluck(:status)).to contain_exactly('pending') - commit.builds.running_or_pending.each(&:success) + context 'when test job fails' do + it 'properly creates builds' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') - commit.builds.running_or_pending.each(&:drop) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:drop) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending') - commit.builds.running_or_pending.each(&:success) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'pending') - commit.builds.running_or_pending.each(&:success) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success') - expect(commit.status).to eq('failed') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'success', 'success') + commit.reload + expect(commit.status).to eq('failed') + end end - it 'properly creates builds when test and test_failure fails' do - expect(create_builds).to be_truthy - expect(commit.builds.pluck(:name)).to contain_exactly('build') - expect(commit.builds.pluck(:status)).to contain_exactly('pending') - commit.builds.running_or_pending.each(&:success) + context 'when test and test_failure jobs fail' do + it 'properly creates builds' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:drop) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending') + commit.builds.running_or_pending.each(&:drop) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'pending') + commit.builds.running_or_pending.each(&:success) + + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success') + commit.reload + expect(commit.status).to eq('failed') + end + end - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') - commit.builds.running_or_pending.each(&:drop) + context 'when deploy job fails' do + it 'properly creates builds' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'pending') - commit.builds.running_or_pending.each(&:drop) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'pending') - commit.builds.running_or_pending.each(&:success) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending') + commit.builds.running_or_pending.each(&:drop) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'test_failure', 'cleanup') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'failed', 'failed', 'success') - expect(commit.status).to eq('failed') - end + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'pending') + commit.builds.running_or_pending.each(&:success) - it 'properly creates builds when deploy fails' do - expect(create_builds).to be_truthy - expect(commit.builds.pluck(:name)).to contain_exactly('build') - expect(commit.builds.pluck(:status)).to contain_exactly('pending') - commit.builds.running_or_pending.each(&:success) + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success') + commit.reload + expect(commit.status).to eq('failed') + end + end - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') - commit.builds.running_or_pending.each(&:success) + context 'when build is canceled in the second stage' do + it 'does not schedule builds after build has been canceled' do + expect(create_builds).to be_truthy + expect(commit.builds.pluck(:name)).to contain_exactly('build') + expect(commit.builds.pluck(:status)).to contain_exactly('pending') + commit.builds.running_or_pending.each(&:success) - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'pending') - commit.builds.running_or_pending.each(&:drop) + expect(commit.builds.running_or_pending).to_not be_empty - expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test', 'deploy', 'cleanup') - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'pending') - commit.builds.running_or_pending.each(&:success) + expect(commit.builds.pluck(:name)).to contain_exactly('build', 'test') + expect(commit.builds.pluck(:status)).to contain_exactly('success', 'pending') + commit.builds.running_or_pending.each(&:cancel) - expect(commit.builds.pluck(:status)).to contain_exactly('success', 'success', 'failed', 'success') - expect(commit.status).to eq('failed') + expect(commit.builds.running_or_pending).to be_empty + expect(commit.reload.status).to eq('canceled') + end end end end @@ -402,4 +307,98 @@ describe Ci::Commit, models: true do expect(commit.coverage).to be_nil end end + + describe '#retryable?' do + subject { commit.retryable? } + + context 'no failed builds' do + before do + FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'success' + end + + it 'be not retryable' do + is_expected.to be_falsey + end + end + + context 'with failed builds' do + before do + FactoryGirl.create :ci_build, name: "rspec", commit: commit, status: 'running' + FactoryGirl.create :ci_build, name: "rubocop", commit: commit, status: 'failed' + end + + it 'be retryable' do + is_expected.to be_truthy + end + end + end + + describe '#stages' do + let(:commit2) { FactoryGirl.create :ci_commit, project: project } + subject { CommitStatus.where(commit: [commit, commit2]).stages } + + before do + FactoryGirl.create :ci_build, commit: commit2, stage: 'test', stage_idx: 1 + FactoryGirl.create :ci_build, commit: commit, stage: 'build', stage_idx: 0 + end + + it 'return all stages' do + is_expected.to eq(%w(build test)) + end + end + + describe '#update_state' do + it 'execute update_state after touching object' do + expect(commit).to receive(:update_state).and_return(true) + commit.touch + end + + context 'dependent objects' do + let(:commit_status) { build :commit_status, commit: commit } + + it 'execute update_state after saving dependent object' do + expect(commit).to receive(:update_state).and_return(true) + commit_status.save + end + end + + context 'update state' do + let(:current) { Time.now.change(usec: 0) } + let(:build) { FactoryGirl.create :ci_build, :success, commit: commit, started_at: current - 120, finished_at: current - 60 } + + before do + build + end + + [:status, :started_at, :finished_at, :duration].each do |param| + it "update #{param}" do + expect(commit.send(param)).to eq(build.send(param)) + end + end + end + end + + describe '#branch?' do + subject { commit.branch? } + + context 'is not a tag' do + before do + commit.tag = false + end + + it 'return true when tag is set to false' do + is_expected.to be_truthy + end + end + + context 'is not a tag' do + before do + commit.tag = true + end + + it 'return false when tag is set to true' do + is_expected.to be_falsey + end + end + end end diff --git a/spec/models/ci/runner_project_spec.rb b/spec/models/ci/runner_project_spec.rb index 000a732db77..95fc160b238 100644 --- a/spec/models/ci/runner_project_spec.rb +++ b/spec/models/ci/runner_project_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: ci_runner_projects -# -# id :integer not null, primary key -# runner_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# gl_project_id :integer -# - require 'spec_helper' describe Ci::RunnerProject, models: true do diff --git a/spec/models/ci/runner_spec.rb b/spec/models/ci/runner_spec.rb index 25e9e5eca48..eaa94228922 100644 --- a/spec/models/ci/runner_spec.rb +++ b/spec/models/ci/runner_spec.rb @@ -1,22 +1,3 @@ -# == Schema Information -# -# Table name: ci_runners -# -# id :integer not null, primary key -# token :string(255) -# created_at :datetime -# updated_at :datetime -# description :string(255) -# contacted_at :datetime -# active :boolean default(TRUE), not null -# is_shared :boolean default(FALSE) -# name :string(255) -# version :string(255) -# revision :string(255) -# platform :string(255) -# architecture :string(255) -# - require 'spec_helper' describe Ci::Runner, models: true do diff --git a/spec/models/ci/trigger_spec.rb b/spec/models/ci/trigger_spec.rb index 159be939300..474b0b1621d 100644 --- a/spec/models/ci/trigger_spec.rb +++ b/spec/models/ci/trigger_spec.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: ci_triggers -# -# id :integer not null, primary key -# token :string(255) -# project_id :integer -# deleted_at :datetime -# created_at :datetime -# updated_at :datetime -# gl_project_id :integer -# - require 'spec_helper' describe Ci::Trigger, models: true do diff --git a/spec/models/ci/variable_spec.rb b/spec/models/ci/variable_spec.rb index 71e84091cb7..c712d211b0f 100644 --- a/spec/models/ci/variable_spec.rb +++ b/spec/models/ci/variable_spec.rb @@ -1,17 +1,3 @@ -# == Schema Information -# -# Table name: ci_variables -# -# id :integer not null, primary key -# project_id :integer -# key :string(255) -# value :text -# encrypted_value :text -# encrypted_value_salt :string(255) -# encrypted_value_iv :string(255) -# gl_project_id :integer -# - require 'spec_helper' describe Ci::Variable, models: true do diff --git a/spec/models/commit_spec.rb b/spec/models/commit_spec.rb index 0e9111c8029..ad47e338a33 100644 --- a/spec/models/commit_spec.rb +++ b/spec/models/commit_spec.rb @@ -163,4 +163,12 @@ eos it { expect(commit.reverts_commit?(another_commit)).to be_truthy } end end + + describe '#ci_commits' do + # TODO: kamil + end + + describe '#status' do + # TODO: kamil + end end diff --git a/spec/models/commit_status_spec.rb b/spec/models/commit_status_spec.rb index 82c68ff6cb1..434e58cfd06 100644 --- a/spec/models/commit_status_spec.rb +++ b/spec/models/commit_status_spec.rb @@ -1,37 +1,3 @@ -# == Schema Information -# -# Table name: ci_builds -# -# id :integer not null, primary key -# project_id :integer -# status :string(255) -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# coverage :float -# commit_id :integer -# commands :text -# job_id :integer -# name :string(255) -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string(255) -# trigger_request_id :integer -# stage_idx :integer -# tag :boolean -# ref :string(255) -# user_id :integer -# type :string(255) -# target_url :string(255) -# description :string(255) -# artifacts_file :text -# gl_project_id :integer -# - require 'spec_helper' describe CommitStatus, models: true do @@ -163,37 +129,73 @@ describe CommitStatus, models: true do end it 'return unique statuses' do - is_expected.to eq([@commit2, @commit3, @commit4, @commit5]) + is_expected.to eq([@commit4, @commit5]) end end - describe :for_ref do - subject { CommitStatus.for_ref('bb').order(:id) } + describe :running_or_pending do + subject { CommitStatus.running_or_pending.order(:id) } before do @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' + @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed' + @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled' end - it 'return statuses with equal and nil ref set' do - is_expected.to eq([@commit1]) + it 'return statuses that are running or pending' do + is_expected.to eq([@commit1, @commit2]) end end - describe :running_or_pending do - subject { CommitStatus.running_or_pending.order(:id) } + describe '#before_sha' do + subject { commit_status.before_sha } + + context 'when no before_sha is set for ci::commit' do + before { commit.before_sha = nil } + + it 'return blank sha' do + is_expected.to eq(Gitlab::Git::BLANK_SHA) + end + end + + context 'for before_sha set for ci::commit' do + let(:value) { '1234' } + before { commit.before_sha = value } + it 'return the set value' do + is_expected.to eq(value) + end + end + end + + describe '#stages' do before do - @commit1 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: 'bb', status: 'running' - @commit2 = FactoryGirl.create :commit_status, commit: commit, name: 'cc', ref: 'cc', status: 'pending' - @commit3 = FactoryGirl.create :commit_status, commit: commit, name: 'aa', ref: nil, status: 'success' - @commit4 = FactoryGirl.create :commit_status, commit: commit, name: 'dd', ref: nil, status: 'failed' - @commit5 = FactoryGirl.create :commit_status, commit: commit, name: 'ee', ref: nil, status: 'canceled' + FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'success' + FactoryGirl.create :commit_status, commit: commit, stage: 'build', stage_idx: 0, status: 'failed' + FactoryGirl.create :commit_status, commit: commit, stage: 'deploy', stage_idx: 2, status: 'running' + FactoryGirl.create :commit_status, commit: commit, stage: 'test', stage_idx: 1, status: 'success' end - it 'return statuses that are running or pending' do - is_expected.to eq([@commit1, @commit2]) + context 'stages list' do + subject { CommitStatus.where(commit: commit).stages } + + it 'return ordered list of stages' do + is_expected.to eq(%w(build test deploy)) + end + end + + context 'stages with statuses' do + subject { CommitStatus.where(commit: commit).stages_status } + + it 'return list of stages with statuses' do + is_expected.to eq({ + 'build' => 'failed', + 'test' => 'success', + 'deploy' => 'running' + }) + end end end end diff --git a/spec/models/concerns/issuable_spec.rb b/spec/models/concerns/issuable_spec.rb index 86ad9de883f..568bf4c9324 100644 --- a/spec/models/concerns/issuable_spec.rb +++ b/spec/models/concerns/issuable_spec.rb @@ -202,4 +202,47 @@ describe Issue, "Issuable" do to eq({ 'Author' => 'Robert', 'Assignee' => 'Douwe' }) end end + + # TODO ZJ + describe "votes" do + before do + create!(:award_emoji, :upvote, awardable: issue) + create!(:award_emoji, :downvote, awardable: issue) + end + + it "returns correct values" do + expect(issue.upvotes).to eq(1) + expect(issue.downvotes).to eq(1) + end + end + + describe ".with_label" do + let(:project) { create(:project, :public) } + let(:bug) { create(:label, project: project, title: 'bug') } + let(:feature) { create(:label, project: project, title: 'feature') } + let(:enhancement) { create(:label, project: project, title: 'enhancement') } + let(:issue1) { create(:issue, title: "Bugfix1", project: project) } + let(:issue2) { create(:issue, title: "Bugfix2", project: project) } + let(:issue3) { create(:issue, title: "Feature1", project: project) } + + before(:each) do + issue1.labels << bug + issue1.labels << feature + issue2.labels << bug + issue2.labels << enhancement + issue3.labels << feature + end + + it 'finds the correct issue containing just enhancement label' do + expect(Issue.with_label(enhancement.title)).to match_array([issue2]) + end + + it 'finds the correct issues containing the same label' do + expect(Issue.with_label(bug.title)).to match_array([issue1, issue2]) + end + + it 'finds the correct issues containing only both labels' do + expect(Issue.with_label([bug.title, enhancement.title])).to match_array([issue2]) + end + end end diff --git a/spec/lib/ci/status_spec.rb b/spec/models/concerns/statuseable_spec.rb index 47f3df6e3ce..8e0a2a2cbde 100644 --- a/spec/lib/ci/status_spec.rb +++ b/spec/models/concerns/statuseable_spec.rb @@ -1,8 +1,17 @@ require 'spec_helper' -describe Ci::Status do - describe '.get_status' do - subject { described_class.get_status(statuses) } +describe Statuseable do + before do + @object = Object.new + @object.extend(Statuseable::ClassMethods) + end + + describe '.status' do + before do + allow(@object).to receive(:all).and_return(CommitStatus.where(id: statuses)) + end + + subject { @object.status } shared_examples 'build status summary' do context 'all successful' do @@ -52,9 +61,35 @@ describe Ci::Status do let(:statuses) do [create(type, status: :success), create(type, status: :canceled)] end + + it { is_expected.to eq 'canceled' } + end + + context 'one failed and one canceled' do + let(:statuses) do + [create(type, status: :failed), create(type, status: :canceled)] + end + it { is_expected.to eq 'failed' } end + context 'one failed but allowed to fail and one canceled' do + let(:statuses) do + [create(type, status: :failed, allow_failure: true), + create(type, status: :canceled)] + end + + it { is_expected.to eq 'canceled' } + end + + context 'one running one canceled' do + let(:statuses) do + [create(type, status: :running), create(type, status: :canceled)] + end + + it { is_expected.to eq 'running' } + end + context 'all canceled' do let(:statuses) do [create(type, status: :canceled), create(type, status: :canceled)] diff --git a/spec/models/deploy_key_spec.rb b/spec/models/deploy_key_spec.rb index 64ba778afea..6a90598a629 100644 --- a/spec/models/deploy_key_spec.rb +++ b/spec/models/deploy_key_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: keys -# -# id :integer not null, primary key -# user_id :integer -# created_at :datetime -# updated_at :datetime -# key :text -# title :string(255) -# type :string(255) -# fingerprint :string(255) -# public :boolean default(FALSE), not null -# - require 'spec_helper' describe DeployKey, models: true do diff --git a/spec/models/deploy_keys_project_spec.rb b/spec/models/deploy_keys_project_spec.rb index 8aedbfb8636..8a1e337c1a3 100644 --- a/spec/models/deploy_keys_project_spec.rb +++ b/spec/models/deploy_keys_project_spec.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: deploy_keys_projects -# -# id :integer not null, primary key -# deploy_key_id :integer not null -# project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' describe DeployKeysProject, models: true do diff --git a/spec/models/email_spec.rb b/spec/models/email_spec.rb index a20a6149649..5d0bd31db5a 100644 --- a/spec/models/email_spec.rb +++ b/spec/models/email_spec.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: emails -# -# id :integer not null, primary key -# user_id :integer not null -# email :string(255) not null -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' describe Email, models: true do diff --git a/spec/models/event_spec.rb b/spec/models/event_spec.rb index 89909c2bcd7..b0e76fec693 100644 --- a/spec/models/event_spec.rb +++ b/spec/models/event_spec.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: events -# -# id :integer not null, primary key -# target_type :string(255) -# target_id :integer -# title :string(255) -# data :text -# project_id :integer -# created_at :datetime -# updated_at :datetime -# action :integer -# author_id :integer -# - require 'spec_helper' describe Event, models: true do @@ -30,32 +14,29 @@ describe Event, models: true do it { is_expected.to respond_to(:commits) } end + describe 'Callbacks' do + describe 'after_create :reset_project_activity' do + let(:project) { create(:project) } + + context "project's last activity was less than 5 minutes ago" do + it 'does not update project.last_activity_at if it has been touched less than 5 minutes ago' do + create_event(project, project.owner) + project.update_column(:last_activity_at, 5.minutes.ago) + project_last_activity_at = project.last_activity_at + + create_event(project, project.owner) + + expect(project.last_activity_at).to eq(project_last_activity_at) + end + end + end + end + describe "Push event" do before do project = create(:project) @user = project.owner - - data = { - before: Gitlab::Git::BLANK_SHA, - after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e", - ref: "refs/heads/master", - user_id: @user.id, - user_name: @user.name, - repository: { - name: project.name, - url: "localhost/rubinius", - description: "", - homepage: "localhost/rubinius", - private: true - } - } - - @event = Event.create( - project: project, - action: Event::PUSHED, - data: data, - author_id: @user.id - ) + @event = create_event(project, @user) end it { expect(@event.push?).to be_truthy } @@ -143,4 +124,28 @@ describe Event, models: true do it { is_expected.to eq([event2]) } end end + + def create_event(project, user, attrs = {}) + data = { + before: Gitlab::Git::BLANK_SHA, + after: "0220c11b9a3e6c69dc8fd35321254ca9a7b98f7e", + ref: "refs/heads/master", + user_id: user.id, + user_name: user.name, + repository: { + name: project.name, + url: "localhost/rubinius", + description: "", + homepage: "localhost/rubinius", + private: true + } + } + + Event.create({ + project: project, + action: Event::PUSHED, + data: data, + author_id: user.id + }.merge(attrs)) + end end diff --git a/spec/models/external_issue_spec.rb b/spec/models/external_issue_spec.rb index 9b144dd1ecc..4fc3b065592 100644 --- a/spec/models/external_issue_spec.rb +++ b/spec/models/external_issue_spec.rb @@ -36,4 +36,19 @@ describe ExternalIssue, models: true do expect(issue.title).to eq "External Issue #{issue}" end end + + describe '#reference_link_text' do + context 'if issue id has a prefix' do + it 'returns the issue ID' do + expect(issue.reference_link_text).to eq 'EXT-1234' + end + end + + context 'if issue id is a number' do + let(:issue) { described_class.new('1234', project) } + it 'returns the issue ID prefixed by #' do + expect(issue.reference_link_text).to eq '#1234' + end + end + end end diff --git a/spec/models/forked_project_link_spec.rb b/spec/models/forked_project_link_spec.rb index d90fbfe1ea5..3b817608ce0 100644 --- a/spec/models/forked_project_link_spec.rb +++ b/spec/models/forked_project_link_spec.rb @@ -1,14 +1,3 @@ -# == Schema Information -# -# Table name: forked_project_links -# -# id :integer not null, primary key -# forked_to_project_id :integer not null -# forked_from_project_id :integer not null -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' describe ForkedProjectLink, "add link on fork" do diff --git a/spec/models/generic_commit_status_spec.rb b/spec/models/generic_commit_status_spec.rb index 5b0883d8702..0caf5869c24 100644 --- a/spec/models/generic_commit_status_spec.rb +++ b/spec/models/generic_commit_status_spec.rb @@ -1,37 +1,3 @@ -# == Schema Information -# -# Table name: ci_builds -# -# id :integer not null, primary key -# project_id :integer -# status :string(255) -# finished_at :datetime -# trace :text -# created_at :datetime -# updated_at :datetime -# started_at :datetime -# runner_id :integer -# coverage :float -# commit_id :integer -# commands :text -# job_id :integer -# name :string(255) -# deploy :boolean default(FALSE) -# options :text -# allow_failure :boolean default(FALSE), not null -# stage :string(255) -# trigger_request_id :integer -# stage_idx :integer -# tag :boolean -# ref :string(255) -# user_id :integer -# type :string(255) -# target_url :string(255) -# description :string(255) -# artifacts_file :text -# gl_project_id :integer -# - require 'spec_helper' describe GenericCommitStatus, models: true do diff --git a/spec/models/group_spec.rb b/spec/models/group_spec.rb index 7bfca1e72c3..6fa16be7f04 100644 --- a/spec/models/group_spec.rb +++ b/spec/models/group_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: namespaces -# -# id :integer not null, primary key -# name :string(255) not null -# path :string(255) not null -# owner_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) -# description :string(255) default(""), not null -# avatar :string(255) -# - require 'spec_helper' describe Group, models: true do diff --git a/spec/models/hooks/web_hook_spec.rb b/spec/models/hooks/web_hook_spec.rb index 04bc2dcfb16..37a27d73aab 100644 --- a/spec/models/hooks/web_hook_spec.rb +++ b/spec/models/hooks/web_hook_spec.rb @@ -43,51 +43,65 @@ describe WebHook, models: true do end describe "execute" do + let(:project) { create(:project) } + let(:project_hook) { create(:project_hook) } + before(:each) do - @project_hook = create(:project_hook) - @project = create(:project) - @project.hooks << [@project_hook] + project.hooks << [project_hook] @data = { before: 'oldrev', after: 'newrev', ref: 'ref' } - WebMock.stub_request(:post, @project_hook.url) + WebMock.stub_request(:post, project_hook.url) + end + + context 'when token is defined' do + let(:project_hook) { create(:project_hook, :token) } + + it 'POSTs to the webhook URL' do + project_hook.execute(@data, 'push_hooks') + expect(WebMock).to have_requested(:post, project_hook.url).with( + headers: { 'Content-Type' => 'application/json', + 'X-Gitlab-Event' => 'Push Hook', + 'X-Gitlab-Token' => project_hook.token } + ).once + end end it "POSTs to the webhook URL" do - @project_hook.execute(@data, 'push_hooks') - expect(WebMock).to have_requested(:post, @project_hook.url).with( - headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook' } + project_hook.execute(@data, 'push_hooks') + expect(WebMock).to have_requested(:post, project_hook.url).with( + headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Push Hook' } ).once end it "POSTs the data as JSON" do - @project_hook.execute(@data, 'push_hooks') - expect(WebMock).to have_requested(:post, @project_hook.url).with( - headers: { 'Content-Type'=>'application/json', 'X-Gitlab-Event'=>'Push Hook' } + project_hook.execute(@data, 'push_hooks') + expect(WebMock).to have_requested(:post, project_hook.url).with( + headers: { 'Content-Type' => 'application/json', 'X-Gitlab-Event' => 'Push Hook' } ).once end it "catches exceptions" do expect(WebHook).to receive(:post).and_raise("Some HTTP Post error") - expect { @project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError) + expect { project_hook.execute(@data, 'push_hooks') }.to raise_error(RuntimeError) end it "handles SSL exceptions" do expect(WebHook).to receive(:post).and_raise(OpenSSL::SSL::SSLError.new('SSL error')) - expect(@project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) + expect(project_hook.execute(@data, 'push_hooks')).to eq([false, 'SSL error']) end it "handles 200 status code" do - WebMock.stub_request(:post, @project_hook.url).to_return(status: 200, body: "Success") + 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([true, 'Success']) end it "handles 2xx status codes" do - WebMock.stub_request(:post, @project_hook.url).to_return(status: 201, body: "Success") + 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([true, 'Success']) end end end diff --git a/spec/models/identity_spec.rb b/spec/models/identity_spec.rb index 5afe042e154..1b987588f59 100644 --- a/spec/models/identity_spec.rb +++ b/spec/models/identity_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: identities -# -# id :integer not null, primary key -# extern_uid :string(255) -# provider :string(255) -# user_id :integer -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' RSpec.describe Identity, models: true do diff --git a/spec/models/issue_spec.rb b/spec/models/issue_spec.rb index fac516f9568..8ab00c70f9d 100644 --- a/spec/models/issue_spec.rb +++ b/spec/models/issue_spec.rb @@ -1,23 +1,3 @@ -# == Schema Information -# -# Table name: issues -# -# id :integer not null, primary key -# title :string(255) -# assignee_id :integer -# author_id :integer -# project_id :integer -# created_at :datetime -# updated_at :datetime -# position :integer default(0) -# branch_name :string(255) -# description :text -# milestone_id :integer -# state :string(255) -# iid :integer -# updated_by_id :integer -# - require 'spec_helper' describe Issue, models: true do @@ -191,18 +171,36 @@ describe Issue, models: true do end describe '#related_branches' do - it 'selects the right branches' do + let(:user) { build(:admin) } + + before do allow(subject.project.repository).to receive(:branch_names). - and_return(['mpempe', "#{subject.iid}mepmep", subject.to_branch_name]) + and_return(["mpempe", "#{subject.iid}mepmep", subject.to_branch_name, "#{subject.iid}-branch"]) + + # Without this stub, the `create(:merge_request)` above fails because it can't find + # the source branch. This seems like a reasonable compromise, in comparison with + # setting up a full repo here. + allow_any_instance_of(MergeRequest).to receive(:create_merge_request_diff) + end + + it "selects the right branches when there are no referenced merge requests" do + expect(subject.related_branches(user)).to eq([subject.to_branch_name, "#{subject.iid}-branch"]) + end - expect(subject.related_branches).to eq([subject.to_branch_name]) + it "selects the right branches when there is a referenced merge request" do + merge_request = create(:merge_request, { description: "Closes ##{subject.iid}", + source_project: subject.project, + source_branch: "#{subject.iid}-branch" }) + merge_request.create_cross_references!(user) + expect(subject.referenced_merge_requests).to_not be_empty + expect(subject.related_branches(user)).to eq([subject.to_branch_name]) end it 'excludes stable branches from the related branches' do allow(subject.project.repository).to receive(:branch_names). and_return(["#{subject.iid}-0-stable"]) - expect(subject.related_branches).to eq [] + expect(subject.related_branches(user)).to eq [] end end @@ -217,11 +215,20 @@ describe Issue, models: true do let(:subject) { create :issue } end - describe '#to_branch_name' do - let(:issue) { create(:issue, title: 'a' * 30) } + describe "#to_branch_name" do + let(:issue) { create(:issue, title: 'testing-issue') } it 'starts with the issue iid' do - expect(issue.to_branch_name).to match /\A#{issue.iid}-a+\z/ + expect(issue.to_branch_name).to match /\A#{issue.iid}-[A-Za-z\-]+\z/ + end + + it "contains the issue title if not confidential" do + expect(issue.to_branch_name).to match /testing-issue\z/ + end + + it "does not contain the issue title if confidential" do + issue = create(:issue, title: 'testing-issue', confidential: true) + expect(issue.to_branch_name).to match /confidential-issue\z/ end end end diff --git a/spec/models/key_spec.rb b/spec/models/key_spec.rb index c962b83644a..26fbedbef2f 100644 --- a/spec/models/key_spec.rb +++ b/spec/models/key_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: keys -# -# id :integer not null, primary key -# user_id :integer -# created_at :datetime -# updated_at :datetime -# key :text -# title :string(255) -# type :string(255) -# fingerprint :string(255) -# public :boolean default(FALSE), not null -# - require 'spec_helper' describe Key, models: true do diff --git a/spec/models/label_link_spec.rb b/spec/models/label_link_spec.rb index dc7510b1de3..5e6f8ca1528 100644 --- a/spec/models/label_link_spec.rb +++ b/spec/models/label_link_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: label_links -# -# id :integer not null, primary key -# label_id :integer -# target_id :integer -# target_type :string(255) -# created_at :datetime -# updated_at :datetime -# - require 'spec_helper' describe LabelLink, models: true do diff --git a/spec/models/label_spec.rb b/spec/models/label_spec.rb index 0614ca1e7c9..dad2628651b 100644 --- a/spec/models/label_spec.rb +++ b/spec/models/label_spec.rb @@ -1,16 +1,3 @@ -# == Schema Information -# -# Table name: labels -# -# id :integer not null, primary key -# title :string(255) -# color :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# template :boolean default(FALSE) -# - require 'spec_helper' describe Label, models: true do @@ -55,6 +42,14 @@ describe Label, models: true do end end + describe "#title" do + let(:label) { create(:label, title: "<b>test</b>") } + + it "sanitizes title" do + expect(label.title).to eq("test") + end + end + describe '#to_reference' do context 'using id' do it 'returns a String reference to the object' do diff --git a/spec/models/member_spec.rb b/spec/models/member_spec.rb index 2d8f1cc1ad3..6e51730eecd 100644 --- a/spec/models/member_spec.rb +++ b/spec/models/member_spec.rb @@ -1,22 +1,3 @@ -# == Schema Information -# -# Table name: members -# -# id :integer not null, primary key -# access_level :integer not null -# source_id :integer not null -# source_type :string(255) not null -# user_id :integer -# notification_level :integer not null -# type :string(255) -# created_at :datetime -# updated_at :datetime -# created_by_id :integer -# invite_email :string(255) -# invite_token :string(255) -# invite_accepted_at :datetime -# - require 'spec_helper' describe Member, models: true do diff --git a/spec/models/merge_request_spec.rb b/spec/models/merge_request_spec.rb index 6f5d912fe5d..c8578749b21 100644 --- a/spec/models/merge_request_spec.rb +++ b/spec/models/merge_request_spec.rb @@ -1,32 +1,3 @@ -# == Schema Information -# -# Table name: merge_requests -# -# id :integer not null, primary key -# target_branch :string(255) not null -# source_branch :string(255) not null -# source_project_id :integer not null -# author_id :integer -# assignee_id :integer -# title :string(255) -# created_at :datetime -# updated_at :datetime -# milestone_id :integer -# state :string(255) -# merge_status :string(255) -# target_project_id :integer not null -# iid :integer -# description :text -# position :integer default(0) -# locked_at :datetime -# updated_by_id :integer -# merge_error :string(255) -# merge_params :text -# merge_when_build_succeeds :boolean default(FALSE), not null -# merge_user_id :integer -# merge_commit_sha :string -# - require 'spec_helper' describe MergeRequest, models: true do @@ -404,12 +375,12 @@ describe MergeRequest, models: true do describe 'when the source project exists' do it 'returns the latest commit' do commit = double(:commit, id: '123abc') - ci_commit = double(:ci_commit) + ci_commit = double(:ci_commit, ref: 'master') allow(subject).to receive(:last_commit).and_return(commit) expect(subject.source_project).to receive(:ci_commit). - with('123abc'). + with('123abc', 'master'). and_return(ci_commit) expect(subject.ci_commit).to eq(ci_commit) diff --git a/spec/models/milestone_spec.rb b/spec/models/milestone_spec.rb index 72a4ea70228..247a9fa9910 100644 --- a/spec/models/milestone_spec.rb +++ b/spec/models/milestone_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: milestones -# -# id :integer not null, primary key -# title :string(255) not null -# project_id :integer not null -# description :text -# due_date :date -# created_at :datetime -# updated_at :datetime -# state :string(255) -# iid :integer -# - require 'spec_helper' describe Milestone, models: true do @@ -34,6 +19,14 @@ describe Milestone, models: true do let(:issue) { create(:issue) } let(:user) { create(:user) } + describe "#title" do + let(:milestone) { create(:milestone, title: "<b>test</b>") } + + it "sanitizes title" do + expect(milestone.title).to eq("test") + end + end + describe "unique milestone title per project" do it "shouldn't accept the same title in a project twice" do new_milestone = Milestone.new(project: milestone.project, title: milestone.title) diff --git a/spec/models/namespace_spec.rb b/spec/models/namespace_spec.rb index 3c3a580942a..4074f966299 100644 --- a/spec/models/namespace_spec.rb +++ b/spec/models/namespace_spec.rb @@ -1,18 +1,3 @@ -# == Schema Information -# -# Table name: namespaces -# -# id :integer not null, primary key -# name :string(255) not null -# path :string(255) not null -# owner_id :integer -# created_at :datetime -# updated_at :datetime -# type :string(255) -# description :string(255) default(""), not null -# avatar :string(255) -# - require 'spec_helper' describe Namespace, models: true do diff --git a/spec/models/note_spec.rb b/spec/models/note_spec.rb index bb591e9cb53..b479f1c2f1a 100644 --- a/spec/models/note_spec.rb +++ b/spec/models/note_spec.rb @@ -1,24 +1,3 @@ -# == Schema Information -# -# Table name: notes -# -# id :integer not null, primary key -# note :text -# noteable_type :string(255) -# author_id :integer -# created_at :datetime -# updated_at :datetime -# project_id :integer -# attachment :string(255) -# line_code :string(255) -# commit_id :string(255) -# noteable_id :integer -# system :boolean default(FALSE), not null -# st_diff :text -# updated_by_id :integer -# is_award :boolean default(FALSE), not null -# - require 'spec_helper' describe Note, models: true do diff --git a/spec/models/project_services/bamboo_service_spec.rb b/spec/models/project_services/bamboo_service_spec.rb index 31b2c90122d..e771f35811e 100644 --- a/spec/models/project_services/bamboo_service_spec.rb +++ b/spec/models/project_services/bamboo_service_spec.rb @@ -27,86 +27,51 @@ describe BambooService, models: true do end describe 'Validations' do - describe '#bamboo_url' do - it 'does not validate the presence of bamboo_url if service is not active' do - bamboo_service = service - bamboo_service.active = false - - expect(bamboo_service).not_to validate_presence_of(:bamboo_url) - end - - it 'validates the presence of bamboo_url if service is active' do - bamboo_service = service - bamboo_service.active = true - - expect(bamboo_service).to validate_presence_of(:bamboo_url) - end - end + subject { service } - describe '#build_key' do - it 'does not validate the presence of build_key if service is not active' do - bamboo_service = service - bamboo_service.active = false + context 'when service is active' do + before { subject.active = true } - expect(bamboo_service).not_to validate_presence_of(:build_key) - end + it { is_expected.to validate_presence_of(:build_key) } + it { is_expected.to validate_presence_of(:bamboo_url) } + it_behaves_like 'issue tracker service URL attribute', :bamboo_url - it 'validates the presence of build_key if service is active' do - bamboo_service = service - bamboo_service.active = true + describe '#username' do + it 'does not validate the presence of username if password is nil' do + subject.password = nil - expect(bamboo_service).to validate_presence_of(:build_key) - end - end + expect(subject).not_to validate_presence_of(:username) + end - describe '#username' do - it 'does not validate the presence of username if service is not active' do - bamboo_service = service - bamboo_service.active = false + it 'validates the presence of username if password is present' do + subject.password = 'secret' - expect(bamboo_service).not_to validate_presence_of(:username) + expect(subject).to validate_presence_of(:username) + end end - it 'does not validate the presence of username if username is nil' do - bamboo_service = service - bamboo_service.active = true - bamboo_service.password = nil + describe '#password' do + it 'does not validate the presence of password if username is nil' do + subject.username = nil - expect(bamboo_service).not_to validate_presence_of(:username) - end + expect(subject).not_to validate_presence_of(:password) + end - it 'validates the presence of username if service is active and username is present' do - bamboo_service = service - bamboo_service.active = true - bamboo_service.password = 'secret' + it 'validates the presence of password if username is present' do + subject.username = 'john' - expect(bamboo_service).to validate_presence_of(:username) + expect(subject).to validate_presence_of(:password) + end end end - describe '#password' do - it 'does not validate the presence of password if service is not active' do - bamboo_service = service - bamboo_service.active = false - - expect(bamboo_service).not_to validate_presence_of(:password) - end - - it 'does not validate the presence of password if username is nil' do - bamboo_service = service - bamboo_service.active = true - bamboo_service.username = nil - - expect(bamboo_service).not_to validate_presence_of(:password) - end - - it 'validates the presence of password if service is active and username is present' do - bamboo_service = service - bamboo_service.active = true - bamboo_service.username = 'john' + context 'when service is inactive' do + before { subject.active = false } - expect(bamboo_service).to validate_presence_of(:password) - end + it { is_expected.not_to validate_presence_of(:build_key) } + it { is_expected.not_to validate_presence_of(:bamboo_url) } + it { is_expected.not_to validate_presence_of(:username) } + it { is_expected.not_to validate_presence_of(:password) } end end diff --git a/spec/models/project_services/buildkite_service_spec.rb b/spec/models/project_services/buildkite_service_spec.rb index 88cd624877a..60364df2015 100644 --- a/spec/models/project_services/buildkite_service_spec.rb +++ b/spec/models/project_services/buildkite_service_spec.rb @@ -26,6 +26,23 @@ describe BuildkiteService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:token) } + it_behaves_like 'issue tracker service URL attribute', :project_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:token) } + end + end + describe 'commits methods' do before do @project = Project.new diff --git a/spec/models/project_services/builds_email_service_spec.rb b/spec/models/project_services/builds_email_service_spec.rb index 7c23c2efccd..236df8f047d 100644 --- a/spec/models/project_services/builds_email_service_spec.rb +++ b/spec/models/project_services/builds_email_service_spec.rb @@ -1,76 +1,71 @@ require 'spec_helper' describe BuildsEmailService do - let(:build) { create(:ci_build) } - let(:data) { Gitlab::BuildDataBuilder.build(build) } - let!(:project) { create(:project, :public, ci_id: 1) } - let(:service) { described_class.new(project: project, active: true) } + let(:data) { Gitlab::BuildDataBuilder.build(create(:ci_build)) } + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:recipients) } + + context 'when pusher is added' do + before { subject.add_pusher = true } + + it { is_expected.not_to validate_presence_of(:recipients) } + end + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:recipients) } + end + end describe '#execute' do it 'sends email' do - service.recipients = 'test@gitlab.com' + subject.recipients = 'test@gitlab.com' data[:build_status] = 'failed' + expect(BuildEmailWorker).to receive(:perform_async) - service.execute(data) + + subject.execute(data) end it 'does not send email with succeeded build and notify_only_broken_builds on' do - expect(service).to receive(:notify_only_broken_builds).and_return(true) + expect(subject).to receive(:notify_only_broken_builds).and_return(true) data[:build_status] = 'success' + expect(BuildEmailWorker).not_to receive(:perform_async) - service.execute(data) + + subject.execute(data) end it 'does not send email with failed build and build_allow_failure is true' do data[:build_status] = 'failed' data[:build_allow_failure] = true + expect(BuildEmailWorker).not_to receive(:perform_async) - service.execute(data) + + subject.execute(data) end it 'does not send email with unknown build status' do data[:build_status] = 'foo' - expect(BuildEmailWorker).not_to receive(:perform_async) - service.execute(data) - end - it 'does not send email when recipients list is empty' do - service.recipients = ' ,, ' - data[:build_status] = 'failed' expect(BuildEmailWorker).not_to receive(:perform_async) - service.execute(data) - end - end - - describe 'validations' do - - context 'when pusher is not added' do - before { service.add_pusher = false } - - it 'does not allow empty recipient input' do - service.recipients = '' - expect(service.valid?).to be false - end - - it 'does allow non-empty recipient input' do - service.recipients = 'test@example.com' - expect(service.valid?).to be true - end + subject.execute(data) end - context 'when pusher is added' do - before { service.add_pusher = true } + it 'does not send email when recipients list is empty' do + subject.recipients = ' ,, ' + data[:build_status] = 'failed' - it 'does allow empty recipient input' do - service.recipients = '' - expect(service.valid?).to be true - end + expect(BuildEmailWorker).not_to receive(:perform_async) - it 'does allow non-empty recipient input' do - service.recipients = 'test@example.com' - expect(service.valid?).to be true - end + subject.execute(data) end end end diff --git a/spec/models/project_services/campfire_service_spec.rb b/spec/models/project_services/campfire_service_spec.rb new file mode 100644 index 00000000000..3e6da42803b --- /dev/null +++ b/spec/models/project_services/campfire_service_spec.rb @@ -0,0 +1,42 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe CampfireService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + end + end +end diff --git a/spec/models/project_services/custom_issue_tracker_service_spec.rb b/spec/models/project_services/custom_issue_tracker_service_spec.rb new file mode 100644 index 00000000000..ff976f8ec59 --- /dev/null +++ b/spec/models/project_services/custom_issue_tracker_service_spec.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe CustomIssueTrackerService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + it { is_expected.to validate_presence_of(:new_issue_url) } + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + it { is_expected.not_to validate_presence_of(:new_issue_url) } + end + end +end diff --git a/spec/models/project_services/drone_ci_service_spec.rb b/spec/models/project_services/drone_ci_service_spec.rb index a2cf68a9e38..3a8e67438fc 100644 --- a/spec/models/project_services/drone_ci_service_spec.rb +++ b/spec/models/project_services/drone_ci_service_spec.rb @@ -28,25 +28,18 @@ describe DroneCiService, models: true do describe 'validations' do context 'active' do - before { allow(subject).to receive(:activated?).and_return(true) } + before { subject.active = true } it { is_expected.to validate_presence_of(:token) } it { is_expected.to validate_presence_of(:drone_url) } - it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } - it { is_expected.to allow_value('http://ci.example.com').for(:drone_url) } - it { is_expected.not_to allow_value('this is not url').for(:drone_url) } - it { is_expected.not_to allow_value('http//noturl').for(:drone_url) } - it { is_expected.not_to allow_value('ftp://ci.example.com').for(:drone_url) } + it_behaves_like 'issue tracker service URL attribute', :drone_url end context 'inactive' do - before { allow(subject).to receive(:activated?).and_return(false) } + before { subject.active = false } it { is_expected.not_to validate_presence_of(:token) } it { is_expected.not_to validate_presence_of(:drone_url) } - it { is_expected.to allow_value('ewf9843kdnfdfs89234n').for(:token) } - it { is_expected.to allow_value('http://drone.example.com').for(:drone_url) } - it { is_expected.to allow_value('ftp://drone.example.com').for(:drone_url) } end end diff --git a/spec/models/project_services/emails_on_push_service_spec.rb b/spec/models/project_services/emails_on_push_service_spec.rb new file mode 100644 index 00000000000..e6f78898c82 --- /dev/null +++ b/spec/models/project_services/emails_on_push_service_spec.rb @@ -0,0 +1,17 @@ +require 'spec_helper' + +describe EmailsOnPushService do + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:recipients) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:recipients) } + end + end +end diff --git a/spec/models/external_wiki_service_spec.rb b/spec/models/project_services/external_wiki_service_spec.rb index d37978720bf..5fe5ea7d2df 100644 --- a/spec/models/external_wiki_service_spec.rb +++ b/spec/models/project_services/external_wiki_service_spec.rb @@ -28,13 +28,18 @@ describe ExternalWikiService, models: true do it { should have_one :service_hook } end - describe "Validations" do - context "active" do - before do - subject.active = true - end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:external_wiki_url) } + it_behaves_like 'issue tracker service URL attribute', :external_wiki_url + end + + context 'when service is inactive' do + before { subject.active = false } - it { should validate_presence_of :external_wiki_url } + it { is_expected.not_to validate_presence_of(:external_wiki_url) } end end diff --git a/spec/models/project_services/flowdock_service_spec.rb b/spec/models/project_services/flowdock_service_spec.rb index ff7fbcaa004..b7e627e6518 100644 --- a/spec/models/project_services/flowdock_service_spec.rb +++ b/spec/models/project_services/flowdock_service_spec.rb @@ -26,6 +26,20 @@ describe FlowdockService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + end + end + describe "Execute" do let(:user) { create(:user) } let(:project) { create(:project) } diff --git a/spec/models/project_services/gemnasium_service_spec.rb b/spec/models/project_services/gemnasium_service_spec.rb index ecb3ccb1673..a08f1ac229f 100644 --- a/spec/models/project_services/gemnasium_service_spec.rb +++ b/spec/models/project_services/gemnasium_service_spec.rb @@ -26,6 +26,22 @@ describe GemnasiumService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + it { is_expected.to validate_presence_of(:api_key) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + it { is_expected.not_to validate_presence_of(:api_key) } + end + end + describe "Execute" do let(:user) { create(:user) } let(:project) { create(:project) } diff --git a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb index 3518dbd1728..7a1f106d6e3 100644 --- a/spec/models/project_services/gitlab_issue_tracker_service_spec.rb +++ b/spec/models/project_services/gitlab_issue_tracker_service_spec.rb @@ -26,6 +26,20 @@ describe GitlabIssueTrackerService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + subject { described_class.new(project: create(:project), active: true) } + + it { is_expected.to validate_presence_of(:issues_url) } + it_behaves_like 'issue tracker service URL attribute', :issues_url + end + + context 'when service is inactive' do + subject { described_class.new(project: create(:project), active: false) } + + it { is_expected.not_to validate_presence_of(:issues_url) } + end + end describe 'project and issue urls' do let(:project) { create(:project) } diff --git a/spec/models/project_services/hipchat_service_spec.rb b/spec/models/project_services/hipchat_service_spec.rb index 91dd92b7c67..6fb5cad5011 100644 --- a/spec/models/project_services/hipchat_service_spec.rb +++ b/spec/models/project_services/hipchat_service_spec.rb @@ -26,6 +26,20 @@ describe HipchatService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + end + end + describe "Execute" do let(:hipchat) { HipchatService.new } let(:user) { create(:user, username: 'username') } @@ -152,7 +166,7 @@ describe HipchatService, models: true do obj_attr = merge_sample_data[:object_attributes] expect(message).to eq("#{user.name} opened " \ - "<a href=\"#{obj_attr[:url]}\">merge request ##{obj_attr["iid"]}</a> in " \ + "<a href=\"#{obj_attr[:url]}\">merge request !#{obj_attr["iid"]}</a> in " \ "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ "<b>Awesome merge request</b>" \ "<pre>please fix</pre>") @@ -202,7 +216,7 @@ describe HipchatService, models: true do title = data[:merge_request]['title'] expect(message).to eq("#{user.name} commented on " \ - "<a href=\"#{obj_attr[:url]}\">merge request ##{merge_id}</a> in " \ + "<a href=\"#{obj_attr[:url]}\">merge request !#{merge_id}</a> in " \ "<a href=\"#{project.web_url}\">#{project_name}</a>: " \ "<b>#{title}</b>" \ "<pre>merge request note</pre>") diff --git a/spec/models/project_services/irker_service_spec.rb b/spec/models/project_services/irker_service_spec.rb index b783b1a576e..4ee022a5171 100644 --- a/spec/models/project_services/irker_service_spec.rb +++ b/spec/models/project_services/irker_service_spec.rb @@ -29,14 +29,16 @@ describe IrkerService, models: true do end describe 'Validations' do - before do - subject.active = true - subject.properties['recipients'] = _recipients + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:recipients) } end - context 'active' do - let(:_recipients) { nil } - it { should validate_presence_of :recipients } + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:recipients) } end end diff --git a/spec/models/project_services/jira_service_spec.rb b/spec/models/project_services/jira_service_spec.rb index 2f8193170ae..5309cfb99ff 100644 --- a/spec/models/project_services/jira_service_spec.rb +++ b/spec/models/project_services/jira_service_spec.rb @@ -26,6 +26,30 @@ describe JiraService, models: true do it { is_expected.to have_one :service_hook } end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:api_url) } + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + it { is_expected.to validate_presence_of(:new_issue_url) } + it_behaves_like 'issue tracker service URL attribute', :api_url + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:api_url) } + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + it { is_expected.not_to validate_presence_of(:new_issue_url) } + end + end + describe "Execute" do let(:user) { create(:user) } let(:project) { create(:project) } @@ -72,7 +96,7 @@ describe JiraService, models: true do context "when a password was previously set" do before do - @jira_service = JiraService.create( + @jira_service = JiraService.create!( project: create(:project), properties: { api_url: 'http://jira.example.com/rest/api/2', diff --git a/spec/models/project_services/pivotaltracker_service_spec.rb b/spec/models/project_services/pivotaltracker_service_spec.rb new file mode 100644 index 00000000000..f37edd4d970 --- /dev/null +++ b/spec/models/project_services/pivotaltracker_service_spec.rb @@ -0,0 +1,42 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe PivotaltrackerService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:token) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:token) } + end + end +end diff --git a/spec/models/project_services/pushover_service_spec.rb b/spec/models/project_services/pushover_service_spec.rb index 96039f9491b..555d9757b47 100644 --- a/spec/models/project_services/pushover_service_spec.rb +++ b/spec/models/project_services/pushover_service_spec.rb @@ -27,14 +27,20 @@ describe PushoverService, models: true do end describe 'Validations' do - context 'active' do - before do - subject.active = true - end + context 'when service is active' do + before { subject.active = true } - it { is_expected.to validate_presence_of :api_key } - it { is_expected.to validate_presence_of :user_key } - it { is_expected.to validate_presence_of :priority } + it { is_expected.to validate_presence_of(:api_key) } + it { is_expected.to validate_presence_of(:user_key) } + it { is_expected.to validate_presence_of(:priority) } + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:api_key) } + it { is_expected.not_to validate_presence_of(:user_key) } + it { is_expected.not_to validate_presence_of(:priority) } end end diff --git a/spec/models/project_services/redmine_service_spec.rb b/spec/models/project_services/redmine_service_spec.rb new file mode 100644 index 00000000000..7d14f6e8280 --- /dev/null +++ b/spec/models/project_services/redmine_service_spec.rb @@ -0,0 +1,49 @@ +# == Schema Information +# +# Table name: services +# +# id :integer not null, primary key +# type :string(255) +# title :string(255) +# project_id :integer +# created_at :datetime +# updated_at :datetime +# active :boolean default(FALSE), not null +# properties :text +# template :boolean default(FALSE) +# push_events :boolean default(TRUE) +# issues_events :boolean default(TRUE) +# merge_requests_events :boolean default(TRUE) +# tag_push_events :boolean default(TRUE) +# note_events :boolean default(TRUE), not null +# + +require 'spec_helper' + +describe RedmineService, models: true do + describe 'Associations' do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } + + it { is_expected.to validate_presence_of(:project_url) } + it { is_expected.to validate_presence_of(:issues_url) } + it { is_expected.to validate_presence_of(:new_issue_url) } + it_behaves_like 'issue tracker service URL attribute', :project_url + it_behaves_like 'issue tracker service URL attribute', :issues_url + it_behaves_like 'issue tracker service URL attribute', :new_issue_url + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:project_url) } + it { is_expected.not_to validate_presence_of(:issues_url) } + it { is_expected.not_to validate_presence_of(:new_issue_url) } + end + end +end diff --git a/spec/models/project_services/slack_service/merge_message_spec.rb b/spec/models/project_services/slack_service/merge_message_spec.rb index dae8bd90922..224c7ceabe8 100644 --- a/spec/models/project_services/slack_service/merge_message_spec.rb +++ b/spec/models/project_services/slack_service/merge_message_spec.rb @@ -31,7 +31,7 @@ describe SlackService::MergeMessage, models: true do context 'open' do it 'returns a message regarding opening of merge requests' do expect(subject.pretext).to eq( - 'Test User opened <somewhere.com/merge_requests/100|merge request #100> '\ + 'Test User opened <somewhere.com/merge_requests/100|merge request !100> '\ 'in <somewhere.com|project_name>: *Issue title*') expect(subject.attachments).to be_empty end @@ -43,7 +43,7 @@ describe SlackService::MergeMessage, models: true do end it 'returns a message regarding closing of merge requests' do expect(subject.pretext).to eq( - 'Test User closed <somewhere.com/merge_requests/100|merge request #100> '\ + 'Test User closed <somewhere.com/merge_requests/100|merge request !100> '\ 'in <somewhere.com|project_name>: *Issue title*') expect(subject.attachments).to be_empty 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 06006b9a4f5..d37590cab75 100644 --- a/spec/models/project_services/slack_service/note_message_spec.rb +++ b/spec/models/project_services/slack_service/note_message_spec.rb @@ -63,7 +63,7 @@ describe SlackService::NoteMessage, models: true do it 'returns a message regarding notes on a merge request' do message = SlackService::NoteMessage.new(@args) expect(message.pretext).to eq("Test User commented on " \ - "<url|merge request #30> in <somewhere.com|project_name>: " \ + "<url|merge request !30> in <somewhere.com|project_name>: " \ "*merge request title*") expected_attachments = [ { diff --git a/spec/models/project_services/slack_service/wiki_page_message_spec.rb b/spec/models/project_services/slack_service/wiki_page_message_spec.rb new file mode 100644 index 00000000000..6ecab645b49 --- /dev/null +++ b/spec/models/project_services/slack_service/wiki_page_message_spec.rb @@ -0,0 +1,74 @@ +require 'spec_helper' + +describe SlackService::WikiPageMessage, models: true do + subject { described_class.new(args) } + + let(:args) do + { + user: { + name: 'Test User', + username: 'Test User' + }, + project_name: 'project_name', + project_url: 'somewhere.com', + object_attributes: { + title: 'Wiki page title', + url: 'url', + content: 'Wiki page description' + } + } + end + + describe '#pretext' do + context 'when :action == "create"' do + before { args[:object_attributes][:action] = 'create' } + + it 'returns a message that a new wiki page was created' do + expect(subject.pretext).to eq( + 'Test User created <url|wiki page> in <somewhere.com|project_name>: '\ + '*Wiki page title*') + end + end + + context 'when :action == "update"' do + before { args[:object_attributes][:action] = 'update' } + + it 'returns a message that a wiki page was updated' do + expect(subject.pretext).to eq( + 'Test User edited <url|wiki page> in <somewhere.com|project_name>: '\ + '*Wiki page title*') + end + end + end + + describe '#attachments' do + let(:color) { '#345' } + + context 'when :action == "create"' do + before { args[:object_attributes][:action] = 'create' } + + + it 'it returns the attachment for a new wiki page' do + expect(subject.attachments).to eq([ + { + text: "Wiki page description", + color: color, + } + ]) + end + end + + context 'when :action == "update"' do + before { args[:object_attributes][:action] = 'update' } + + it 'it returns the attachment for an updated wiki page' do + expect(subject.attachments).to eq([ + { + text: "Wiki page description", + color: color, + } + ]) + end + end + end +end diff --git a/spec/models/project_services/slack_service_spec.rb b/spec/models/project_services/slack_service_spec.rb index a9e0afad90f..a97b7560137 100644 --- a/spec/models/project_services/slack_service_spec.rb +++ b/spec/models/project_services/slack_service_spec.rb @@ -26,13 +26,18 @@ describe SlackService, models: true do it { is_expected.to have_one :service_hook } end - describe "Validations" do - context "active" do - before do - subject.active = true - end + describe 'Validations' do + context 'when service is active' do + before { subject.active = true } - it { is_expected.to validate_presence_of :webhook } + it { is_expected.to validate_presence_of(:webhook) } + it_behaves_like 'issue tracker service URL attribute', :webhook + end + + context 'when service is inactive' do + before { subject.active = false } + + it { is_expected.not_to validate_presence_of(:webhook) } end end @@ -75,6 +80,17 @@ describe SlackService, models: true do @merge_request = merge_service.execute @merge_sample_data = merge_service.hook_data(@merge_request, 'open') + + opts = { + title: "Awesome wiki_page", + content: "Some text describing some thing or another", + format: "md", + message: "user created page: Awesome wiki_page" + } + + wiki_page_service = WikiPages::CreateService.new(project, user, opts) + @wiki_page = wiki_page_service.execute + @wiki_page_sample_data = wiki_page_service.hook_data(@wiki_page, 'create') end it "should call Slack API for push events" do @@ -95,6 +111,12 @@ describe SlackService, models: true do expect(WebMock).to have_requested(:post, webhook_url).once end + it "should call Slack API for wiki page events" do + slack.execute(@wiki_page_sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).once + end + it 'should use the username as an option for slack when configured' do allow(slack).to receive(:username).and_return(username) expect(Slack::Notifier).to receive(:new). diff --git a/spec/models/project_services/teamcity_service_spec.rb b/spec/models/project_services/teamcity_service_spec.rb index bc7423cee69..ad24b895170 100644 --- a/spec/models/project_services/teamcity_service_spec.rb +++ b/spec/models/project_services/teamcity_service_spec.rb @@ -27,86 +27,51 @@ describe TeamcityService, models: true do end describe 'Validations' do - describe '#teamcity_url' do - it 'does not validate the presence of teamcity_url if service is not active' do - teamcity_service = service - teamcity_service.active = false - - expect(teamcity_service).not_to validate_presence_of(:teamcity_url) - end - - it 'validates the presence of teamcity_url if service is active' do - teamcity_service = service - teamcity_service.active = true - - expect(teamcity_service).to validate_presence_of(:teamcity_url) - end - end + subject { service } - describe '#build_type' do - it 'does not validate the presence of build_type if service is not active' do - teamcity_service = service - teamcity_service.active = false + context 'when service is active' do + before { subject.active = true } - expect(teamcity_service).not_to validate_presence_of(:build_type) - end + it { is_expected.to validate_presence_of(:build_type) } + it { is_expected.to validate_presence_of(:teamcity_url) } + it_behaves_like 'issue tracker service URL attribute', :teamcity_url - it 'validates the presence of build_type if service is active' do - teamcity_service = service - teamcity_service.active = true + describe '#username' do + it 'does not validate the presence of username if password is nil' do + subject.password = nil - expect(teamcity_service).to validate_presence_of(:build_type) - end - end + expect(subject).not_to validate_presence_of(:username) + end - describe '#username' do - it 'does not validate the presence of username if service is not active' do - teamcity_service = service - teamcity_service.active = false + it 'validates the presence of username if password is present' do + subject.password = 'secret' - expect(teamcity_service).not_to validate_presence_of(:username) + expect(subject).to validate_presence_of(:username) + end end - it 'does not validate the presence of username if username is nil' do - teamcity_service = service - teamcity_service.active = true - teamcity_service.password = nil + describe '#password' do + it 'does not validate the presence of password if username is nil' do + subject.username = nil - expect(teamcity_service).not_to validate_presence_of(:username) - end + expect(subject).not_to validate_presence_of(:password) + end - it 'validates the presence of username if service is active and username is present' do - teamcity_service = service - teamcity_service.active = true - teamcity_service.password = 'secret' + it 'validates the presence of password if username is present' do + subject.username = 'john' - expect(teamcity_service).to validate_presence_of(:username) + expect(subject).to validate_presence_of(:password) + end end end - describe '#password' do - it 'does not validate the presence of password if service is not active' do - teamcity_service = service - teamcity_service.active = false - - expect(teamcity_service).not_to validate_presence_of(:password) - end - - it 'does not validate the presence of password if username is nil' do - teamcity_service = service - teamcity_service.active = true - teamcity_service.username = nil - - expect(teamcity_service).not_to validate_presence_of(:password) - end - - it 'validates the presence of password if service is active and username is present' do - teamcity_service = service - teamcity_service.active = true - teamcity_service.username = 'john' + context 'when service is inactive' do + before { subject.active = false } - expect(teamcity_service).to validate_presence_of(:password) - end + it { is_expected.not_to validate_presence_of(:build_type) } + it { is_expected.not_to validate_presence_of(:teamcity_url) } + it { is_expected.not_to validate_presence_of(:username) } + it { is_expected.not_to validate_presence_of(:password) } end end diff --git a/spec/models/project_snippet_spec.rb b/spec/models/project_snippet_spec.rb index e0feb606f78..d9d7c0b0aaa 100644 --- a/spec/models/project_snippet_spec.rb +++ b/spec/models/project_snippet_spec.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: snippets -# -# id :integer not null, primary key -# title :string(255) -# content :text -# author_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# file_name :string(255) -# type :string(255) -# visibility_level :integer default(0), not null -# - require 'spec_helper' describe ProjectSnippet, models: true do diff --git a/spec/models/project_spec.rb b/spec/models/project_spec.rb index f29c389e094..f6e5b132643 100644 --- a/spec/models/project_spec.rb +++ b/spec/models/project_spec.rb @@ -1,43 +1,3 @@ -# == Schema Information -# -# Table name: projects -# -# id :integer not null, primary key -# name :string(255) -# path :string(255) -# description :text -# created_at :datetime -# updated_at :datetime -# creator_id :integer -# issues_enabled :boolean default(TRUE), not null -# wall_enabled :boolean default(TRUE), not null -# merge_requests_enabled :boolean default(TRUE), not null -# wiki_enabled :boolean default(TRUE), not null -# namespace_id :integer -# issues_tracker :string(255) default("gitlab"), not null -# issues_tracker_id :string(255) -# snippets_enabled :boolean default(TRUE), not null -# last_activity_at :datetime -# import_url :string(255) -# visibility_level :integer default(0), not null -# archived :boolean default(FALSE), not null -# avatar :string(255) -# import_status :string(255) -# repository_size :float default(0.0) -# star_count :integer default(0), not null -# import_type :string(255) -# import_source :string(255) -# commit_count :integer default(0) -# import_error :text -# ci_id :integer -# builds_enabled :boolean default(TRUE), not null -# shared_runners_enabled :boolean default(TRUE), not null -# runners_token :string -# build_coverage_regex :string -# build_allow_git_fetch :boolean default(TRUE), not null -# build_timeout :integer default(3600), not null -# - require 'spec_helper' describe Project, models: true do @@ -441,9 +401,22 @@ describe Project, models: true do describe :ci_commit do let(:project) { create :project } - let(:commit) { create :ci_commit, project: project } + let(:commit) { create :ci_commit, project: project, ref: 'master' } + + subject { project.ci_commit(commit.sha, 'master') } + + it { is_expected.to eq(commit) } - it { expect(project.ci_commit(commit.sha)).to eq(commit) } + context 'return latest' do + let(:commit2) { create :ci_commit, project: project, ref: 'master' } + + before do + commit + commit2 + end + + it { is_expected.to eq(commit2) } + end end describe :builds_enabled do @@ -706,11 +679,8 @@ describe Project, models: true do with('foo.wiki', project). and_return(wiki) - expect(repo).to receive(:expire_cache) - expect(repo).to receive(:expire_emptiness_caches) - - expect(wiki).to receive(:expire_cache) - expect(wiki).to receive(:expire_emptiness_caches) + expect(repo).to receive(:before_delete) + expect(wiki).to receive(:before_delete) project.expire_caches_before_rename('foo') end @@ -788,4 +758,18 @@ describe Project, models: true do end end end + + describe '#protected_branch?' do + let(:project) { create(:empty_project) } + + it 'returns true when a branch is a protected branch' do + project.protected_branches.create!(name: 'foo') + + expect(project.protected_branch?('foo')).to eq(true) + end + + it 'returns false when a branch is not a protected branch' do + expect(project.protected_branch?('foo')).to eq(false) + end + end end diff --git a/spec/models/protected_branch_spec.rb b/spec/models/protected_branch_spec.rb index 7e956cf6779..b523834c6e9 100644 --- a/spec/models/protected_branch_spec.rb +++ b/spec/models/protected_branch_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: protected_branches -# -# id :integer not null, primary key -# project_id :integer not null -# name :string(255) not null -# created_at :datetime -# updated_at :datetime -# developers_can_push :boolean default(FALSE), not null -# - require 'spec_helper' describe ProtectedBranch, models: true do diff --git a/spec/models/release_spec.rb b/spec/models/release_spec.rb index 72ecb442a36..527005b2b69 100644 --- a/spec/models/release_spec.rb +++ b/spec/models/release_spec.rb @@ -1,15 +1,3 @@ -# == Schema Information -# -# Table name: releases -# -# id :integer not null, primary key -# tag :string(255) -# description :text -# project_id :integer -# created_at :datetime -# updated_at :datetime -# - require 'rails_helper' RSpec.describe Release, type: :model do diff --git a/spec/models/repository_spec.rb b/spec/models/repository_spec.rb index c163001b7c1..34a13f9b5c9 100644 --- a/spec/models/repository_spec.rb +++ b/spec/models/repository_spec.rb @@ -132,25 +132,92 @@ describe Repository, models: true do it { expect(subject.basename).to eq('a/b/c') } end end + end + + describe "#changelog" do + before do + repository.send(:cache).expire(:changelog) + end + + it 'accepts changelog' do + expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('changelog')]) + + expect(repository.changelog.name).to eq('changelog') + end + + it 'accepts news instead of changelog' do + expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('news')]) + + expect(repository.changelog.name).to eq('news') + end + it 'accepts history instead of changelog' do + expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('history')]) + + expect(repository.changelog.name).to eq('history') + end + + it 'accepts changes instead of changelog' do + expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('changes')]) + + expect(repository.changelog.name).to eq('changes') + end + + it 'is case-insensitive' do + expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('CHANGELOG')]) + + expect(repository.changelog.name).to eq('CHANGELOG') + end end - describe "#license" do + describe "#license_blob" do before do - repository.send(:cache).expire(:license) + repository.send(:cache).expire(:license_blob) + repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'master') end - it 'test selection preference' do - files = [TestBlob.new('file'), TestBlob.new('license'), TestBlob.new('copying')] - expect(repository.tree).to receive(:blobs).and_return(files) + 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) + + expect(repository.license_blob).to be_nil + end + + it 'detects license file with no recognizable open-source license content' do + repository.commit_file(user, 'LICENSE', 'Copyright!', 'Add LICENSE', 'master', false) + + expect(repository.license_blob.name).to eq('LICENSE') + end + + %w[LICENSE LICENCE LiCensE LICENSE.md LICENSE.foo COPYING COPYING.md].each do |filename| + it "detects '#{filename}'" do + repository.commit_file(user, filename, Licensee::License.new('mit').content, "Add #{filename}", 'master', false) + + expect(repository.license_blob.name).to eq(filename) + end + end + end + + describe '#license_key' do + before do + repository.send(:cache).expire(:license_key) + repository.remove_file(user, 'LICENSE', 'Remove LICENSE', 'master') + end + + it 'returns nil when no license is detected' do + expect(repository.license_key).to be_nil + end + + it 'detects license file with no recognizable open-source license content' do + repository.commit_file(user, 'LICENSE', 'Copyright!', 'Add LICENSE', 'master', false) - expect(repository.license.name).to eq('license') + expect(repository.license_key).to be_nil end - it 'also accepts licence instead of license' do - expect(repository.tree).to receive(:blobs).and_return([TestBlob.new('licence')]) + it 'returns the license key' do + repository.commit_file(user, 'LICENSE', Licensee::License.new('mit').content, 'Add LICENSE', 'master', false) - expect(repository.license.name).to eq('licence') + expect(repository.license_key).to eq('mit') end end @@ -494,7 +561,7 @@ describe Repository, models: true do end describe :skip_merged_commit do - subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", nil, 100, 0, true).map{ |k| k.id } } + subject { repository.commits(Gitlab::Git::BRANCH_REF_PREFIX + "'test'", limit: 100, skip_merges: true).map{ |k| k.id } } it { is_expected.not_to include('e56497bb5f03a90a51293fc6d516788730953899') } end @@ -541,6 +608,41 @@ describe Repository, models: true do end end + describe '#cherry_pick' do + let(:conflict_commit) { repository.commit('c642fe9b8b9f28f9225d7ea953fe14e74748d53b') } + let(:pickable_commit) { repository.commit('7d3b0f7cff5f37573aea97cebfd5692ea1689924') } + let(:pickable_merge) { repository.commit('e56497bb5f03a90a51293fc6d516788730953899') } + + context 'when there is a conflict' do + it 'should abort the operation' do + expect(repository.cherry_pick(user, conflict_commit, 'master')).to eq(false) + end + end + + context 'when commit was already cherry-picked' do + it 'should abort the operation' do + repository.cherry_pick(user, pickable_commit, 'master') + + expect(repository.cherry_pick(user, pickable_commit, 'master')).to eq(false) + end + end + + context 'when commit can be cherry-picked' do + it 'should cherry-pick the changes' do + expect(repository.cherry_pick(user, pickable_commit, 'master')).to be_truthy + end + end + + context 'cherry-picking a merge commit' do + it 'should cherry-pick the changes' do + expect(repository.blob_at_branch('master', 'foo/bar/.gitkeep')).to be_nil + + repository.cherry_pick(user, pickable_merge, 'master') + expect(repository.blob_at_branch('master', 'foo/bar/.gitkeep')).not_to be_nil + end + end + end + describe '#before_delete' do describe 'when a repository does not exist' do before do @@ -693,6 +795,16 @@ describe Repository, models: true do end + describe "#copy_gitattributes" do + it 'returns true with a valid ref' do + expect(repository.copy_gitattributes('master')).to be_truthy + end + + it 'returns false with an invalid ref' do + expect(repository.copy_gitattributes('invalid')).to be_falsey + end + end + describe "#main_language" do it 'shows the main language of the project' do expect(repository.main_language).to eq("Ruby") @@ -746,13 +858,30 @@ describe Repository, models: true do end describe '#add_tag' do - it 'adds a tag' do - expect(repository).to receive(:before_push_tag) + context 'with a valid target' do + let(:user) { build_stubbed(:user) } - expect_any_instance_of(Gitlab::Shell).to receive(:add_tag). - with(repository.path_with_namespace, '8.5', 'master', 'foo') + it 'creates the tag using rugged' do + expect(repository.rugged.tags).to receive(:create). + with('8.5', repository.commit('master').id, + hash_including(message: 'foo', + tagger: hash_including(name: user.name, email: user.email))). + and_call_original - repository.add_tag('8.5', 'master', 'foo') + repository.add_tag(user, '8.5', 'master', 'foo') + end + + it 'returns a Gitlab::Git::Tag object' do + tag = repository.add_tag(user, '8.5', 'master', 'foo') + + expect(tag).to be_a(Gitlab::Git::Tag) + end + end + + context 'with an invalid target' do + it 'returns false' do + expect(repository.add_tag(user, '8.5', 'bar', 'foo')).to be false + end end end @@ -910,9 +1039,32 @@ describe Repository, models: true do end end + describe '.clean_old_archives' do + let(:path) { Gitlab.config.gitlab.repository_downloads_path } + + context 'when the downloads directory does not exist' do + it 'does not remove any archives' do + expect(File).to receive(:directory?).with(path).and_return(false) + + expect(Gitlab::Popen).not_to receive(:popen) + + described_class.clean_old_archives + end + end + + context 'when the downloads directory exists' do + it 'removes old archives' do + expect(File).to receive(:directory?).with(path).and_return(true) + + expect(Gitlab::Popen).to receive(:popen) + + described_class.clean_old_archives + end + end + end + def create_remote_branch(remote_name, branch_name, target) rugged = repository.rugged rugged.references.create("refs/remotes/#{remote_name}/#{branch_name}", target) end - end diff --git a/spec/models/service_spec.rb b/spec/models/service_spec.rb index 173628c08d0..8592e112c50 100644 --- a/spec/models/service_spec.rb +++ b/spec/models/service_spec.rb @@ -1,24 +1,3 @@ -# == Schema Information -# -# Table name: services -# -# id :integer not null, primary key -# type :string(255) -# title :string(255) -# project_id :integer -# created_at :datetime -# updated_at :datetime -# active :boolean default(FALSE), not null -# properties :text -# template :boolean default(FALSE) -# push_events :boolean default(TRUE) -# issues_events :boolean default(TRUE) -# merge_requests_events :boolean default(TRUE) -# tag_push_events :boolean default(TRUE) -# note_events :boolean default(TRUE), not null -# build_events :boolean default(FALSE), not null -# - require 'spec_helper' describe Service, models: true do diff --git a/spec/models/snippet_spec.rb b/spec/models/snippet_spec.rb index 5077ac7b62b..7a613e360d4 100644 --- a/spec/models/snippet_spec.rb +++ b/spec/models/snippet_spec.rb @@ -1,19 +1,3 @@ -# == Schema Information -# -# Table name: snippets -# -# id :integer not null, primary key -# title :string(255) -# content :text -# author_id :integer not null -# project_id :integer -# created_at :datetime -# updated_at :datetime -# file_name :string(255) -# type :string(255) -# visibility_level :integer default(0), not null -# - require 'spec_helper' describe Snippet, models: true do diff --git a/spec/models/todo_spec.rb b/spec/models/todo_spec.rb index d9b86b9368f..623b82c01d8 100644 --- a/spec/models/todo_spec.rb +++ b/spec/models/todo_spec.rb @@ -1,21 +1,3 @@ -# == Schema Information -# -# Table name: todos -# -# id :integer not null, primary key -# user_id :integer not null -# project_id :integer not null -# target_id :integer -# target_type :string not null -# author_id :integer -# action :integer not null -# state :string not null -# created_at :datetime -# updated_at :datetime -# note_id :integer -# commit_id :string -# - require 'spec_helper' describe Todo, models: true do diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index de1a233dfff..b60725756cc 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,66 +1,3 @@ -# == Schema Information -# -# Table name: users -# -# id :integer not null, primary key -# email :string(255) default(""), not null -# encrypted_password :string(255) default(""), not null -# reset_password_token :string(255) -# reset_password_sent_at :datetime -# remember_created_at :datetime -# sign_in_count :integer default(0) -# current_sign_in_at :datetime -# last_sign_in_at :datetime -# current_sign_in_ip :string(255) -# last_sign_in_ip :string(255) -# created_at :datetime -# updated_at :datetime -# name :string(255) -# admin :boolean default(FALSE), not null -# projects_limit :integer default(10) -# skype :string(255) default(""), not null -# linkedin :string(255) default(""), not null -# twitter :string(255) default(""), not null -# authentication_token :string(255) -# theme_id :integer default(1), not null -# bio :string(255) -# failed_attempts :integer default(0) -# locked_at :datetime -# username :string(255) -# can_create_group :boolean default(TRUE), not null -# can_create_team :boolean default(TRUE), not null -# state :string(255) -# color_scheme_id :integer default(1), not null -# notification_level :integer default(1), not null -# password_expires_at :datetime -# created_by_id :integer -# last_credential_check_at :datetime -# avatar :string(255) -# confirmation_token :string(255) -# confirmed_at :datetime -# confirmation_sent_at :datetime -# unconfirmed_email :string(255) -# hide_no_ssh_key :boolean default(FALSE) -# website_url :string(255) default(""), not null -# notification_email :string(255) -# hide_no_password :boolean default(FALSE) -# password_automatically_set :boolean default(FALSE) -# location :string(255) -# encrypted_otp_secret :string(255) -# encrypted_otp_secret_iv :string(255) -# encrypted_otp_secret_salt :string(255) -# otp_required_for_login :boolean default(FALSE), not null -# otp_backup_codes :text -# public_email :string(255) default(""), not null -# dashboard :integer default(0) -# project_view :integer default(0) -# consumed_timestep :integer -# layout :integer default(0) -# hide_project_limit :boolean default(FALSE) -# unlock_token :string -# otp_grace_period_started_at :datetime -# - require 'spec_helper' describe User, models: true do diff --git a/spec/requests/api/builds_spec.rb b/spec/requests/api/builds_spec.rb index 967c34800d0..5ead735be48 100644 --- a/spec/requests/api/builds_spec.rb +++ b/spec/requests/api/builds_spec.rb @@ -59,7 +59,7 @@ describe API::API, api: true do describe 'GET /projects/:id/repository/commits/:sha/builds' do before do - project.ensure_ci_commit(commit.sha) + project.ensure_ci_commit(commit.sha, 'master') get api("/projects/#{project.id}/repository/commits/#{commit.sha}/builds", api_user) end diff --git a/spec/requests/api/commit_status_spec.rb b/spec/requests/api/commit_status_spec.rb index 429a24109fd..f3785b19362 100644 --- a/spec/requests/api/commit_status_spec.rb +++ b/spec/requests/api/commit_status_spec.rb @@ -16,7 +16,8 @@ describe API::CommitStatus, api: true do let(:get_url) { "/projects/#{project.id}/repository/commits/#{sha}/statuses" } context 'ci commit exists' do - let!(:ci_commit) { project.ensure_ci_commit(commit.id) } + let!(:master) { project.ci_commits.create(sha: commit.id, ref: 'master') } + let!(:develop) { project.ci_commits.create(sha: commit.id, ref: 'develop') } it_behaves_like 'a paginated resources' do let(:request) { get api(get_url, reporter) } @@ -25,16 +26,16 @@ describe API::CommitStatus, api: true do context "reporter user" do let(:statuses_id) { json_response.map { |status| status['id'] } } - def create_status(opts = {}) - create(:commit_status, { commit: ci_commit }.merge(opts)) + def create_status(commit, opts = {}) + create(:commit_status, { commit: commit, ref: commit.ref }.merge(opts)) end - let!(:status1) { create_status(status: 'running') } - let!(:status2) { create_status(name: 'coverage', status: 'pending') } - let!(:status3) { create_status(ref: 'develop', status: 'running', allow_failure: true) } - let!(:status4) { create_status(name: 'coverage', status: 'success') } - let!(:status5) { create_status(name: 'coverage', ref: 'develop', status: 'success') } - let!(:status6) { create_status(status: 'success') } + let!(:status1) { create_status(master, status: 'running') } + let!(:status2) { create_status(master, name: 'coverage', status: 'pending') } + let!(:status3) { create_status(develop, status: 'running', allow_failure: true) } + let!(:status4) { create_status(master, name: 'coverage', status: 'success') } + let!(:status5) { create_status(develop, name: 'coverage', status: 'success') } + let!(:status6) { create_status(master, status: 'success') } context 'latest commit statuses' do before { get api(get_url, reporter) } diff --git a/spec/requests/api/commits_spec.rb b/spec/requests/api/commits_spec.rb index 7ff21175c1b..cb82ca7802d 100644 --- a/spec/requests/api/commits_spec.rb +++ b/spec/requests/api/commits_spec.rb @@ -32,6 +32,41 @@ describe API::API, api: true do expect(response.status).to eq(401) end end + + context "since optional parameter" do + it "should return project commits since provided parameter" do + commits = project.repository.commits("master") + since = commits.second.created_at + + get api("/projects/#{project.id}/repository/commits?since=#{since.utc.iso8601}", user) + + expect(json_response.size).to eq 2 + expect(json_response.first["id"]).to eq(commits.first.id) + expect(json_response.second["id"]).to eq(commits.second.id) + end + end + + context "until optional parameter" do + it "should return project commits until provided parameter" do + commits = project.repository.commits("master") + before = commits.second.created_at + + get api("/projects/#{project.id}/repository/commits?until=#{before.utc.iso8601}", user) + + expect(json_response.size).to eq(commits.size - 1) + expect(json_response.first["id"]).to eq(commits.second.id) + expect(json_response.second["id"]).to eq(commits.third.id) + end + end + + context "invalid xmlschema date parameters" do + it "should return an invalid parameter error message" do + get api("/projects/#{project.id}/repository/commits?since=invalid-date", user) + + expect(response.status).to eq(400) + expect(json_response['message']).to include "\"since\" must be a timestamp in ISO 8601 format" + end + end end describe "GET /projects:id/repository/commits/:sha" do @@ -48,14 +83,14 @@ describe API::API, api: true do expect(response.status).to eq(404) end - it "should return not_found for CI status" do + it "should return nil for commit without CI" do get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) expect(response.status).to eq(200) - expect(json_response['status']).to eq('not_found') + expect(json_response['status']).to be_nil end it "should return status for CI" do - ci_commit = project.ensure_ci_commit(project.repository.commit.sha) + ci_commit = project.ensure_ci_commit(project.repository.commit.sha, 'master') get api("/projects/#{project.id}/repository/commits/#{project.repository.commit.id}", user) expect(response.status).to eq(200) expect(json_response['status']).to eq(ci_commit.status) diff --git a/spec/requests/api/issues_spec.rb b/spec/requests/api/issues_spec.rb index f88e39cad9e..9dd43f4fab3 100644 --- a/spec/requests/api/issues_spec.rb +++ b/spec/requests/api/issues_spec.rb @@ -39,6 +39,7 @@ describe API::API, api: true do let!(:empty_milestone) do create(:milestone, title: '2.0.0', project: project) end + let!(:note) { create(:note_on_issue, author: user, project: project, noteable: issue) } before { project.team << [user, :reporter] } @@ -232,8 +233,28 @@ describe API::API, api: true do end describe "GET /projects/:id/issues/:issue_id" do + it 'exposes known attributes' do + get api("/projects/#{project.id}/issues/#{issue.id}", user) + + expect(response.status).to eq(200) + expect(json_response['id']).to eq(issue.id) + expect(json_response['iid']).to eq(issue.iid) + expect(json_response['project_id']).to eq(issue.project.id) + expect(json_response['title']).to eq(issue.title) + expect(json_response['description']).to eq(issue.description) + expect(json_response['state']).to eq(issue.state) + expect(json_response['created_at']).to be_present + expect(json_response['updated_at']).to be_present + expect(json_response['labels']).to eq(issue.label_names) + expect(json_response['milestone']).to be_a Hash + expect(json_response['assignee']).to be_a Hash + expect(json_response['author']).to be_a Hash + expect(json_response['user_notes_count']).to be(1) + end + it "should return a project issue by id" do get api("/projects/#{project.id}/issues/#{issue.id}", user) + expect(response.status).to eq(200) expect(json_response['title']).to eq(issue.title) expect(json_response['iid']).to eq(issue.iid) diff --git a/spec/requests/api/licenses_spec.rb b/spec/requests/api/licenses_spec.rb new file mode 100644 index 00000000000..c17dcb222a9 --- /dev/null +++ b/spec/requests/api/licenses_spec.rb @@ -0,0 +1,136 @@ +require 'spec_helper' + +describe API::Licenses, api: true do + include ApiHelpers + + describe 'Entity' do + before { get api('/licenses/mit') } + + it { expect(json_response['key']).to eq('mit') } + it { expect(json_response['name']).to eq('MIT License') } + it { expect(json_response['nickname']).to be_nil } + it { expect(json_response['popular']).to be true } + it { expect(json_response['html_url']).to eq('http://choosealicense.com/licenses/mit/') } + it { expect(json_response['source_url']).to eq('https://opensource.org/licenses/MIT') } + it { expect(json_response['description']).to include('A permissive license that is short and to the point.') } + it { expect(json_response['conditions']).to eq(%w[include-copyright]) } + it { expect(json_response['permissions']).to eq(%w[commercial-use modifications distribution private-use]) } + it { expect(json_response['limitations']).to eq(%w[no-liability]) } + it { expect(json_response['content']).to include('The MIT License (MIT)') } + end + + describe 'GET /licenses' do + it 'returns a list of available license templates' do + get api('/licenses') + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(15) + expect(json_response.map { |l| l['key'] }).to include('agpl-3.0') + end + + describe 'the popular parameter' do + context 'with popular=1' do + it 'returns a list of available popular license templates' do + get api('/licenses?popular=1') + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(3) + expect(json_response.map { |l| l['key'] }).to include('apache-2.0') + end + end + end + end + + describe 'GET /licenses/:key' do + context 'with :project and :fullname given' do + before do + get api("/licenses/#{license_type}?project=My+Awesome+Project&fullname=Anton+#{license_type.upcase}") + end + + context 'for the mit license' do + let(:license_type) { 'mit' } + + it 'returns the license text' do + expect(json_response['content']).to include('The MIT License (MIT)') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include('Copyright (c) 2016 Anton') + end + end + + context 'for the agpl-3.0 license' do + let(:license_type) { 'agpl-3.0' } + + it 'returns the license text' do + expect(json_response['content']).to include('GNU AFFERO GENERAL PUBLIC LICENSE') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include('My Awesome Project') + expect(json_response['content']).to include('Copyright (C) 2016 Anton') + end + end + + context 'for the gpl-3.0 license' do + let(:license_type) { 'gpl-3.0' } + + it 'returns the license text' do + expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include('My Awesome Project') + expect(json_response['content']).to include('Copyright (C) 2016 Anton') + end + end + + context 'for the gpl-2.0 license' do + let(:license_type) { 'gpl-2.0' } + + it 'returns the license text' do + expect(json_response['content']).to include('GNU GENERAL PUBLIC LICENSE') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include('My Awesome Project') + expect(json_response['content']).to include('Copyright (C) 2016 Anton') + end + end + + context 'for the apache-2.0 license' do + let(:license_type) { 'apache-2.0' } + + it 'returns the license text' do + expect(json_response['content']).to include('Apache License') + end + + it 'replaces placeholder values' do + expect(json_response['content']).to include('Copyright 2016 Anton') + end + end + + context 'for an uknown license' do + let(:license_type) { 'muth-over9000' } + + it 'returns a 404' do + expect(response.status).to eq(404) + end + end + end + + context 'with no :fullname given' do + context 'with an authenticated user' do + let(:user) { create(:user) } + + 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}") + end + end + end + end +end diff --git a/spec/requests/api/merge_requests_spec.rb b/spec/requests/api/merge_requests_spec.rb index 1fa7e76894f..4b0111df149 100644 --- a/spec/requests/api/merge_requests_spec.rb +++ b/spec/requests/api/merge_requests_spec.rb @@ -113,6 +113,34 @@ describe API::API, api: true do end describe "GET /projects/:id/merge_requests/:merge_request_id" do + it 'exposes known attributes' do + get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) + + expect(response.status).to eq(200) + expect(json_response['id']).to eq(merge_request.id) + expect(json_response['iid']).to eq(merge_request.iid) + expect(json_response['project_id']).to eq(merge_request.project.id) + expect(json_response['title']).to eq(merge_request.title) + expect(json_response['description']).to eq(merge_request.description) + expect(json_response['state']).to eq(merge_request.state) + expect(json_response['created_at']).to be_present + expect(json_response['updated_at']).to be_present + expect(json_response['labels']).to eq(merge_request.label_names) + expect(json_response['milestone']).to be_nil + expect(json_response['assignee']).to be_a Hash + expect(json_response['author']).to be_a Hash + expect(json_response['target_branch']).to eq(merge_request.target_branch) + expect(json_response['source_branch']).to eq(merge_request.source_branch) + expect(json_response['upvotes']).to eq(0) + expect(json_response['downvotes']).to eq(0) + expect(json_response['source_project_id']).to eq(merge_request.source_project.id) + expect(json_response['target_project_id']).to eq(merge_request.target_project.id) + expect(json_response['work_in_progress']).to be_falsy + expect(json_response['merge_when_build_succeeds']).to be_falsy + expect(json_response['merge_status']).to eq('can_be_merged') + expect(json_response['user_notes_count']).to be(2) + end + it "should return merge_request" do get api("/projects/#{project.id}/merge_requests/#{merge_request.id}", user) expect(response.status).to eq(200) diff --git a/spec/requests/api/milestones_spec.rb b/spec/requests/api/milestones_spec.rb index 344f0fe0b7f..241995041bb 100644 --- a/spec/requests/api/milestones_spec.rb +++ b/spec/requests/api/milestones_spec.rb @@ -127,7 +127,7 @@ describe API::API, api: true do describe 'GET /projects/:id/milestones/:milestone_id/issues' do before do - milestone.issues << create(:issue) + milestone.issues << create(:issue, project: project) end it 'should return project issues for a particular milestone' do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues", user) @@ -140,5 +140,34 @@ describe API::API, api: true do get api("/projects/#{project.id}/milestones/#{milestone.id}/issues") expect(response.status).to eq(401) end + + describe 'confidential issues' do + let(:public_project) { create(:project, :public) } + let(:milestone) { create(:milestone, project: public_project) } + let(:issue) { create(:issue, project: public_project) } + let(:confidential_issue) { create(:issue, confidential: true, project: public_project) } + before do + public_project.team << [user, :developer] + milestone.issues << issue << confidential_issue + end + + it 'returns confidential issues to team members' do + get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", user) + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(2) + expect(json_response.map { |issue| issue['id'] }).to include(issue.id, confidential_issue.id) + end + + it 'does not return confidential issues to regular users' do + get api("/projects/#{public_project.id}/milestones/#{milestone.id}/issues", create(:user)) + + expect(response.status).to eq(200) + expect(json_response).to be_an Array + expect(json_response.size).to eq(1) + expect(json_response.map { |issue| issue['id'] }).to include(issue.id) + end + end end end diff --git a/spec/requests/api/notes_spec.rb b/spec/requests/api/notes_spec.rb index ec9eda0a2ed..49091fc0f49 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, 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) } @@ -45,7 +45,7 @@ describe API::API, api: true do end it "should return a 404 error when issue id not found" do - get api("/projects/#{project.id}/issues/123/notes", user) + get api("/projects/#{project.id}/issues/12345/notes", user) expect(response.status).to eq(404) end @@ -106,7 +106,7 @@ describe API::API, api: true do end it "should return a 404 error if issue note not found" do - get api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user) + get api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) expect(response.status).to eq(404) end @@ -134,7 +134,7 @@ describe API::API, api: true do end it "should return a 404 error if snippet note not found" do - get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/123", user) + get api("/projects/#{project.id}/snippets/#{snippet.id}/notes/12345", user) expect(response.status).to eq(404) end end @@ -191,6 +191,27 @@ describe API::API, api: true do expect(response.status).to eq(401) end end + + context 'when user does not have access to create noteable' do + let(:private_issue) { create(:issue, project: create(:project, :private)) } + + ## + # We are posting to project user has access to, but we use issue id + # from a different project, see #15577 + # + before do + post api("/projects/#{project.id}/issues/#{private_issue.id}/notes", user), + body: 'Hi!' + end + + it 'responds with 500' do + expect(response.status).to eq 500 + end + + it 'does not create new note' do + expect(private_issue.notes.reload).to be_empty + end + end end describe "POST /projects/:id/noteable/:noteable_id/notes to test observer on create" do @@ -211,7 +232,7 @@ describe API::API, api: true do end it 'should return a 404 error when note id not found' do - put api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user), + put api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user), body: 'Hello!' expect(response.status).to eq(404) end @@ -233,7 +254,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/123", user), body: "Hello!" + "notes/12345", user), body: "Hello!" expect(response.status).to eq(404) end end @@ -248,7 +269,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/123", user), body: "Hello!" + "notes/12345", user), body: "Hello!" expect(response.status).to eq(404) end end @@ -268,7 +289,7 @@ describe API::API, api: true do end it 'returns a 404 error when note id not found' do - delete api("/projects/#{project.id}/issues/#{issue.id}/notes/123", user) + delete api("/projects/#{project.id}/issues/#{issue.id}/notes/12345", user) expect(response.status).to eq(404) end @@ -288,7 +309,7 @@ describe API::API, api: true do it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/snippets/#{snippet.id}/"\ - "notes/123", user) + "notes/12345", user) expect(response.status).to eq(404) end @@ -308,7 +329,7 @@ describe API::API, api: true do it 'returns a 404 error when note id not found' do delete api("/projects/#{project.id}/merge_requests/"\ - "#{merge_request.id}/notes/123", user) + "#{merge_request.id}/notes/12345", user) expect(response.status).to eq(404) end diff --git a/spec/requests/api/project_hooks_spec.rb b/spec/requests/api/project_hooks_spec.rb index 142b637d291..ffb93bbb120 100644 --- a/spec/requests/api/project_hooks_spec.rb +++ b/spec/requests/api/project_hooks_spec.rb @@ -148,14 +148,24 @@ describe API::API, 'ProjectHooks', api: true do expect(response.status).to eq(200) end - it "should return success when deleting non existent hook" do + it "should return a 404 error when deleting non existent hook" do delete api("/projects/#{project.id}/hooks/42", user) - expect(response.status).to eq(200) + expect(response.status).to eq(404) end it "should return a 405 error if hook id not given" do delete api("/projects/#{project.id}/hooks", user) expect(response.status).to eq(405) end + + it "shold return a 404 if a user attempts to delete project hooks he/she does not own" do + test_user = create(:user) + other_project = create(:project) + other_project.team << [test_user, :master] + + delete api("/projects/#{other_project.id}/hooks/#{hook.id}", test_user) + expect(response.status).to eq(404) + expect(WebHook.exists?(hook.id)).to be_truthy + end end end diff --git a/spec/requests/api/project_snippets_spec.rb b/spec/requests/api/project_snippets_spec.rb index 3722ddf5a33..9706d060cfa 100644 --- a/spec/requests/api/project_snippets_spec.rb +++ b/spec/requests/api/project_snippets_spec.rb @@ -15,4 +15,91 @@ describe API::API, api: true do expect(json_response['expires_at']).to be_nil end end + + describe 'GET /projects/:project_id/snippets/' do + it 'all snippets available to team member' do + project = create(:project, :public) + user = create(:user) + project.team << [user, :developer] + public_snippet = create(:project_snippet, :public, project: project) + internal_snippet = create(:project_snippet, :internal, project: project) + private_snippet = create(:project_snippet, :private, project: project) + + get api("/projects/#{project.id}/snippets/", user) + + expect(response.status).to eq(200) + expect(json_response.size).to eq(3) + expect(json_response.map{ |snippet| snippet['id']} ).to include(public_snippet.id, internal_snippet.id, private_snippet.id) + end + + it 'hides private snippets from regular user' do + project = create(:project, :public) + user = create(:user) + create(:project_snippet, :private, project: project) + + get api("/projects/#{project.id}/snippets/", user) + expect(response.status).to eq(200) + expect(json_response.size).to eq(0) + end + end + + describe 'POST /projects/:project_id/snippets/' do + it 'creates a new snippet' do + admin = create(:admin) + project = create(:project) + params = { + title: 'Test Title', + file_name: 'test.rb', + code: 'puts "hello world"', + visibility_level: Gitlab::VisibilityLevel::PUBLIC + } + + post api("/projects/#{project.id}/snippets/", admin), params + + expect(response.status).to eq(201) + snippet = ProjectSnippet.find(json_response['id']) + expect(snippet.content).to eq(params[:code]) + expect(snippet.title).to eq(params[:title]) + expect(snippet.file_name).to eq(params[:file_name]) + expect(snippet.visibility_level).to eq(params[:visibility_level]) + end + end + + describe 'PUT /projects/:project_id/snippets/:id/' do + it 'updates snippet' do + admin = create(:admin) + snippet = create(:project_snippet, author: admin) + new_content = 'New content' + + put api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin), code: new_content + + expect(response.status).to eq(200) + snippet.reload + expect(snippet.content).to eq(new_content) + end + end + + describe 'DELETE /projects/:project_id/snippets/:id/' do + it 'deletes snippet' do + admin = create(:admin) + snippet = create(:project_snippet, author: admin) + + delete api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/", admin) + + expect(response.status).to eq(200) + end + end + + describe 'GET /projects/:project_id/snippets/:id/raw' do + it 'returns raw text' do + admin = create(:admin) + snippet = create(:project_snippet, author: admin) + + get api("/projects/#{snippet.project.id}/snippets/#{snippet.id}/raw", admin) + + expect(response.status).to eq(200) + expect(response.content_type).to eq 'text/plain' + expect(response.body).to eq(snippet.content) + end + end end diff --git a/spec/requests/api/projects_spec.rb b/spec/requests/api/projects_spec.rb index fccd08bd6da..66193eac051 100644 --- a/spec/requests/api/projects_spec.rb +++ b/spec/requests/api/projects_spec.rb @@ -11,7 +11,7 @@ describe API::API, api: true do 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, author: user, project: project, title: 'example') } + 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) } diff --git a/spec/requests/api/tags_spec.rb b/spec/requests/api/tags_spec.rb index 9f9c3b1cf4c..12e170b232f 100644 --- a/spec/requests/api/tags_spec.rb +++ b/spec/requests/api/tags_spec.rb @@ -32,9 +32,11 @@ describe API::API, api: true do it "should return an array of project tags with release info" do get api("/projects/#{project.id}/repository/tags", user) + expect(response.status).to eq(200) expect(json_response).to be_an Array expect(json_response.first['name']).to eq(tag_name) + expect(json_response.first['message']).to eq('Version 1.1.0') expect(json_response.first['release']['description']).to eq(description) end end @@ -145,7 +147,7 @@ describe API::API, api: true do tag_name: 'v8.0.0', ref: 'master' expect(response.status).to eq(400) - expect(json_response['message']).to eq('Tag already exists') + expect(json_response['message']).to eq('Tag v8.0.0 already exists') end it 'should return 400 if ref name is invalid' do @@ -153,7 +155,7 @@ describe API::API, api: true do tag_name: 'mytag', ref: 'foo' expect(response.status).to eq(400) - expect(json_response['message']).to eq('Invalid reference name') + expect(json_response['message']).to eq('Target foo is invalid') end end diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb index 679227bf881..40b24c125b5 100644 --- a/spec/requests/api/users_spec.rb +++ b/spec/requests/api/users_spec.rb @@ -20,6 +20,24 @@ describe API::API, api: true do end context "when authenticated" do + #These specs are written just in case API authentication is not required anymore + context "when public level is restricted" do + before do + stub_application_setting(restricted_visibility_levels: [Gitlab::VisibilityLevel::PUBLIC]) + allow_any_instance_of(API::Helpers).to receive(:authenticate!).and_return(true) + end + + it "renders 403" do + get api("/users") + expect(response.status).to eq(403) + end + + it "renders 404" do + get api("/users/#{user.id}") + expect(response.status).to eq(404) + end + end + it "should return an array of users" do get api("/users", user) expect(response.status).to eq(200) diff --git a/spec/requests/ci/api/builds_spec.rb b/spec/requests/ci/api/builds_spec.rb index 57d7eb927fd..dfd361a2cdd 100644 --- a/spec/requests/ci/api/builds_spec.rb +++ b/spec/requests/ci/api/builds_spec.rb @@ -20,8 +20,8 @@ describe Ci::API::API do describe "POST /builds/register" do it "should start a build" do - commit = FactoryGirl.create(:ci_commit, project: project) - commit.create_builds('master', false, nil) + commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master') + commit.create_builds(nil) build = commit.builds.first post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -56,8 +56,8 @@ describe Ci::API::API do end it "returns options" do - commit = FactoryGirl.create(:ci_commit, project: project) - commit.create_builds('master', false, nil) + commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master') + commit.create_builds(nil) post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -66,8 +66,8 @@ describe Ci::API::API do end it "returns variables" do - commit = FactoryGirl.create(:ci_commit, project: project) - commit.create_builds('master', false, nil) + commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master') + commit.create_builds(nil) project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -83,10 +83,10 @@ describe Ci::API::API do it "returns variables for triggers" do trigger = FactoryGirl.create(:ci_trigger, project: project) - commit = FactoryGirl.create(:ci_commit, project: project) + commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master') trigger_request = FactoryGirl.create(:ci_trigger_request_with_variables, commit: commit, trigger: trigger) - commit.create_builds('master', false, nil, trigger_request) + commit.create_builds(nil, trigger_request) project.variables << Ci::Variable.new(key: "SECRET_KEY", value: "secret_value") post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -103,8 +103,8 @@ describe Ci::API::API do end it "returns dependent builds" do - commit = FactoryGirl.create(:ci_commit, project: project) - commit.create_builds('master', false, nil, nil) + commit = FactoryGirl.create(:ci_commit, project: project, ref: 'master') + commit.create_builds(nil, nil) commit.builds.where(stage: 'test').each(&:success) post ci_api("/builds/register"), token: runner.token, info: { platform: :darwin } @@ -156,6 +156,52 @@ describe Ci::API::API do end end + describe 'PATCH /builds/:id/trace.txt' do + let(:build) { create(:ci_build, :trace, runner_id: runner.id) } + let(:headers) { { Ci::API::Helpers::BUILD_TOKEN_HEADER => build.token, 'Content-Type' => 'text/plain' } } + let(:headers_with_range) { headers.merge({ 'Content-Range' => '11-20' }) } + + before do + build.run! + patch ci_api("/builds/#{build.id}/trace.txt"), ' appended', headers_with_range + end + + context 'when request is valid' do + it { expect(response.status).to eq 202 } + it { expect(build.reload.trace).to eq 'BUILD TRACE appended' } + it { expect(response.header).to have_key 'Range' } + it { expect(response.header).to have_key 'Build-Status' } + end + + context 'when content-range start is too big' do + let(:headers_with_range) { headers.merge({ 'Content-Range' => '15-20' }) } + + it { expect(response.status).to eq 416 } + it { expect(response.header).to have_key 'Range' } + it { expect(response.header['Range']).to eq '0-11' } + end + + context 'when content-range start is too small' do + let(:headers_with_range) { headers.merge({ 'Content-Range' => '8-20' }) } + + it { expect(response.status).to eq 416 } + it { expect(response.header).to have_key 'Range' } + it { expect(response.header['Range']).to eq '0-11' } + end + + context 'when Content-Range header is missing' do + let(:headers_with_range) { headers.merge({}) } + + it { expect(response.status).to eq 400 } + end + + context 'when build has been errased' do + let(:build) { create(:ci_build, runner_id: runner.id, erased_at: Time.now) } + + it { expect(response.status).to eq 403 } + end + end + context "Artifacts" do let(:file_upload) { fixture_file_upload(Rails.root + 'spec/fixtures/banana_sample.gif', 'image/gif') } let(:file_upload2) { fixture_file_upload(Rails.root + 'spec/fixtures/dk.png', 'image/gif') } diff --git a/spec/services/ci/create_builds_service_spec.rb b/spec/services/ci/create_builds_service_spec.rb index 1fca3628686..ecc3a88a262 100644 --- a/spec/services/ci/create_builds_service_spec.rb +++ b/spec/services/ci/create_builds_service_spec.rb @@ -1,7 +1,7 @@ require 'spec_helper' describe Ci::CreateBuildsService, services: true do - let(:commit) { create(:ci_commit) } + let(:commit) { create(:ci_commit, ref: 'master') } let(:user) { create(:user) } describe '#execute' do @@ -9,7 +9,7 @@ describe Ci::CreateBuildsService, services: true do # subject do - described_class.new.execute(commit, 'test', 'master', nil, user, nil, status) + described_class.new(commit).execute(commit, nil, user, status) end context 'next builds available' do diff --git a/spec/services/ci/image_for_build_service_spec.rb b/spec/services/ci/image_for_build_service_spec.rb index 870861ad20a..4cc4b3870d1 100644 --- a/spec/services/ci/image_for_build_service_spec.rb +++ b/spec/services/ci/image_for_build_service_spec.rb @@ -5,7 +5,7 @@ module Ci let(:service) { ImageForBuildService.new } let(:project) { FactoryGirl.create(:empty_project) } let(:commit_sha) { '01234567890123456789' } - let(:commit) { project.ensure_ci_commit(commit_sha) } + let(:commit) { project.ensure_ci_commit(commit_sha, 'master') } let(:build) { FactoryGirl.create(:ci_build, commit: commit) } describe :execute do diff --git a/spec/services/create_tag_service_spec.rb b/spec/services/create_tag_service_spec.rb new file mode 100644 index 00000000000..91f9e663b66 --- /dev/null +++ b/spec/services/create_tag_service_spec.rb @@ -0,0 +1,53 @@ +require 'spec_helper' + +describe CreateTagService, services: true do + let(:project) { create(:project) } + let(:repository) { project.repository } + let(:user) { create(:user) } + let(:service) { described_class.new(project, user) } + + describe '#execute' do + it 'creates the tag and returns success' do + response = service.execute('v42.42.42', 'master', 'Foo') + + expect(response[:status]).to eq(:success) + expect(response[:tag]).to be_a Gitlab::Git::Tag + expect(response[:tag].name).to eq('v42.42.42') + end + + context 'when target is invalid' do + it 'returns an error' do + response = service.execute('v1.1.0', 'foo', 'Foo') + + expect(response).to eq(status: :error, + message: 'Target foo is invalid') + end + end + + context 'when tag already exists' do + it 'returns an error' do + expect(repository).to receive(:add_tag). + with(user, 'v1.1.0', 'master', 'Foo'). + and_raise(Rugged::TagError) + + response = service.execute('v1.1.0', 'master', 'Foo') + + expect(response).to eq(status: :error, + message: 'Tag v1.1.0 already exists') + end + end + + context 'when pre-receive hook fails' do + it 'returns an error' do + expect(repository).to receive(:add_tag). + with(user, 'v1.1.0', 'master', 'Foo'). + and_raise(GitHooksService::PreReceiveError) + + response = service.execute('v1.1.0', 'master', 'Foo') + + expect(response).to eq(status: :error, + message: 'Tag creation was rejected by Git hook') + end + end + end +end diff --git a/spec/services/git_push_service_spec.rb b/spec/services/git_push_service_spec.rb index b40a5c1c818..eeab540c2fd 100644 --- a/spec/services/git_push_service_spec.rb +++ b/spec/services/git_push_service_spec.rb @@ -201,6 +201,36 @@ describe GitPushService, services: true do end + describe "Updates git attributes" do + context "for default branch" do + it "calls the copy attributes method for the first push to the default branch" do + expect(project.repository).to receive(:copy_gitattributes).with('master') + + execute_service(project, user, @blankrev, 'newrev', 'refs/heads/master') + end + + it "calls the copy attributes method for changes to the default branch" do + expect(project.repository).to receive(:copy_gitattributes).with('refs/heads/master') + + execute_service(project, user, 'oldrev', 'newrev', 'refs/heads/master') + end + end + + context "for non-default branch" do + before do + # Make sure the "default" branch is different + allow(project).to receive(:default_branch).and_return('not-master') + end + + it "does not call copy attributes method" do + expect(project.repository).not_to receive(:copy_gitattributes) + + execute_service(project, user, @oldrev, @newrev, @ref) + end + end + end + + describe "Webhooks" do context "execute webhooks" do it "when pushing a branch for the first time" do diff --git a/spec/services/git_tag_push_service_spec.rb b/spec/services/git_tag_push_service_spec.rb index cc780587e74..a63656e6268 100644 --- a/spec/services/git_tag_push_service_spec.rb +++ b/spec/services/git_tag_push_service_spec.rb @@ -5,19 +5,17 @@ describe GitTagPushService, services: true do let(:user) { create :user } let(:project) { create :project } - let(:service) { GitTagPushService.new } + let(:service) { GitTagPushService.new(project, user, oldrev: oldrev, newrev: newrev, ref: ref) } - before do - @oldrev = Gitlab::Git::BLANK_SHA - @newrev = "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" # gitlab-test: git rev-parse refs/tags/v1.1.0 - @ref = 'refs/tags/v1.1.0' - end + let(:oldrev) { Gitlab::Git::BLANK_SHA } + let(:newrev) { "8a2a6eb295bb170b34c24c76c49ed0e9b2eaf34b" } # gitlab-test: git rev-parse refs/tags/v1.1.0 + let(:ref) { 'refs/tags/v1.1.0' } describe "Git Tag Push Data" do before do - service.execute(project, user, @oldrev, @newrev, @ref) + service.execute @push_data = service.push_data - @tag_name = Gitlab::Git.ref_name(@ref) + @tag_name = Gitlab::Git.ref_name(ref) @tag = project.repository.find_tag(@tag_name) @commit = project.commit(@tag.target) end @@ -25,9 +23,9 @@ describe GitTagPushService, services: true do subject { @push_data } it { is_expected.to include(object_kind: 'tag_push') } - it { is_expected.to include(ref: @ref) } - it { is_expected.to include(before: @oldrev) } - it { is_expected.to include(after: @newrev) } + it { is_expected.to include(ref: ref) } + it { is_expected.to include(before: oldrev) } + it { is_expected.to include(after: newrev) } it { is_expected.to include(message: @tag.message) } it { is_expected.to include(user_id: user.id) } it { is_expected.to include(user_name: user.name) } @@ -80,9 +78,11 @@ describe GitTagPushService, services: true do describe "Webhooks" do context "execute webhooks" do + let(:service) { GitTagPushService.new(project, user, oldrev: 'oldrev', newrev: 'newrev', ref: 'refs/tags/v1.0.0') } + it "when pushing tags" do expect(project).to receive(:execute_hooks) - service.execute(project, user, 'oldrev', 'newrev', 'refs/tags/v1.0.0') + service.execute end end end diff --git a/spec/services/issues/bulk_update_service_spec.rb b/spec/services/issues/bulk_update_service_spec.rb index 6a7ea4b2f44..e91906d0d49 100644 --- a/spec/services/issues/bulk_update_service_spec.rb +++ b/spec/services/issues/bulk_update_service_spec.rb @@ -100,7 +100,7 @@ describe Issues::BulkUpdateService, services: true do describe :update_milestone do before do - @milestone = create :milestone + @milestone = create(:milestone, project: @project) @params = { issues_ids: [issue.id], milestone_id: @milestone.id diff --git a/spec/services/issues/create_service_spec.rb b/spec/services/issues/create_service_spec.rb index 5e7915db7e1..ac28b6f71f9 100644 --- a/spec/services/issues/create_service_spec.rb +++ b/spec/services/issues/create_service_spec.rb @@ -3,40 +3,75 @@ require 'spec_helper' describe Issues::CreateService, services: true do let(:project) { create(:empty_project) } let(:user) { create(:user) } - let(:assignee) { create(:user) } - describe :execute do - context 'valid params' do + describe '#execute' do + let(:issue) { described_class.new(project, user, opts).execute } + + context 'when params are valid' do + let(:assignee) { create(:user) } + let(:milestone) { create(:milestone, project: project) } + let(:labels) { create_pair(:label, project: project) } + before do project.team << [user, :master] project.team << [assignee, :master] + end - opts = { - title: 'Awesome issue', + let(:opts) do + { title: 'Awesome issue', description: 'please fix', - assignee: assignee - } - - @issue = Issues::CreateService.new(project, user, opts).execute + assignee: assignee, + label_ids: labels.map(&:id), + milestone_id: milestone.id } end - it { expect(@issue).to be_valid } - it { expect(@issue.title).to eq('Awesome issue') } - it { expect(@issue.assignee).to eq assignee } + it { expect(issue).to be_valid } + it { expect(issue.title).to eq('Awesome issue') } + it { expect(issue.assignee).to eq assignee } + it { expect(issue.labels).to match_array labels } + it { expect(issue.milestone).to eq milestone } it 'creates a pending todo for new assignee' do attributes = { project: project, author: user, user: assignee, - target_id: @issue.id, - target_type: @issue.class.name, + target_id: issue.id, + target_type: issue.class.name, action: Todo::ASSIGNED, state: :pending } expect(Todo.where(attributes).count).to eq 1 end + + context 'when label belongs to different project' do + let(:label) { create(:label) } + + let(:opts) do + { title: 'Title', + description: 'Description', + label_ids: [label.id] } + end + + it 'does not assign label'do + expect(issue.labels).to_not include label + end + end + + context 'when milestone belongs to different project' do + let(:milestone) { create(:milestone) } + + let(:opts) do + { title: 'Title', + description: 'Description', + milestone_id: milestone.id } + end + + it 'does not assign milestone' do + expect(issue.milestone).to_not eq milestone + end + end end end end diff --git a/spec/services/issues/move_service_spec.rb b/spec/services/issues/move_service_spec.rb index 2a5e4ac3ec4..c15e26189a5 100644 --- a/spec/services/issues/move_service_spec.rb +++ b/spec/services/issues/move_service_spec.rb @@ -7,10 +7,11 @@ describe Issues::MoveService, services: true do let(:description) { 'Some issue description' } let(:old_project) { create(:project) } let(:new_project) { create(:project) } + let(:milestone1) { create(:milestone, project_id: old_project.id, title: 'v9.0') } let(:old_issue) do create(:issue, title: title, description: description, - project: old_project, author: author) + project: old_project, author: author, milestone: milestone1) end let(:move_service) do @@ -21,11 +22,24 @@ describe Issues::MoveService, services: true do before do old_project.team << [user, :reporter] new_project.team << [user, :reporter] + + ['label1', 'label2'].each do |label| + old_issue.labels << create(:label, + project_id: old_project.id, + title: label) + end + + new_project.labels << create(:label, title: 'label1') + new_project.labels << create(:label, title: 'label2') end end describe '#execute' do shared_context 'issue move executed' do + let!(:milestone2) do + create(:milestone, project_id: new_project.id, title: 'v9.0') + end + let!(:new_issue) { move_service.execute(old_issue, new_project) } end @@ -39,6 +53,23 @@ describe Issues::MoveService, services: true do expect(new_issue.project).to eq new_project end + it 'assigns milestone to new issue' do + expect(new_issue.reload.milestone.title).to eq 'v9.0' + expect(new_issue.reload.milestone).to eq(milestone2) + end + + it 'assign labels to new issue' do + expected_label_titles = new_issue.reload.labels.map(&:title) + expect(expected_label_titles).to include 'label1' + expect(expected_label_titles).to include 'label2' + expect(expected_label_titles.size).to eq 2 + + new_issue.labels.each do |label| + expect(new_project.labels).to include(label) + expect(old_project.labels).not_to include(label) + end + end + it 'rewrites issue title' do expect(new_issue.title).to eq title end @@ -72,11 +103,6 @@ describe Issues::MoveService, services: true do expect(new_issue.author).to eq author end - it 'removes data that is invalid in new context' do - expect(new_issue.milestone).to be_nil - expect(new_issue.labels).to be_empty - end - it 'creates a new internal id for issue' do expect(new_issue.iid).to be 1 end diff --git a/spec/services/issues/update_service_spec.rb b/spec/services/issues/update_service_spec.rb index 6b214a0d96b..52f69306994 100644 --- a/spec/services/issues/update_service_spec.rb +++ b/spec/services/issues/update_service_spec.rb @@ -4,10 +4,15 @@ describe Issues::UpdateService, services: true do let(:user) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } - let(:issue) { create(:issue, title: 'Old title', assignee_id: user3.id) } - let(:label) { create(:label) } + let(:project) { create(:empty_project) } + let(:label) { create(:label, project: project) } let(:label2) { create(:label) } - let(:project) { issue.project } + + let(:issue) do + create(:issue, title: 'Old title', + assignee_id: user3.id, + project: project) + end before do project.team << [user, :master] diff --git a/spec/services/merge_requests/build_service_spec.rb b/spec/services/merge_requests/build_service_spec.rb new file mode 100644 index 00000000000..782d74ec5ec --- /dev/null +++ b/spec/services/merge_requests/build_service_spec.rb @@ -0,0 +1,181 @@ +require 'spec_helper' + +describe MergeRequests::BuildService, services: true do + include RepoHelpers + + let(:project) { create(:project) } + let(:user) { create(:user) } + let(:issue_confidential) { false } + let(:issue) { create(:issue, project: project, title: 'A bug', confidential: issue_confidential) } + let(:description) { nil } + let(:source_branch) { 'feature-branch' } + let(:target_branch) { 'master' } + let(:merge_request) { service.execute } + let(:compare) { double(:compare, commits: commits) } + let(:commit_1) { double(:commit_1, safe_message: "Initial commit\n\nCreate the app") } + let(:commit_2) { double(:commit_2, safe_message: 'This is a bad commit message!') } + let(:commits) { nil } + + let(:service) do + MergeRequests::BuildService.new(project, user, + description: description, + source_branch: source_branch, + target_branch: target_branch) + end + + before do + allow(CompareService).to receive_message_chain(:new, :execute).and_return(compare) + end + + describe 'execute' do + context 'missing source branch' do + let(:source_branch) { '' } + + it 'forbids the merge request from being created' do + expect(merge_request.can_be_created).to eq(false) + end + + it 'adds an error message to the merge request' do + expect(merge_request.errors).to contain_exactly('You must select source and target branch') + end + end + + context 'missing target branch' do + let(:target_branch) { '' } + + it 'forbids the merge request from being created' do + expect(merge_request.can_be_created).to eq(false) + end + + it 'adds an error message to the merge request' do + expect(merge_request.errors).to contain_exactly('You must select source and target branch') + end + end + + context 'no commits in the diff' do + let(:commits) { [] } + + it 'forbids the merge request from being created' do + expect(merge_request.can_be_created).to eq(false) + end + end + + context 'one commit in the diff' do + let(:commits) { [commit_1] } + + it 'allows the merge request to be created' do + expect(merge_request.can_be_created).to eq(true) + end + + it 'uses the title of the commit as the title of the merge request' do + expect(merge_request.title).to eq(commit_1.safe_message.split("\n").first) + end + + it 'uses the description of the commit as the description of the merge request' do + expect(merge_request.description).to eq(commit_1.safe_message.split(/\n+/, 2).last) + end + + context 'merge request already has a description set' do + let(:description) { 'Merge request description' } + + it 'keeps the description from the initial params' do + expect(merge_request.description).to eq(description) + end + end + + context 'commit has no description' do + let(:commits) { [commit_2] } + + it 'uses the title of the commit as the title of the merge request' do + expect(merge_request.title).to eq(commit_2.safe_message) + end + + it 'sets the description to nil' do + expect(merge_request.description).to be_nil + end + end + + context 'branch starts with issue IID followed by a hyphen' do + let(:source_branch) { "#{issue.iid}-fix-issue" } + + it 'appends "Closes #$issue-iid" to the description' do + expect(merge_request.description).to eq("#{commit_1.safe_message.split(/\n+/, 2).last}\nCloses ##{issue.iid}") + end + + context 'merge request already has a description set' do + let(:description) { 'Merge request description' } + + it 'appends "Closes #$issue-iid" to the description' do + expect(merge_request.description).to eq("#{description}\nCloses ##{issue.iid}") + end + end + + context 'commit has no description' do + let(:commits) { [commit_2] } + + it 'sets the description to "Closes #$issue-iid"' do + expect(merge_request.description).to eq("Closes ##{issue.iid}") + end + end + end + end + + context 'more than one commit in the diff' do + let(:commits) { [commit_1, commit_2] } + + it 'allows the merge request to be created' do + expect(merge_request.can_be_created).to eq(true) + end + + it 'uses the title of the branch as the merge request title' do + expect(merge_request.title).to eq('Feature branch') + end + + it 'does not add a description' do + expect(merge_request.description).to be_nil + end + + context 'merge request already has a description set' do + let(:description) { 'Merge request description' } + + it 'keeps the description from the initial params' do + expect(merge_request.description).to eq(description) + end + end + + context 'branch starts with GitLab issue IID followed by a hyphen' do + let(:source_branch) { "#{issue.iid}-fix-issue" } + + it 'sets the title to: Resolves "$issue-title"' do + expect(merge_request.title).to eq("Resolve \"#{issue.title}\"") + end + + context 'issue does not exist' do + let(:source_branch) { "#{issue.iid.succ}-fix-issue" } + + it 'uses the title of the branch as the merge request title' do + expect(merge_request.title).to eq("#{issue.iid.succ} fix issue") + end + end + + context 'issue is confidential' do + let(:issue_confidential) { true } + + it 'uses the title of the branch as the merge request title' do + expect(merge_request.title).to eq("#{issue.iid} fix issue") + end + end + end + + context 'branch starts with external issue IID followed by a hyphen' do + let(:source_branch) { '12345-fix-issue' } + + before { allow(project).to receive(:default_issues_tracker?).and_return(false) } + + it 'sets the title to: Resolves External Issue $issue-iid' do + expect(merge_request.title).to eq('Resolve External Issue 12345') + end + end + end + end +end diff --git a/spec/services/merge_requests/update_service_spec.rb b/spec/services/merge_requests/update_service_spec.rb index cb8cff2fa8c..213e8c2eb3a 100644 --- a/spec/services/merge_requests/update_service_spec.rb +++ b/spec/services/merge_requests/update_service_spec.rb @@ -1,14 +1,19 @@ require 'spec_helper' describe MergeRequests::UpdateService, services: true do + let(:project) { create(:project) } let(:user) { create(:user) } let(:user2) { create(:user) } let(:user3) { create(:user) } - let(:merge_request) { create(:merge_request, :simple, title: 'Old title', assignee_id: user3.id) } - let(:project) { merge_request.project } - let(:label) { create(:label) } + let(:label) { create(:label, project: project) } let(:label2) { create(:label) } + let(:merge_request) do + create(:merge_request, :simple, title: 'Old title', + assignee_id: user3.id, + source_project: project) + end + before do project.team << [user, :master] project.team << [user2, :developer] diff --git a/spec/services/notification_service_spec.rb b/spec/services/notification_service_spec.rb index d7c72dc0811..4bbc4ddc3ab 100644 --- a/spec/services/notification_service_spec.rb +++ b/spec/services/notification_service_spec.rb @@ -10,7 +10,7 @@ describe NotificationService, services: true do end describe 'Keys' do - describe :new_key do + describe '#new_key' do let!(:key) { create(:personal_key) } it { expect(notification.new_key(key)).to be_truthy } @@ -22,7 +22,7 @@ describe NotificationService, services: true do end describe 'Email' do - describe :new_email do + describe '#new_email' do let!(:email) { create(:email) } it { expect(notification.new_email(email)).to be_truthy } @@ -147,8 +147,8 @@ describe NotificationService, services: true do ActionMailer::Base.deliveries.clear end - describe :new_note do - it do + describe '#new_note' do + it 'notifies the team members' do notification.new_note(note) # Notify all team members @@ -177,6 +177,39 @@ describe NotificationService, services: true do end end + context 'project snippet note' do + let(:project) { create(:empty_project, :public) } + let(:snippet) { create(:project_snippet, project: project, author: create(:user)) } + let(:note) { create(:note_on_project_snippet, noteable: snippet, project_id: snippet.project.id, note: '@all mentioned') } + + before do + build_team(note.project) + note.project.team << [note.author, :master] + ActionMailer::Base.deliveries.clear + end + + describe '#new_note' do + it 'notifies the team members' do + notification.new_note(note) + + # Notify all team members + note.project.team.members.each do |member| + # User with disabled notification should not be notified + next if member.id == @u_disabled.id + # Author should not be notified + next if member.id == note.author.id + should_email(member) + end + + should_email(note.noteable.author) + should_not_email(note.author) + should_email(@u_mentioned) + should_not_email(@u_disabled) + should_email(@u_not_mentioned) + end + end + end + context 'commit note' do let(:project) { create(:project, :public) } let(:note) { create(:note_on_commit, project: project) } @@ -187,7 +220,7 @@ describe NotificationService, services: true do allow_any_instance_of(Commit).to receive(:author).and_return(@u_committer) end - describe :new_note, :perform_enqueued_jobs do + describe '#new_note, #perform_enqueued_jobs' do it do notification.new_note(note) @@ -230,7 +263,7 @@ describe NotificationService, services: true do ActionMailer::Base.deliveries.clear end - describe :new_issue do + describe '#new_issue' do it do notification.new_issue(issue, @u_disabled) @@ -289,7 +322,7 @@ describe NotificationService, services: true do end end - describe :reassigned_issue do + describe '#reassigned_issue' do it 'emails new assignee' do notification.reassigned_issue(issue, @u_disabled) @@ -419,7 +452,7 @@ describe NotificationService, services: true do end end - describe :close_issue do + describe '#close_issue' do it 'should sent email to issue assignee and issue author' do notification.close_issue(issue, @u_disabled) @@ -435,7 +468,7 @@ describe NotificationService, services: true do end end - describe :reopen_issue do + describe '#reopen_issue' do it 'should send email to issue assignee and issue author' do notification.reopen_issue(issue, @u_disabled) @@ -461,7 +494,7 @@ describe NotificationService, services: true do ActionMailer::Base.deliveries.clear end - describe :new_merge_request do + describe '#new_merge_request' do it do notification.new_merge_request(merge_request, @u_disabled) @@ -483,7 +516,7 @@ describe NotificationService, services: true do end end - describe :reassigned_merge_request do + describe '#reassigned_merge_request' do it do notification.reassigned_merge_request(merge_request, merge_request.author) @@ -498,7 +531,7 @@ describe NotificationService, services: true do end end - describe :relabel_merge_request do + describe '#relabel_merge_request' do let(:label) { create(:label, merge_requests: [merge_request]) } let(:label2) { create(:label) } let!(:subscriber_to_label) { create(:user).tap { |u| label.toggle_subscription(u) } } @@ -527,7 +560,7 @@ describe NotificationService, services: true do end end - describe :closed_merge_request do + describe '#closed_merge_request' do it do notification.close_mr(merge_request, @u_disabled) @@ -542,7 +575,7 @@ describe NotificationService, services: true do end end - describe :merged_merge_request do + describe '#merged_merge_request' do it do notification.merge_mr(merge_request, @u_disabled) @@ -557,7 +590,7 @@ describe NotificationService, services: true do end end - describe :reopen_merge_request do + describe '#reopen_merge_request' do it do notification.reopen_mr(merge_request, @u_disabled) @@ -581,7 +614,7 @@ describe NotificationService, services: true do ActionMailer::Base.deliveries.clear end - describe :project_was_moved do + describe '#project_was_moved' do it do notification.project_was_moved(project, "gitlab/gitlab") diff --git a/spec/services/projects/import_service_spec.rb b/spec/services/projects/import_service_spec.rb index 32bf3acf483..7f2dcdab960 100644 --- a/spec/services/projects/import_service_spec.rb +++ b/spec/services/projects/import_service_spec.rb @@ -112,9 +112,16 @@ describe Projects::ImportService, services: true do def stub_github_omniauth_provider provider = OpenStruct.new( - name: 'github', - app_id: 'asd123', - app_secret: 'asd123' + 'name' => 'github', + 'app_id' => 'asd123', + 'app_secret' => 'asd123', + 'args' => { + 'client_options' => { + 'site' => 'https://github.com/api/v3', + 'authorize_url' => 'https://github.com/login/oauth/authorize', + 'token_url' => 'https://github.com/login/oauth/access_token' + } + } ) Gitlab.config.omniauth.providers << provider diff --git a/spec/services/system_note_service_spec.rb b/spec/services/system_note_service_spec.rb index 240eae10052..5fbf2ae5247 100644 --- a/spec/services/system_note_service_spec.rb +++ b/spec/services/system_note_service_spec.rb @@ -506,6 +506,15 @@ describe SystemNoteService, services: true do end end + describe '.new_commit_summary' do + it 'escapes HTML titles' do + commit = double(title: '<pre>This is a test</pre>', short_id: '12345678') + escaped = '* 12345678 - <pre>This is a test</pre>' + + expect(described_class.new_commit_summary([commit])).to eq([escaped]) + end + end + include JiraServiceHelper describe 'JIRA integration' do diff --git a/spec/services/todo_service_spec.rb b/spec/services/todo_service_spec.rb index 455b19c89cc..0d88b77ad74 100644 --- a/spec/services/todo_service_spec.rb +++ b/spec/services/todo_service_spec.rb @@ -55,6 +55,25 @@ describe TodoService, services: true do should_create_todo(user: admin, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) should_not_create_todo(user: john_doe, target: confidential_issue, author: john_doe, action: Todo::MENTIONED) end + + context 'when a private group is mentioned' do + let(:group) { create :group, :private } + let(:project) { create :project, :private, group: group } + let(:issue) { create :issue, author: author, project: project, description: group.to_reference } + + before do + group.add_owner(author) + group.add_user(member, Gitlab::Access::DEVELOPER) + group.add_user(john_doe, Gitlab::Access::DEVELOPER) + + service.new_issue(issue, author) + end + + it 'creates a todo for group members' do + should_create_todo(user: member, target: issue) + should_create_todo(user: john_doe, target: issue) + end + end end describe '#update_issue' do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 596d607f2a1..576d16e7ea3 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -51,10 +51,4 @@ FactoryGirl::SyntaxRunner.class_eval do include RSpec::Mocks::ExampleMethods end -# Work around a Rails 4.2.5.1 issue -# See https://github.com/rspec/rspec-rails/issues/1532 -RSpec::Rails::ViewRendering::EmptyTemplatePathSetDecorator.class_eval do - alias_method :find_all_anywhere, :find_all -end - ActiveRecord::Migration.maintain_test_schema! diff --git a/spec/support/gitlab_stubs/gitlab_ci.yml b/spec/support/gitlab_stubs/gitlab_ci.yml index a5b256bd3ec..e55a61b2b94 100644 --- a/spec/support/gitlab_stubs/gitlab_ci.yml +++ b/spec/support/gitlab_stubs/gitlab_ci.yml @@ -4,7 +4,7 @@ services: before_script: - gem install bundler - - bundle install + - bundle install - bundle exec rake db:create variables: @@ -17,7 +17,7 @@ types: rspec: script: "rake spec" - tags: + tags: - ruby - postgres only: @@ -26,27 +26,32 @@ rspec: spinach: script: "rake spinach" allow_failure: true - tags: + tags: - ruby - mysql except: - tags staging: + variables: + KEY1: value1 + KEY2: value2 script: "cap deploy stating" type: deploy - tags: + tags: - ruby - mysql except: - stable production: + variables: + DB_NAME: mysql type: deploy - script: + script: - cap deploy production - cap notify - tags: + tags: - ruby - mysql only: diff --git a/spec/support/issue_tracker_service_shared_example.rb b/spec/support/issue_tracker_service_shared_example.rb new file mode 100644 index 00000000000..b6d7436c360 --- /dev/null +++ b/spec/support/issue_tracker_service_shared_example.rb @@ -0,0 +1,7 @@ +RSpec.shared_examples 'issue tracker service URL attribute' do |url_attr| + it { is_expected.to allow_value('https://example.com').for(url_attr) } + + it { is_expected.not_to allow_value('example.com').for(url_attr) } + it { is_expected.not_to allow_value('ftp://example.com').for(url_attr) } + it { is_expected.not_to allow_value('herp-and-derp').for(url_attr) } +end diff --git a/spec/support/project_hook_data_shared_example.rb b/spec/support/project_hook_data_shared_example.rb index 422083875d7..7dbaa6a6459 100644 --- a/spec/support/project_hook_data_shared_example.rb +++ b/spec/support/project_hook_data_shared_example.rb @@ -1,4 +1,4 @@ -RSpec.shared_examples 'project hook data' do |project_key: :project| +RSpec.shared_examples 'project hook data with deprecateds' do |project_key: :project| it 'contains project data' do expect(data[project_key][:name]).to eq(project.name) expect(data[project_key][:description]).to eq(project.description) @@ -17,6 +17,21 @@ RSpec.shared_examples 'project hook data' do |project_key: :project| end end +RSpec.shared_examples 'project hook data' do |project_key: :project| + it 'contains project data' do + expect(data[project_key][:name]).to eq(project.name) + expect(data[project_key][:description]).to eq(project.description) + expect(data[project_key][:web_url]).to eq(project.web_url) + expect(data[project_key][:avatar_url]).to eq(project.avatar_url) + expect(data[project_key][:git_http_url]).to eq(project.http_url_to_repo) + expect(data[project_key][:git_ssh_url]).to eq(project.ssh_url_to_repo) + expect(data[project_key][:namespace]).to eq(project.namespace.name) + expect(data[project_key][:visibility_level]).to eq(project.visibility_level) + expect(data[project_key][:path_with_namespace]).to eq(project.path_with_namespace) + expect(data[project_key][:default_branch]).to eq(project.default_branch) + end +end + RSpec.shared_examples 'deprecated repository hook data' do |project_key: :project| it 'contains deprecated repository data' do expect(data[:repository][:name]).to eq(project.name) diff --git a/spec/support/repo_helpers.rb b/spec/support/repo_helpers.rb index aa8258d6dad..73f375c481b 100644 --- a/spec/support/repo_helpers.rb +++ b/spec/support/repo_helpers.rb @@ -42,7 +42,7 @@ Signed-off-by: Dmitriy Zaporozhets <dmitriy.zaporozhets@gmail.com> eos ) end - + def another_sample_commit OpenStruct.new( id: "e56497bb5f03a90a51293fc6d516788730953899", diff --git a/spec/workers/repository_check/batch_worker_spec.rb b/spec/workers/repository_check/batch_worker_spec.rb index f486e45ddad..27727d6abf9 100644 --- a/spec/workers/repository_check/batch_worker_spec.rb +++ b/spec/workers/repository_check/batch_worker_spec.rb @@ -4,7 +4,7 @@ describe RepositoryCheck::BatchWorker do subject { described_class.new } it 'prefers projects that have never been checked' do - projects = create_list(:project, 3) + projects = create_list(:project, 3, created_at: 1.week.ago) projects[0].update_column(:last_repository_check_at, 4.months.ago) projects[2].update_column(:last_repository_check_at, 3.months.ago) @@ -12,7 +12,7 @@ describe RepositoryCheck::BatchWorker do end it 'sorts projects by last_repository_check_at' do - projects = create_list(:project, 3) + projects = create_list(:project, 3, created_at: 1.week.ago) projects[0].update_column(:last_repository_check_at, 2.months.ago) projects[1].update_column(:last_repository_check_at, 4.months.ago) projects[2].update_column(:last_repository_check_at, 3.months.ago) @@ -21,7 +21,7 @@ describe RepositoryCheck::BatchWorker do end it 'excludes projects that were checked recently' do - projects = create_list(:project, 3) + projects = create_list(:project, 3, created_at: 1.week.ago) projects[0].update_column(:last_repository_check_at, 2.days.ago) projects[1].update_column(:last_repository_check_at, 2.months.ago) projects[2].update_column(:last_repository_check_at, 3.days.ago) @@ -30,10 +30,17 @@ describe RepositoryCheck::BatchWorker do end it 'does nothing when repository checks are disabled' do - create(:empty_project) + create(:empty_project, created_at: 1.week.ago) current_settings = double('settings', repository_checks_enabled: false) expect(subject).to receive(:current_settings) { current_settings } expect(subject.perform).to eq(nil) end + + it 'skips projects created less than 24 hours ago' do + project = create(:empty_project) + project.update_column(:created_at, 23.hours.ago) + + expect(subject.perform).to eq([]) + end end diff --git a/spec/workers/repository_check/single_repository_worker_spec.rb b/spec/workers/repository_check/single_repository_worker_spec.rb new file mode 100644 index 00000000000..5a03bb77ebd --- /dev/null +++ b/spec/workers/repository_check/single_repository_worker_spec.rb @@ -0,0 +1,57 @@ +require 'spec_helper' +require 'fileutils' + +describe RepositoryCheck::SingleRepositoryWorker do + subject { described_class.new } + + it 'fails if the wiki repository is broken' do + project = create(:project_empty_repo, wiki_enabled: true) + project.create_wiki + + # Test sanity: everything should be fine before the wiki repo is broken + subject.perform(project.id) + expect(project.reload.last_repository_check_failed).to eq(false) + + break_wiki(project) + subject.perform(project.id) + + expect(project.reload.last_repository_check_failed).to eq(true) + end + + it 'skips wikis when disabled' do + project = create(:project_empty_repo, wiki_enabled: false) + # Make sure the test would fail if the wiki repo was checked + break_wiki(project) + + subject.perform(project.id) + + expect(project.reload.last_repository_check_failed).to eq(false) + end + + it 'creates missing wikis' do + project = create(:project_empty_repo, wiki_enabled: true) + FileUtils.rm_rf(wiki_path(project)) + + subject.perform(project.id) + + expect(project.reload.last_repository_check_failed).to eq(false) + end + + it 'does not create a wiki if the main repo does not exist at all' do + project = create(:project_empty_repo) + FileUtils.rm_rf(project.repository.path_to_repo) + FileUtils.rm_rf(wiki_path(project)) + + subject.perform(project.id) + + expect(File.exist?(wiki_path(project))).to eq(false) + end + + def break_wiki(project) + FileUtils.rm_rf(wiki_path(project) + '/objects') + end + + def wiki_path(project) + project.wiki.repository.path_to_repo + end +end |