summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorMike Greiling <mike@pixelcog.com>2017-11-02 17:05:06 -0500
committerMike Greiling <mike@pixelcog.com>2017-11-02 17:05:06 -0500
commit11c21e953fe1d8aa108c7558d715300e3221a308 (patch)
tree46a55d65375e712f4ef59cead95c07179fbfefe3 /spec
parent2b29a4675bbaf79ed463e5ffc059e81aeb3251a8 (diff)
parent1d4efeec2e1a203be113077e6504d8f256271db1 (diff)
downloadgitlab-ce-11c21e953fe1d8aa108c7558d715300e3221a308.tar.gz
Merge branch 'master' into sh-headless-chrome-support
* master: (109 commits) Remove Filesystem check metrics that use too much CPU to handle requests Set merge_request_diff_id on MR when creating Add a column linking an MR to its diff Remove useless closeReopenReport specs Clarify external artifacts only working when GitLab pages is enabled Send SIGSTP before SIGTERM to actually give Sidekiq jobs 30s to finish when the memory killer kicks in Remove an exception from the git user default SSH config check Geo route whitelisting is too optimistic Update .nvmrc to current stable (v9.0.0) Update documentation Address Douwe's feedback Refactor responsive table styles to support nested error block Add changelog items Update specs for sudo behavior Move RSS and incoming email tokens from User Settings > Accounts to User Settings > Access Tokens Remove user authentication_token column Migrate user private tokens to personal access tokens Add sudo API scope Consistently use PersonalAccessToken instead of PersonalToken Remove User#private_token ...
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/application_controller_spec.rb86
-rw-r--r--spec/controllers/metrics_controller_spec.rb11
-rw-r--r--spec/controllers/projects/issues_controller_spec.rb39
-rw-r--r--spec/controllers/projects/merge_requests_controller_spec.rb56
-rw-r--r--spec/features/atom/dashboard_issues_spec.rb6
-rw-r--r--spec/features/atom/dashboard_spec.rb6
-rw-r--r--spec/features/atom/issues_spec.rb6
-rw-r--r--spec/features/atom/users_spec.rb6
-rw-r--r--spec/features/issues/filtered_search/recent_searches_spec.rb25
-rw-r--r--spec/features/issues_spec.rb10
-rw-r--r--spec/features/merge_requests/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/profile_spec.rb37
-rw-r--r--spec/features/projects/commit/mini_pipeline_graph_spec.rb2
-rw-r--r--spec/features/projects/pipelines/pipeline_spec.rb14
-rw-r--r--spec/features/projects/wiki/user_views_wiki_page_spec.rb13
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/user/login.json6
-rw-r--r--spec/helpers/ci_status_helper_spec.rb12
-rw-r--r--spec/helpers/gitlab_routing_helper_spec.rb26
-rw-r--r--spec/helpers/issuables_helper_spec.rb32
-rw-r--r--spec/javascripts/groups/components/app_spec.js4
-rw-r--r--spec/javascripts/issue_spec.js34
-rw-r--r--spec/javascripts/jobs/mock_data.js2
-rw-r--r--spec/javascripts/labels_issue_sidebar_spec.js3
-rw-r--r--spec/javascripts/namespace_select_spec.js65
-rw-r--r--spec/javascripts/pipelines/graph/action_component_spec.js2
-rw-r--r--spec/javascripts/pipelines/graph/dropdown_action_component_spec.js2
-rw-r--r--spec/javascripts/pipelines/graph/job_component_spec.js2
-rw-r--r--spec/javascripts/pipelines/graph/mock_data.js12
-rw-r--r--spec/javascripts/pipelines/graph/stage_column_component_spec.js2
-rw-r--r--spec/javascripts/repo/components/repo_editor_spec.js18
-rw-r--r--spec/javascripts/search_autocomplete_spec.js1
-rw-r--r--spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js10
-rw-r--r--spec/javascripts/vue_shared/ci_action_icons_spec.js27
-rw-r--r--spec/javascripts/vue_shared/ci_status_icon_spec.js27
-rw-r--r--spec/javascripts/vue_shared/components/ci_badge_link_spec.js18
-rw-r--r--spec/javascripts/vue_shared/components/icon_spec.js48
-rw-r--r--spec/javascripts/vue_shared/components/markdown/field_spec.js14
-rw-r--r--spec/lib/banzai/filter/issue_reference_filter_spec.rb62
-rw-r--r--spec/lib/banzai/filter/label_reference_filter_spec.rb12
-rw-r--r--spec/lib/banzai/filter/merge_request_reference_filter_spec.rb10
-rw-r--r--spec/lib/banzai/filter/milestone_reference_filter_spec.rb11
-rw-r--r--spec/lib/banzai/filter/snippet_reference_filter_spec.rb10
-rw-r--r--spec/lib/banzai/filter/user_reference_filter_spec.rb33
-rw-r--r--spec/lib/gitlab/auth_spec.rb14
-rw-r--r--spec/lib/gitlab/ci/status/build/cancelable_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/factory_spec.rb18
-rw-r--r--spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/play_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/retryable_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/build/stop_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/canceled_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/created_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/failed_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/manual_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/pending_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/running_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/skipped_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/success_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/status/success_warning_spec.rb2
-rw-r--r--spec/lib/gitlab/git/blob_spec.rb55
-rw-r--r--spec/lib/gitlab/git/lfs_changes_spec.rb48
-rw-r--r--spec/lib/gitlab/git/repository_spec.rb83
-rw-r--r--spec/lib/gitlab/git/rev_list_spec.rb87
-rw-r--r--spec/lib/gitlab/gitaly_client/operation_service_spec.rb34
-rw-r--r--spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb48
-rw-r--r--spec/lib/gitlab/middleware/go_spec.rb138
-rw-r--r--spec/lib/gitlab/middleware/read_only_spec.rb26
-rw-r--r--spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb63
-rw-r--r--spec/lib/gitlab/workhorse_spec.rb18
-rw-r--r--spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb8
-rw-r--r--spec/migrations/migrate_user_authentication_token_to_personal_access_token_spec.rb25
-rw-r--r--spec/migrations/populate_merge_requests_latest_merge_request_diff_id_spec.rb61
-rw-r--r--spec/models/concerns/token_authenticatable_spec.rb2
-rw-r--r--spec/models/project_wiki_spec.rb47
-rw-r--r--spec/models/user_spec.rb9
-rw-r--r--spec/requests/api/doorkeeper_access_spec.rb10
-rw-r--r--spec/requests/api/helpers_spec.rb432
-rw-r--r--spec/requests/api/session_spec.rb107
-rw-r--r--spec/requests/api/users_spec.rb30
-rw-r--r--spec/routing/routing_spec.rb5
-rw-r--r--spec/serializers/issue_entity_spec.rb20
-rw-r--r--spec/serializers/merge_request_entity_spec.rb11
-rw-r--r--spec/services/issuable/common_system_notes_service_spec.rb49
-rw-r--r--spec/services/projects/group_links/create_service_spec.rb22
-rw-r--r--spec/services/projects/group_links/destroy_service_spec.rb16
-rw-r--r--spec/spec_helper.rb1
-rw-r--r--spec/support/api_helpers.rb28
-rw-r--r--spec/support/bare_repo_operations.rb60
-rw-r--r--spec/support/gitlab-git-test.git/objects/88/3e379fcaa5f818fca81cdbabd7a497794d6535bin0 -> 304 bytes
-rw-r--r--spec/support/gitlab-git-test.git/objects/c8/b1ab16c858c67b680eea4644cf652485f555cfbin0 -> 597 bytes
-rw-r--r--spec/support/gitlab-git-test.git/objects/e3/7697aea12699f0b44544332a7c0f41ace5fb162
-rw-r--r--spec/support/gitlab-git-test.git/objects/eb/a0c153ed20d927bab00507f356043b6b4be31ebin0 -> 185 bytes
-rw-r--r--spec/support/gitlab-git-test.git/objects/f6/5ad228d96e2a2ae7088e8557fe8906f6dd2b3fbin0 -> 642 bytes
-rw-r--r--spec/support/gitlab_stubs/session.json4
-rw-r--r--spec/support/gitlab_stubs/user.json6
-rw-r--r--spec/support/live_debugger.rb17
-rw-r--r--spec/support/login_helpers.rb18
-rw-r--r--spec/support/test_env.rb2
-rw-r--r--spec/tasks/gitlab/users_rake_spec.rb38
-rw-r--r--spec/tasks/tokens_spec.rb6
100 files changed, 1526 insertions, 994 deletions
diff --git a/spec/controllers/application_controller_spec.rb b/spec/controllers/application_controller_spec.rb
index 6802b839eaa..b73ca0c2346 100644
--- a/spec/controllers/application_controller_spec.rb
+++ b/spec/controllers/application_controller_spec.rb
@@ -50,70 +50,36 @@ describe ApplicationController do
end
end
- describe "#authenticate_user_from_token!" do
- describe "authenticating a user from a private token" do
- controller(described_class) do
- def index
- render text: "authenticated"
- end
- end
-
- context "when the 'private_token' param is populated with the private token" do
- it "logs the user in" do
- get :index, private_token: user.private_token
- expect(response).to have_gitlab_http_status(200)
- expect(response.body).to eq("authenticated")
- end
- end
-
- context "when the 'PRIVATE-TOKEN' header is populated with the private token" do
- it "logs the user in" do
- @request.headers['PRIVATE-TOKEN'] = user.private_token
- get :index
- expect(response).to have_gitlab_http_status(200)
- expect(response.body).to eq("authenticated")
- end
- end
-
- it "doesn't log the user in otherwise" do
- @request.headers['PRIVATE-TOKEN'] = "token"
- get :index, private_token: "token", authenticity_token: "token"
- expect(response.status).not_to eq(200)
- expect(response.body).not_to eq("authenticated")
+ describe "#authenticate_user_from_personal_access_token!" do
+ controller(described_class) do
+ def index
+ render text: 'authenticated'
end
end
- describe "authenticating a user from a personal access token" do
- controller(described_class) do
- def index
- render text: 'authenticated'
- end
- end
-
- let(:personal_access_token) { create(:personal_access_token, user: user) }
+ let(:personal_access_token) { create(:personal_access_token, user: user) }
- context "when the 'personal_access_token' param is populated with the personal access token" do
- it "logs the user in" do
- get :index, private_token: personal_access_token.token
- expect(response).to have_gitlab_http_status(200)
- expect(response.body).to eq('authenticated')
- end
+ context "when the 'personal_access_token' param is populated with the personal access token" do
+ it "logs the user in" do
+ get :index, private_token: personal_access_token.token
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to eq('authenticated')
end
+ end
- context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do
- it "logs the user in" do
- @request.headers["PRIVATE-TOKEN"] = personal_access_token.token
- get :index
- expect(response).to have_gitlab_http_status(200)
- expect(response.body).to eq('authenticated')
- end
+ context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do
+ it "logs the user in" do
+ @request.headers["PRIVATE-TOKEN"] = personal_access_token.token
+ get :index
+ expect(response).to have_gitlab_http_status(200)
+ expect(response.body).to eq('authenticated')
end
+ end
- it "doesn't log the user in otherwise" do
- get :index, private_token: "token"
- expect(response.status).not_to eq(200)
- expect(response.body).not_to eq('authenticated')
- end
+ it "doesn't log the user in otherwise" do
+ get :index, private_token: "token"
+ expect(response.status).not_to eq(200)
+ expect(response.body).not_to eq('authenticated')
end
end
@@ -152,11 +118,15 @@ describe ApplicationController do
end
end
+ before do
+ sign_in user
+ end
+
context 'when format is handled' do
let(:requested_format) { :json }
it 'returns 200 response' do
- get :index, private_token: user.private_token, format: requested_format
+ get :index, format: requested_format
expect(response).to have_gitlab_http_status 200
end
@@ -164,7 +134,7 @@ describe ApplicationController do
context 'when format is not handled' do
it 'returns 404 response' do
- get :index, private_token: user.private_token
+ get :index
expect(response).to have_gitlab_http_status 404
end
diff --git a/spec/controllers/metrics_controller_spec.rb b/spec/controllers/metrics_controller_spec.rb
index 7b0976e3e67..4aed2a25baa 100644
--- a/spec/controllers/metrics_controller_spec.rb
+++ b/spec/controllers/metrics_controller_spec.rb
@@ -59,17 +59,6 @@ describe MetricsController do
expect(response.body).to match(/^redis_shared_state_ping_latency_seconds [0-9\.]+$/)
end
- it 'returns file system check metrics' do
- get :index
-
- expect(response.body).to match(/^filesystem_access_latency_seconds{shard="default"} [0-9\.]+$/)
- expect(response.body).to match(/^filesystem_accessible{shard="default"} 1$/)
- expect(response.body).to match(/^filesystem_write_latency_seconds{shard="default"} [0-9\.]+$/)
- expect(response.body).to match(/^filesystem_writable{shard="default"} 1$/)
- expect(response.body).to match(/^filesystem_read_latency_seconds{shard="default"} [0-9\.]+$/)
- expect(response.body).to match(/^filesystem_readable{shard="default"} 1$/)
- end
-
context 'prometheus metrics are disabled' do
before do
allow(Gitlab::Metrics).to receive(:prometheus_metrics_enabled?).and_return(false)
diff --git a/spec/controllers/projects/issues_controller_spec.rb b/spec/controllers/projects/issues_controller_spec.rb
index aecdfb50759..8016176110e 100644
--- a/spec/controllers/projects/issues_controller_spec.rb
+++ b/spec/controllers/projects/issues_controller_spec.rb
@@ -248,6 +248,45 @@ describe Projects::IssuesController do
end
end
+ describe 'PUT #update' do
+ subject do
+ put :update,
+ namespace_id: project.namespace,
+ project_id: project,
+ id: issue.to_param,
+ issue: { title: 'New title' }, format: :json
+ end
+
+ before do
+ sign_in(user)
+ end
+
+ context 'when user has access to update issue' do
+ before do
+ project.add_developer(user)
+ end
+
+ it 'updates the issue' do
+ subject
+
+ expect(response).to have_http_status(:ok)
+ expect(issue.reload.title).to eq('New title')
+ end
+ end
+
+ context 'when user does not have access to update issue' do
+ before do
+ project.add_guest(user)
+ end
+
+ it 'responds with 404' do
+ subject
+
+ expect(response).to have_http_status(:not_found)
+ 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 52ef8c6a589..14021b8ca50 100644
--- a/spec/controllers/projects/merge_requests_controller_spec.rb
+++ b/spec/controllers/projects/merge_requests_controller_spec.rb
@@ -186,17 +186,23 @@ describe Projects::MergeRequestsController do
end
describe 'PUT update' do
+ def update_merge_request(mr_params, additional_params = {})
+ params = {
+ namespace_id: project.namespace,
+ project_id: project,
+ id: merge_request.iid,
+ merge_request: mr_params
+ }.merge(additional_params)
+
+ put :update, params
+ end
+
context 'changing the assignee' do
it 'limits the attributes exposed on the assignee' do
assignee = create(:user)
project.add_developer(assignee)
- put :update,
- namespace_id: project.namespace.to_param,
- project_id: project,
- id: merge_request.iid,
- merge_request: { assignee_id: assignee.id },
- format: :json
+ update_merge_request({ assignee_id: assignee.id }, format: :json)
body = JSON.parse(response.body)
expect(body['assignee'].keys)
@@ -204,6 +210,20 @@ describe Projects::MergeRequestsController do
end
end
+ context 'when user does not have access to update issue' do
+ before do
+ reporter = create(:user)
+ project.add_reporter(reporter)
+ sign_in(reporter)
+ end
+
+ it 'responds with 404' do
+ update_merge_request(title: 'New title')
+
+ expect(response).to have_http_status(:not_found)
+ end
+ end
+
context 'there is no source project' do
let(:project) { create(:project, :repository) }
let(:forked_project) { fork_project_with_submodules(project) }
@@ -214,13 +234,7 @@ describe Projects::MergeRequestsController do
end
it 'closes MR without errors' do
- post :update,
- namespace_id: project.namespace,
- project_id: project,
- id: merge_request.iid,
- merge_request: {
- state_event: 'close'
- }
+ update_merge_request(state_event: 'close')
expect(response).to redirect_to([merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request])
expect(merge_request.reload.closed?).to be_truthy
@@ -229,13 +243,7 @@ describe Projects::MergeRequestsController do
it 'allows editing of a closed merge request' do
merge_request.close!
- put :update,
- namespace_id: project.namespace,
- project_id: project,
- id: merge_request.iid,
- merge_request: {
- title: 'New title'
- }
+ update_merge_request(title: 'New title')
expect(response).to redirect_to([merge_request.target_project.namespace.becomes(Namespace), merge_request.target_project, merge_request])
expect(merge_request.reload.title).to eq 'New title'
@@ -244,13 +252,7 @@ describe Projects::MergeRequestsController do
it 'does not allow to update target branch closed merge request' do
merge_request.close!
- put :update,
- namespace_id: project.namespace,
- project_id: project,
- id: merge_request.iid,
- merge_request: {
- target_branch: 'new_branch'
- }
+ update_merge_request(target_branch: 'new_branch')
expect { merge_request.reload.target_branch }.not_to change { merge_request.target_branch }
end
diff --git a/spec/features/atom/dashboard_issues_spec.rb b/spec/features/atom/dashboard_issues_spec.rb
index 5aae2dbaf91..89c9d377003 100644
--- a/spec/features/atom/dashboard_issues_spec.rb
+++ b/spec/features/atom/dashboard_issues_spec.rb
@@ -13,8 +13,10 @@ describe "Dashboard Issues Feed" do
end
describe "atom feed" do
- it "renders atom feed via private token" do
- visit issues_dashboard_path(:atom, private_token: user.private_token)
+ it "renders atom feed via personal access token" do
+ personal_access_token = create(:personal_access_token, user: user)
+
+ visit issues_dashboard_path(:atom, private_token: personal_access_token.token)
expect(response_headers['Content-Type']).to have_content('application/atom+xml')
expect(body).to have_selector('title', text: "#{user.name} issues")
diff --git a/spec/features/atom/dashboard_spec.rb b/spec/features/atom/dashboard_spec.rb
index 321c8a2a670..2c0c331b6db 100644
--- a/spec/features/atom/dashboard_spec.rb
+++ b/spec/features/atom/dashboard_spec.rb
@@ -4,9 +4,11 @@ describe "Dashboard Feed" do
describe "GET /" do
let!(:user) { create(:user, name: "Jonh") }
- context "projects atom feed via private token" do
+ context "projects atom feed via personal access token" do
it "renders projects atom feed" do
- visit dashboard_projects_path(:atom, private_token: user.private_token)
+ personal_access_token = create(:personal_access_token, user: user)
+
+ visit dashboard_projects_path(:atom, private_token: personal_access_token.token)
expect(body).to have_selector('feed title')
end
end
diff --git a/spec/features/atom/issues_spec.rb b/spec/features/atom/issues_spec.rb
index 3eeb4d35131..4102ac0588a 100644
--- a/spec/features/atom/issues_spec.rb
+++ b/spec/features/atom/issues_spec.rb
@@ -28,10 +28,12 @@ describe 'Issues Feed' do
end
end
- context 'when authenticated via private token' do
+ context 'when authenticated via personal access token' do
it 'renders atom feed' do
+ personal_access_token = create(:personal_access_token, user: user)
+
visit project_issues_path(project, :atom,
- private_token: user.private_token)
+ private_token: personal_access_token.token)
expect(response_headers['Content-Type'])
.to have_content('application/atom+xml')
diff --git a/spec/features/atom/users_spec.rb b/spec/features/atom/users_spec.rb
index 9ce687afb31..2b934d81674 100644
--- a/spec/features/atom/users_spec.rb
+++ b/spec/features/atom/users_spec.rb
@@ -4,9 +4,11 @@ describe "User Feed" do
describe "GET /" do
let!(:user) { create(:user) }
- context 'user atom feed via private token' do
+ context 'user atom feed via personal access token' do
it "renders user atom feed" do
- visit user_path(user, :atom, private_token: user.private_token)
+ personal_access_token = create(:personal_access_token, user: user)
+
+ visit user_path(user, :atom, private_token: personal_access_token.token)
expect(body).to have_selector('feed title')
end
end
diff --git a/spec/features/issues/filtered_search/recent_searches_spec.rb b/spec/features/issues/filtered_search/recent_searches_spec.rb
index 464d964c90e..86f90e51d6b 100644
--- a/spec/features/issues/filtered_search/recent_searches_spec.rb
+++ b/spec/features/issues/filtered_search/recent_searches_spec.rb
@@ -27,9 +27,8 @@ describe 'Recent searches', :js do
input_filtered_search('foo', submit: true)
input_filtered_search('bar', submit: true)
- items = all('.filtered-search-history-dropdown-item', visible: false)
+ items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
- expect(items.count).to eq(2)
expect(items[0].text).to eq('bar')
expect(items[1].text).to eq('foo')
end
@@ -38,9 +37,8 @@ describe 'Recent searches', :js do
visit project_issues_path(project_1, label_name: 'foo', search: 'bar')
visit project_issues_path(project_1, label_name: 'qux', search: 'garply')
- items = all('.filtered-search-history-dropdown-item', visible: false)
+ items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
- expect(items.count).to eq(2)
expect(items[0].text).to eq('label:~qux garply')
expect(items[1].text).to eq('label:~foo bar')
end
@@ -50,9 +48,8 @@ describe 'Recent searches', :js do
visit project_issues_path(project_1, search: 'foo')
- items = all('.filtered-search-history-dropdown-item', visible: false)
+ items = all('.filtered-search-history-dropdown-item', visible: false, count: 3)
- expect(items.count).to eq(3)
expect(items[0].text).to eq('foo')
expect(items[1].text).to eq('saved1')
expect(items[2].text).to eq('saved2')
@@ -69,9 +66,8 @@ describe 'Recent searches', :js do
input_filtered_search('more', submit: true)
input_filtered_search('things', submit: true)
- items = all('.filtered-search-history-dropdown-item', visible: false)
+ items = all('.filtered-search-history-dropdown-item', visible: false, count: 2)
- expect(items.count).to eq(2)
expect(items[0].text).to eq('things')
expect(items[1].text).to eq('more')
end
@@ -80,8 +76,7 @@ describe 'Recent searches', :js do
set_recent_searches(project_1_local_storage_key, '["foo", "bar"]')
visit project_issues_path(project_1)
- find('.filtered-search-history-dropdown-toggle-button').click
- all('.filtered-search-history-dropdown-item')[0].click
+ all('.filtered-search-history-dropdown-item', visible: false, count: 2)[0].click
wait_for_filtered_search('foo')
expect(find('.filtered-search').value.strip).to eq('foo')
@@ -91,14 +86,10 @@ describe 'Recent searches', :js do
set_recent_searches(project_1_local_storage_key, '["foo"]')
visit project_issues_path(project_1)
- find('.filtered-search-history-dropdown-toggle-button').click
- items_before = all('.filtered-search-history-dropdown-item')
+ all('.filtered-search-history-dropdown-item', visible: false, count: 1)
- expect(items_before.count).to eq(1)
-
- find('.filtered-search-history-clear-button').click
- find('.filtered-search-history-dropdown-toggle-button').click
- items_after = all('.filtered-search-history-dropdown-item')
+ find('.filtered-search-history-clear-button', visible: false).click
+ items_after = all('.filtered-search-history-dropdown-item', visible: false, count: 0)
expect(items_after.count).to eq(0)
end
diff --git a/spec/features/issues_spec.rb b/spec/features/issues_spec.rb
index b6da66ea901..a5c24b362a2 100644
--- a/spec/features/issues_spec.rb
+++ b/spec/features/issues_spec.rb
@@ -583,6 +583,16 @@ describe 'Issues' do
expect(page.find_field("issue_description").value).not_to match /\n\n$/
end
+
+ it "cancels a file upload correctly" do
+ dropzone_file([Rails.root.join('spec', 'fixtures', 'dk.png')], 0, false)
+
+ click_button 'Cancel'
+
+ expect(page).to have_button('Attach a file')
+ expect(page).not_to have_button('Cancel')
+ expect(page).not_to have_selector('.uploading-progress-container', visible: true)
+ end
end
context 'form filled by URL parameters' do
diff --git a/spec/features/merge_requests/mini_pipeline_graph_spec.rb b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
index bac56270362..bd47599c2f9 100644
--- a/spec/features/merge_requests/mini_pipeline_graph_spec.rb
+++ b/spec/features/merge_requests/mini_pipeline_graph_spec.rb
@@ -83,7 +83,7 @@ feature 'Mini Pipeline Graph', :js do
end
before do
- toggle.click
+ toggle.trigger('click')
wait_for_requests
end
diff --git a/spec/features/profile_spec.rb b/spec/features/profile_spec.rb
index 1cddd35fd8a..0166ab8be99 100644
--- a/spec/features/profile_spec.rb
+++ b/spec/features/profile_spec.rb
@@ -1,6 +1,6 @@
require 'spec_helper'
-describe 'Profile account page' do
+describe 'Profile account page', :js do
let(:user) { create(:user) }
before do
@@ -56,47 +56,38 @@ describe 'Profile account page' do
end
end
- describe 'when I reset private token' do
- before do
- visit profile_account_path
- end
-
- it 'resets private token' do
- previous_token = find("#private-token").value
-
- click_link('Reset private token')
-
- expect(find('#private-token').value).not_to eq(previous_token)
- end
- end
-
describe 'when I reset RSS token' do
before do
- visit profile_account_path
+ visit profile_personal_access_tokens_path
end
it 'resets RSS token' do
- previous_token = find("#rss-token").value
+ within('.rss-token-reset') do
+ previous_token = find("#rss_token").value
- click_link('Reset RSS token')
+ click_link('reset it')
+
+ expect(find('#rss_token').value).not_to eq(previous_token)
+ end
expect(page).to have_content 'RSS token was successfully reset'
- expect(find('#rss-token').value).not_to eq(previous_token)
end
end
describe 'when I reset incoming email token' do
before do
allow(Gitlab.config.incoming_email).to receive(:enabled).and_return(true)
- visit profile_account_path
+ visit profile_personal_access_tokens_path
end
it 'resets incoming email token' do
- previous_token = find('#incoming-email-token').value
+ within('.incoming-email-token-reset') do
+ previous_token = find('#incoming_email_token').value
- click_link('Reset incoming email token')
+ click_link('reset it')
- expect(find('#incoming-email-token').value).not_to eq(previous_token)
+ expect(find('#incoming_email_token').value).not_to eq(previous_token)
+ end
end
end
diff --git a/spec/features/projects/commit/mini_pipeline_graph_spec.rb b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
index 807a2189cc4..6f6d562c7b6 100644
--- a/spec/features/projects/commit/mini_pipeline_graph_spec.rb
+++ b/spec/features/projects/commit/mini_pipeline_graph_spec.rb
@@ -18,7 +18,7 @@ feature 'Mini Pipeline Graph in Commit View', :js do
expect(page).to have_selector('.mr-widget-pipeline-graph')
- first('.mini-pipeline-graph-dropdown-toggle').click
+ first('.mini-pipeline-graph-dropdown-toggle').trigger('click')
wait_for_requests
diff --git a/spec/features/projects/pipelines/pipeline_spec.rb b/spec/features/projects/pipelines/pipeline_spec.rb
index 59e32e169ec..b8fa1a54c24 100644
--- a/spec/features/projects/pipelines/pipeline_spec.rb
+++ b/spec/features/projects/pipelines/pipeline_spec.rb
@@ -67,7 +67,7 @@ describe 'Pipeline', :js do
it 'shows a running icon and a cancel action for the running build' do
page.within('#ci-badge-deploy') do
expect(page).to have_selector('.js-ci-status-icon-running')
- expect(page).to have_selector('.js-icon-action-cancel')
+ expect(page).to have_selector('.js-icon-cancel')
expect(page).to have_content('deploy')
end
end
@@ -86,8 +86,8 @@ describe 'Pipeline', :js do
expect(page).to have_content('build')
end
- page.within('#ci-badge-build .ci-action-icon-container') do
- expect(page).to have_selector('.js-icon-action-retry')
+ page.within('#ci-badge-build .ci-action-icon-container.js-icon-retry') do
+ expect(page).to have_selector('svg')
end
end
@@ -105,8 +105,8 @@ describe 'Pipeline', :js do
expect(page).to have_content('test')
end
- page.within('#ci-badge-test .ci-action-icon-container') do
- expect(page).to have_selector('.js-icon-action-retry')
+ page.within('#ci-badge-test .ci-action-icon-container.js-icon-retry') do
+ expect(page).to have_selector('svg')
end
end
@@ -124,8 +124,8 @@ describe 'Pipeline', :js do
expect(page).to have_content('manual')
end
- page.within('#ci-badge-manual-build .ci-action-icon-container') do
- expect(page).to have_selector('.js-icon-action-play')
+ page.within('#ci-badge-manual-build .ci-action-icon-container.js-icon-play') do
+ expect(page).to have_selector('svg')
end
end
diff --git a/spec/features/projects/wiki/user_views_wiki_page_spec.rb b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
index 7d07029ad9b..ff325aeadd3 100644
--- a/spec/features/projects/wiki/user_views_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_views_wiki_page_spec.rb
@@ -81,10 +81,15 @@ describe 'User views a wiki page' do
end
it 'shows a file stored in a page' do
- file = Gollum::File.new(project.wiki)
-
- allow_any_instance_of(Gollum::Wiki).to receive(:file).with('image.jpg', 'master').and_return(file)
- allow_any_instance_of(Gollum::File).to receive(:mime_type).and_return('image/jpeg')
+ gollum_file_double = double('Gollum::File',
+ mime_type: 'image/jpeg',
+ name: 'images/image.jpg',
+ path: 'images/image.jpg',
+ raw_data: '')
+ wiki_file = Gitlab::Git::WikiFile.new(gollum_file_double)
+
+ allow(wiki_file).to receive(:mime_type).and_return('image/jpeg')
+ allow_any_instance_of(ProjectWiki).to receive(:find_file).with('image.jpg', nil).and_return(wiki_file)
expect(page).to have_xpath('//img[@data-src="image.jpg"]')
expect(page).to have_link('image', href: "#{project.wiki.wiki_base_path}/image.jpg")
diff --git a/spec/fixtures/api/schemas/public_api/v4/user/login.json b/spec/fixtures/api/schemas/public_api/v4/user/login.json
index e6c1d9c9d84..aa066883c47 100644
--- a/spec/fixtures/api/schemas/public_api/v4/user/login.json
+++ b/spec/fixtures/api/schemas/public_api/v4/user/login.json
@@ -27,11 +27,9 @@
"can_create_group",
"can_create_project",
"two_factor_enabled",
- "external",
- "private_token"
+ "external"
],
"properties": {
- "$ref": "full.json",
- "private_token": { "type": "string" }
+ "$ref": "full.json"
}
}
diff --git a/spec/helpers/ci_status_helper_spec.rb b/spec/helpers/ci_status_helper_spec.rb
index 6a3945c0ebc..bc2422aba90 100644
--- a/spec/helpers/ci_status_helper_spec.rb
+++ b/spec/helpers/ci_status_helper_spec.rb
@@ -8,17 +8,13 @@ describe CiStatusHelper do
describe '#ci_icon_for_status' do
it 'renders to correct svg on success' do
- expect(helper).to receive(:render)
- .with('shared/icons/icon_status_success.svg', anything)
-
- helper.ci_icon_for_status(success_commit.status)
+ expect(helper.ci_icon_for_status('success').to_s)
+ .to include 'status_success'
end
it 'renders the correct svg on failure' do
- expect(helper).to receive(:render)
- .with('shared/icons/icon_status_failed.svg', anything)
-
- helper.ci_icon_for_status(failed_commit.status)
+ expect(helper.ci_icon_for_status('failed').to_s)
+ .to include 'status_failed'
end
end
diff --git a/spec/helpers/gitlab_routing_helper_spec.rb b/spec/helpers/gitlab_routing_helper_spec.rb
index a44b200c5da..6c4f7050ee0 100644
--- a/spec/helpers/gitlab_routing_helper_spec.rb
+++ b/spec/helpers/gitlab_routing_helper_spec.rb
@@ -63,4 +63,30 @@ describe GitlabRoutingHelper do
it { expect(resend_invite_group_member_path(group_member)).to eq resend_invite_group_group_member_path(group_member.source, group_member) }
end
end
+
+ describe '#preview_markdown_path' do
+ let(:project) { create(:project) }
+
+ it 'returns group preview markdown path for a group parent' do
+ group = create(:group)
+
+ expect(preview_markdown_path(group)).to eq("/groups/#{group.path}/preview_markdown")
+ end
+
+ it 'returns project preview markdown path for a project parent' do
+ expect(preview_markdown_path(project)).to eq("/#{project.full_path}/preview_markdown")
+ end
+
+ it 'returns snippet preview markdown path for a personal snippet' do
+ @snippet = create(:personal_snippet)
+
+ expect(preview_markdown_path(nil)).to eq("/snippets/preview_markdown")
+ end
+
+ it 'returns project preview markdown path for a project snippet' do
+ @snippet = create(:project_snippet, project: project)
+
+ expect(preview_markdown_path(project)).to eq("/#{project.full_path}/preview_markdown")
+ end
+ end
end
diff --git a/spec/helpers/issuables_helper_spec.rb b/spec/helpers/issuables_helper_spec.rb
index ead3e28438e..cb851d828f2 100644
--- a/spec/helpers/issuables_helper_spec.rb
+++ b/spec/helpers/issuables_helper_spec.rb
@@ -159,4 +159,36 @@ describe IssuablesHelper do
end
end
end
+
+ describe '#issuable_initial_data' do
+ let(:user) { create(:user) }
+
+ before do
+ allow(helper).to receive(:current_user).and_return(user)
+ allow(helper).to receive(:can?).and_return(true)
+ end
+
+ it 'returns the correct json for an issue' do
+ issue = create(:issue, author: user, description: 'issue text')
+ @project = issue.project
+
+ expected_data = {
+ 'endpoint' => "/#{@project.full_path}/issues/#{issue.iid}",
+ 'canUpdate' => true,
+ 'canDestroy' => true,
+ 'issuableRef' => "##{issue.iid}",
+ 'markdownPreviewPath' => "/#{@project.full_path}/preview_markdown",
+ 'markdownDocsPath' => '/help/user/markdown',
+ 'issuableTemplates' => [],
+ 'projectPath' => @project.path,
+ 'projectNamespace' => @project.namespace.path,
+ 'initialTitleHtml' => issue.title,
+ 'initialTitleText' => issue.title,
+ 'initialDescriptionHtml' => '<p dir="auto">issue text</p>',
+ 'initialDescriptionText' => 'issue text',
+ 'initialTaskStatus' => '0 of 0 tasks completed'
+ }
+ expect(JSON.parse(helper.issuable_initial_data(issue))).to eq(expected_data)
+ end
+ end
end
diff --git a/spec/javascripts/groups/components/app_spec.js b/spec/javascripts/groups/components/app_spec.js
index cd19a0fae1e..59d4f7c45c6 100644
--- a/spec/javascripts/groups/components/app_spec.js
+++ b/spec/javascripts/groups/components/app_spec.js
@@ -431,9 +431,9 @@ describe('AppComponent', () => {
});
it('should render groups tree', (done) => {
- vm.groups = [mockParentGroupItem];
+ vm.store.state.groups = [mockParentGroupItem];
vm.isLoading = false;
- vm.pageInfo = mockPageInfo;
+ vm.store.state.pageInfo = mockPageInfo;
Vue.nextTick(() => {
expect(vm.$el.querySelector('.groups-list-tree-container')).toBeDefined();
done();
diff --git a/spec/javascripts/issue_spec.js b/spec/javascripts/issue_spec.js
index 60a452f2223..3636aac79a0 100644
--- a/spec/javascripts/issue_spec.js
+++ b/spec/javascripts/issue_spec.js
@@ -1,6 +1,5 @@
/* eslint-disable space-before-function-paren, one-var, one-var-declaration-per-line, no-use-before-define, comma-dangle, max-len */
import Issue from '~/issue';
-import CloseReopenReportToggle from '~/close_reopen_report_toggle';
import '~/lib/utils/text_utility';
describe('Issue', function() {
@@ -189,37 +188,4 @@ describe('Issue', function() {
});
});
});
-
- describe('units', () => {
- describe('class constructor', () => {
- it('calls .initCloseReopenReport', () => {
- spyOn(Issue.prototype, 'initCloseReopenReport');
-
- new Issue(); // eslint-disable-line no-new
-
- expect(Issue.prototype.initCloseReopenReport).toHaveBeenCalled();
- });
- });
-
- describe('initCloseReopenReport', () => {
- it('calls .initDroplab', () => {
- const container = jasmine.createSpyObj('container', ['querySelector']);
- const dropdownTrigger = {};
- const dropdownList = {};
- const button = {};
-
- spyOn(document, 'querySelector').and.returnValue(container);
- spyOn(CloseReopenReportToggle.prototype, 'initDroplab');
- container.querySelector.and.returnValues(dropdownTrigger, dropdownList, button);
-
- Issue.prototype.initCloseReopenReport();
-
- expect(document.querySelector).toHaveBeenCalledWith('.js-issuable-close-dropdown');
- expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-toggle');
- expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-menu');
- expect(container.querySelector).toHaveBeenCalledWith('.js-issuable-close-button');
- expect(CloseReopenReportToggle.prototype.initDroplab).toHaveBeenCalled();
- });
- });
- });
});
diff --git a/spec/javascripts/jobs/mock_data.js b/spec/javascripts/jobs/mock_data.js
index 17e4ef26b2c..43532275121 100644
--- a/spec/javascripts/jobs/mock_data.js
+++ b/spec/javascripts/jobs/mock_data.js
@@ -22,7 +22,7 @@ export default {
details_path: '/root/ci-mock/-/jobs/4757',
favicon: '/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico',
action: {
- icon: 'icon_action_retry',
+ icon: 'retry',
title: 'Retry',
path: '/root/ci-mock/-/jobs/4757/retry',
method: 'post',
diff --git a/spec/javascripts/labels_issue_sidebar_spec.js b/spec/javascripts/labels_issue_sidebar_spec.js
index 4e66304e0ad..a197b35f6fb 100644
--- a/spec/javascripts/labels_issue_sidebar_spec.js
+++ b/spec/javascripts/labels_issue_sidebar_spec.js
@@ -1,13 +1,12 @@
/* eslint-disable no-new */
import IssuableContext from '~/issuable_context';
-/* global LabelsSelect */
+import LabelsSelect from '~/labels_select';
import '~/gl_dropdown';
import 'select2';
import '~/api';
import '~/create_label';
import '~/users_select';
-import '~/labels_select';
(() => {
let saveLabelCount = 0;
diff --git a/spec/javascripts/namespace_select_spec.js b/spec/javascripts/namespace_select_spec.js
new file mode 100644
index 00000000000..9d7625ca269
--- /dev/null
+++ b/spec/javascripts/namespace_select_spec.js
@@ -0,0 +1,65 @@
+import NamespaceSelect from '~/namespace_select';
+
+describe('NamespaceSelect', () => {
+ beforeEach(() => {
+ spyOn($.fn, 'glDropdown');
+ });
+
+ it('initializes glDropdown', () => {
+ const dropdown = document.createElement('div');
+
+ // eslint-disable-next-line no-new
+ new NamespaceSelect({ dropdown });
+
+ expect($.fn.glDropdown).toHaveBeenCalled();
+ });
+
+ describe('as input', () => {
+ let glDropdownOptions;
+
+ beforeEach(() => {
+ const dropdown = document.createElement('div');
+ // eslint-disable-next-line no-new
+ new NamespaceSelect({ dropdown });
+ glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0];
+ });
+
+ it('prevents click events', () => {
+ const dummyEvent = new Event('dummy');
+ spyOn(dummyEvent, 'preventDefault');
+
+ glDropdownOptions.clicked({ e: dummyEvent });
+
+ expect(dummyEvent.preventDefault).toHaveBeenCalled();
+ });
+ });
+
+ describe('as filter', () => {
+ let glDropdownOptions;
+
+ beforeEach(() => {
+ const dropdown = document.createElement('div');
+ dropdown.dataset.isFilter = 'true';
+ // eslint-disable-next-line no-new
+ new NamespaceSelect({ dropdown });
+ glDropdownOptions = $.fn.glDropdown.calls.argsFor(0)[0];
+ });
+
+ it('does not prevent click events', () => {
+ const dummyEvent = new Event('dummy');
+ spyOn(dummyEvent, 'preventDefault');
+
+ glDropdownOptions.clicked({ e: dummyEvent });
+
+ expect(dummyEvent.preventDefault).not.toHaveBeenCalled();
+ });
+
+ it('sets URL of dropdown items', () => {
+ const dummyNamespace = { id: 'eal' };
+
+ const itemUrl = glDropdownOptions.url(dummyNamespace);
+
+ expect(itemUrl).toContain(`namespace_id=${dummyNamespace.id}`);
+ });
+ });
+});
diff --git a/spec/javascripts/pipelines/graph/action_component_spec.js b/spec/javascripts/pipelines/graph/action_component_spec.js
index 85bd87318db..e8fcd4b1a36 100644
--- a/spec/javascripts/pipelines/graph/action_component_spec.js
+++ b/spec/javascripts/pipelines/graph/action_component_spec.js
@@ -11,7 +11,7 @@ describe('pipeline graph action component', () => {
tooltipText: 'bar',
link: 'foo',
actionMethod: 'post',
- actionIcon: 'icon_action_cancel',
+ actionIcon: 'cancel',
},
}).$mount();
diff --git a/spec/javascripts/pipelines/graph/dropdown_action_component_spec.js b/spec/javascripts/pipelines/graph/dropdown_action_component_spec.js
index 25fd18b197e..ba721bc53c6 100644
--- a/spec/javascripts/pipelines/graph/dropdown_action_component_spec.js
+++ b/spec/javascripts/pipelines/graph/dropdown_action_component_spec.js
@@ -11,7 +11,7 @@ describe('action component', () => {
tooltipText: 'bar',
link: 'foo',
actionMethod: 'post',
- actionIcon: 'icon_action_cancel',
+ actionIcon: 'cancel',
},
}).$mount();
diff --git a/spec/javascripts/pipelines/graph/job_component_spec.js b/spec/javascripts/pipelines/graph/job_component_spec.js
index e90593e0f40..342ee6c1242 100644
--- a/spec/javascripts/pipelines/graph/job_component_spec.js
+++ b/spec/javascripts/pipelines/graph/job_component_spec.js
@@ -14,7 +14,7 @@ describe('pipeline graph job component', () => {
group: 'success',
details_path: '/root/ci-mock/builds/4256',
action: {
- icon: 'icon_action_retry',
+ icon: 'retry',
title: 'Retry',
path: '/root/ci-mock/builds/4256/retry',
method: 'post',
diff --git a/spec/javascripts/pipelines/graph/mock_data.js b/spec/javascripts/pipelines/graph/mock_data.js
index 56c522b7f77..b9494f86d74 100644
--- a/spec/javascripts/pipelines/graph/mock_data.js
+++ b/spec/javascripts/pipelines/graph/mock_data.js
@@ -39,7 +39,7 @@ export default {
"details_path": "/root/ci-mock/builds/4153",
"favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
"action": {
- "icon": "icon_action_retry",
+ "icon": "retry",
"title": "Retry",
"path": "/root/ci-mock/builds/4153/retry",
"method": "post"
@@ -62,7 +62,7 @@ export default {
"details_path": "/root/ci-mock/builds/4153",
"favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
"action": {
- "icon": "icon_action_retry",
+ "icon": "retry",
"title": "Retry",
"path": "/root/ci-mock/builds/4153/retry",
"method": "post"
@@ -96,7 +96,7 @@ export default {
"details_path": "/root/ci-mock/builds/4166",
"favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
"action": {
- "icon": "icon_action_retry",
+ "icon": "retry",
"title": "Retry",
"path": "/root/ci-mock/builds/4166/retry",
"method": "post"
@@ -119,7 +119,7 @@ export default {
"details_path": "/root/ci-mock/builds/4166",
"favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
"action": {
- "icon": "icon_action_retry",
+ "icon": "retry",
"title": "Retry",
"path": "/root/ci-mock/builds/4166/retry",
"method": "post"
@@ -138,7 +138,7 @@ export default {
"details_path": "/root/ci-mock/builds/4159",
"favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
"action": {
- "icon": "icon_action_retry",
+ "icon": "retry",
"title": "Retry",
"path": "/root/ci-mock/builds/4159/retry",
"method": "post"
@@ -161,7 +161,7 @@ export default {
"details_path": "/root/ci-mock/builds/4159",
"favicon": "/assets/ci_favicons/dev/favicon_status_success-308b4fc054cdd1b68d0865e6cfb7b02e92e3472f201507418f8eddb74ac11a59.ico",
"action": {
- "icon": "icon_action_retry",
+ "icon": "retry",
"title": "Retry",
"path": "/root/ci-mock/builds/4159/retry",
"method": "post"
diff --git a/spec/javascripts/pipelines/graph/stage_column_component_spec.js b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
index aa4d6eedaf4..063ab53681b 100644
--- a/spec/javascripts/pipelines/graph/stage_column_component_spec.js
+++ b/spec/javascripts/pipelines/graph/stage_column_component_spec.js
@@ -13,7 +13,7 @@ describe('stage column component', () => {
group: 'success',
details_path: '/root/ci-mock/builds/4256',
action: {
- icon: 'icon_action_retry',
+ icon: 'retry',
title: 'Retry',
path: '/root/ci-mock/builds/4256/retry',
method: 'post',
diff --git a/spec/javascripts/repo/components/repo_editor_spec.js b/spec/javascripts/repo/components/repo_editor_spec.js
index 82f914b7c9d..979d2185076 100644
--- a/spec/javascripts/repo/components/repo_editor_spec.js
+++ b/spec/javascripts/repo/components/repo_editor_spec.js
@@ -17,6 +17,7 @@ describe('RepoEditor', () => {
f.active = true;
f.tempFile = true;
vm.$store.state.openFiles.push(f);
+ vm.$store.getters.activeFile.html = 'testing';
vm.monaco = true;
vm.$mount();
@@ -31,18 +32,25 @@ describe('RepoEditor', () => {
it('renders an ide container', (done) => {
Vue.nextTick(() => {
expect(vm.shouldHideEditor).toBeFalsy();
+ expect(vm.$el.textContent.trim()).toBe('');
+
done();
});
});
describe('when open file is binary and not raw', () => {
- it('does not render the IDE', (done) => {
+ beforeEach((done) => {
vm.$store.getters.activeFile.binary = true;
- Vue.nextTick(() => {
- expect(vm.shouldHideEditor).toBeTruthy();
- done();
- });
+ Vue.nextTick(done);
+ });
+
+ it('does not render the IDE', () => {
+ expect(vm.shouldHideEditor).toBeTruthy();
+ });
+
+ it('shows activeFile html', () => {
+ expect(vm.$el.textContent.trim()).toBe('testing');
});
});
});
diff --git a/spec/javascripts/search_autocomplete_spec.js b/spec/javascripts/search_autocomplete_spec.js
index cf811af3d6c..5e55a5d2686 100644
--- a/spec/javascripts/search_autocomplete_spec.js
+++ b/spec/javascripts/search_autocomplete_spec.js
@@ -3,7 +3,6 @@
import '~/gl_dropdown';
import '~/search_autocomplete';
import '~/lib/utils/common_utils';
-import 'vendor/fuzzaldrin-plus';
(function() {
var assertLinks, dashboardIssuesPath, dashboardMRsPath, groupIssuesPath, groupMRsPath, groupName, mockDashboardOptions, mockGroupOptions, mockProjectOptions, projectIssuesPath, projectMRsPath, projectName, userId, widget;
diff --git a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
index 690665ae12c..33ed0cb4342 100644
--- a/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
+++ b/spec/javascripts/vue_mr_widget/components/mr_widget_pipeline_spec.js
@@ -1,5 +1,4 @@
import Vue from 'vue';
-import { statusIconEntityMap } from '~/vue_shared/ci_status_icons';
import pipelineComponent from '~/vue_merge_request_widget/components/mr_widget_pipeline';
import mockData from '../mock_data';
@@ -29,14 +28,6 @@ describe('MRWidgetPipeline', () => {
});
describe('computed', () => {
- describe('svg', () => {
- it('should have the proper SVG icon', () => {
- const vm = createComponent({ pipeline: mockData.pipeline });
-
- expect(vm.svg).toEqual(statusIconEntityMap.icon_status_failed);
- });
- });
-
describe('hasPipeline', () => {
it('should return true when there is a pipeline', () => {
expect(Object.keys(mockData.pipeline).length).toBeGreaterThan(0);
@@ -142,6 +133,7 @@ describe('MRWidgetPipeline', () => {
Vue.nextTick(() => {
expect(el.querySelectorAll('.js-ci-error').length).toEqual(1);
expect(el.innerText).toContain('Could not connect to the CI server');
+ expect(el.querySelector('.ci-status-icon svg use').getAttribute('xlink:href')).toContain('status_failed');
done();
});
});
diff --git a/spec/javascripts/vue_shared/ci_action_icons_spec.js b/spec/javascripts/vue_shared/ci_action_icons_spec.js
deleted file mode 100644
index 3d53a5ab24d..00000000000
--- a/spec/javascripts/vue_shared/ci_action_icons_spec.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import getActionIcon from '~/vue_shared/ci_action_icons';
-import cancelSVG from 'icons/_icon_action_cancel.svg';
-import retrySVG from 'icons/_icon_action_retry.svg';
-import playSVG from 'icons/_icon_action_play.svg';
-import stopSVG from 'icons/_icon_action_stop.svg';
-
-describe('getActionIcon', () => {
- it('should return an empty string', () => {
- expect(getActionIcon()).toEqual('');
- });
-
- it('should return cancel svg', () => {
- expect(getActionIcon('icon_action_cancel')).toEqual(cancelSVG);
- });
-
- it('should return retry svg', () => {
- expect(getActionIcon('icon_action_retry')).toEqual(retrySVG);
- });
-
- it('should return play svg', () => {
- expect(getActionIcon('icon_action_play')).toEqual(playSVG);
- });
-
- it('should render stop svg', () => {
- expect(getActionIcon('icon_action_stop')).toEqual(stopSVG);
- });
-});
diff --git a/spec/javascripts/vue_shared/ci_status_icon_spec.js b/spec/javascripts/vue_shared/ci_status_icon_spec.js
deleted file mode 100644
index b6621d6054d..00000000000
--- a/spec/javascripts/vue_shared/ci_status_icon_spec.js
+++ /dev/null
@@ -1,27 +0,0 @@
-import { borderlessStatusIconEntityMap, statusIconEntityMap } from '~/vue_shared/ci_status_icons';
-
-describe('CI status icons', () => {
- const statuses = [
- 'icon_status_canceled',
- 'icon_status_created',
- 'icon_status_failed',
- 'icon_status_manual',
- 'icon_status_pending',
- 'icon_status_running',
- 'icon_status_skipped',
- 'icon_status_success',
- 'icon_status_warning',
- ];
-
- it('should have a dictionary for borderless icons', () => {
- statuses.forEach((status) => {
- expect(borderlessStatusIconEntityMap[status]).toBeDefined();
- });
- });
-
- it('should have a dictionary for icons', () => {
- statuses.forEach((status) => {
- expect(statusIconEntityMap[status]).toBeDefined();
- });
- });
-});
diff --git a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
index ba303738f71..8762ce9903b 100644
--- a/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
+++ b/spec/javascripts/vue_shared/components/ci_badge_link_spec.js
@@ -11,63 +11,63 @@ describe('CI Badge Link Component', () => {
text: 'canceled',
label: 'canceled',
group: 'canceled',
- icon: 'icon_status_canceled',
+ icon: 'status_canceled',
details_path: 'status/canceled',
},
created: {
text: 'created',
label: 'created',
group: 'created',
- icon: 'icon_status_created',
+ icon: 'status_created',
details_path: 'status/created',
},
failed: {
text: 'failed',
label: 'failed',
group: 'failed',
- icon: 'icon_status_failed',
+ icon: 'status_failed',
details_path: 'status/failed',
},
manual: {
text: 'manual',
label: 'manual action',
group: 'manual',
- icon: 'icon_status_manual',
+ icon: 'status_manual',
details_path: 'status/manual',
},
pending: {
text: 'pending',
label: 'pending',
group: 'pending',
- icon: 'icon_status_pending',
+ icon: 'status_pending',
details_path: 'status/pending',
},
running: {
text: 'running',
label: 'running',
group: 'running',
- icon: 'icon_status_running',
+ icon: 'status_running',
details_path: 'status/running',
},
skipped: {
text: 'skipped',
label: 'skipped',
group: 'skipped',
- icon: 'icon_status_skipped',
+ icon: 'status_skipped',
details_path: 'status/skipped',
},
success_warining: {
text: 'passed',
label: 'passed',
group: 'success_with_warnings',
- icon: 'icon_status_warning',
+ icon: 'status_warning',
details_path: 'status/warning',
},
success: {
text: 'passed',
label: 'passed',
group: 'passed',
- icon: 'icon_status_success',
+ icon: 'status_success',
details_path: 'status/passed',
},
};
diff --git a/spec/javascripts/vue_shared/components/icon_spec.js b/spec/javascripts/vue_shared/components/icon_spec.js
new file mode 100644
index 00000000000..104da4473ce
--- /dev/null
+++ b/spec/javascripts/vue_shared/components/icon_spec.js
@@ -0,0 +1,48 @@
+import Vue from 'vue';
+import Icon from '~/vue_shared/components/icon.vue';
+import mountComponent from '../../helpers/vue_mount_component_helper';
+
+describe('Sprite Icon Component', function () {
+ describe('Initialization', function () {
+ let icon;
+
+ beforeEach(function () {
+ const IconComponent = Vue.extend(Icon);
+
+ icon = mountComponent(IconComponent, {
+ name: 'test',
+ size: 99,
+ cssClasses: 'extraclasses',
+ });
+ });
+
+ afterEach(() => {
+ icon.$destroy();
+ });
+
+ it('should return a defined Vue component', function () {
+ expect(icon).toBeDefined();
+ });
+
+ it('should have <svg> as a child element', function () {
+ expect(icon.$el.tagName).toBe('svg');
+ });
+
+ it('should have <use> as a child element with the correct href', function () {
+ expect(icon.$el.firstChild.tagName).toBe('use');
+ expect(icon.$el.firstChild.getAttribute('xlink:href')).toBe(`${gon.sprite_icons}#test`);
+ });
+
+ it('should properly compute iconSizeClass', function () {
+ expect(icon.iconSizeClass).toBe('s99');
+ });
+
+ it('should properly render img css', function () {
+ const classList = icon.$el.classList;
+ const containsSizeClass = classList.contains('s99');
+ const containsCustomClass = classList.contains('extraclasses');
+ expect(containsSizeClass).toBe(true);
+ expect(containsCustomClass).toBe(true);
+ });
+ });
+});
diff --git a/spec/javascripts/vue_shared/components/markdown/field_spec.js b/spec/javascripts/vue_shared/components/markdown/field_spec.js
index 60a5c2ae74e..65c49b9f30b 100644
--- a/spec/javascripts/vue_shared/components/markdown/field_spec.js
+++ b/spec/javascripts/vue_shared/components/markdown/field_spec.js
@@ -42,12 +42,14 @@ describe('Markdown field component', () => {
beforeEach(() => {
spyOn(Vue.http, 'post').and.callFake(() => new Promise((resolve) => {
- resolve({
- json() {
- return {
- body: '<p>markdown preview</p>',
- };
- },
+ setTimeout(() => {
+ resolve({
+ json() {
+ return {
+ body: '<p>markdown preview</p>',
+ };
+ },
+ });
});
}));
diff --git a/spec/lib/banzai/filter/issue_reference_filter_spec.rb b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
index 9c74c9b8c99..3c98b18f99b 100644
--- a/spec/lib/banzai/filter/issue_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/issue_reference_filter_spec.rb
@@ -317,6 +317,68 @@ describe Banzai::Filter::IssueReferenceFilter do
end
end
+ context 'group context' do
+ let(:group) { create(:group) }
+ let(:context) { { project: nil, group: group } }
+
+ it 'ignores shorthanded issue reference' do
+ reference = "##{issue.iid}"
+ text = "Fixed #{reference}"
+
+ expect(reference_filter(text, context).to_html).to eq(text)
+ end
+
+ it 'ignores valid references when cross-reference project uses external tracker' do
+ expect_any_instance_of(described_class).to receive(:find_object)
+ .with(project, issue.iid)
+ .and_return(nil)
+
+ reference = "#{project.full_path}##{issue.iid}"
+ text = "Issue #{reference}"
+
+ expect(reference_filter(text, context).to_html).to eq(text)
+ end
+
+ it 'links to a valid reference for complete cross-reference' do
+ reference = "#{project.full_path}##{issue.iid}"
+ doc = reference_filter("See #{reference}", context)
+
+ expect(doc.css('a').first.attr('href')).to eq helper.url_for_issue(issue.iid, project)
+ end
+
+ it 'ignores reference for shorthand cross-reference' do
+ reference = "#{project.path}##{issue.iid}"
+ text = "See #{reference}"
+
+ expect(reference_filter(text, context).to_html).to eq(text)
+ end
+
+ it 'links to a valid reference for url cross-reference' do
+ reference = helper.url_for_issue(issue.iid, project) + "#note_123"
+
+ doc = reference_filter("See #{reference}", context)
+
+ expect(doc.css('a').first.attr('href')).to eq(helper.url_for_issue(issue.iid, project) + "#note_123")
+ end
+
+ it 'links to a valid reference for cross-reference in link href' do
+ reference = "#{helper.url_for_issue(issue.iid, project) + "#note_123"}"
+ reference_link = %{<a href="#{reference}">Reference</a>}
+
+ doc = reference_filter("See #{reference_link}", context)
+
+ expect(doc.css('a').first.attr('href')).to eq helper.url_for_issue(issue.iid, project) + "#note_123"
+ end
+
+ it 'links to a valid reference for issue reference in the link href' do
+ reference = issue.to_reference(group)
+ reference_link = %{<a href="#{reference}">Reference</a>}
+ doc = reference_filter("See #{reference_link}", context)
+
+ expect(doc.css('a').first.attr('href')).to eq helper.url_for_issue(issue.iid, project)
+ end
+ end
+
describe '#issues_per_project' do
context 'using an internal issue tracker' do
it 'returns a Hash containing the issues per project' do
diff --git a/spec/lib/banzai/filter/label_reference_filter_spec.rb b/spec/lib/banzai/filter/label_reference_filter_spec.rb
index 2cd30a5e302..862b1fe3fd3 100644
--- a/spec/lib/banzai/filter/label_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/label_reference_filter_spec.rb
@@ -594,4 +594,16 @@ describe Banzai::Filter::LabelReferenceFilter do
expect(reference_filter(act).to_html).to eq exp
end
end
+
+ describe 'group context' do
+ it 'points to referenced project issues page' do
+ project = create(:project)
+ label = create(:label, project: project)
+ reference = "#{project.full_path}~#{label.name}"
+
+ result = reference_filter("See #{reference}", { project: nil, group: create(:group) } )
+
+ expect(result.css('a').first.attr('href')).to eq(urls.project_issues_url(project, label_name: label.name))
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
index ed2788f8a33..158844e25ae 100644
--- a/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/merge_request_reference_filter_spec.rb
@@ -214,4 +214,14 @@ describe Banzai::Filter::MergeRequestReferenceFilter do
expect(doc.to_html).to match(/\(<a.+>#{Regexp.escape(merge.to_reference(project))} \(diffs, comment 123\)<\/a>\.\)/)
end
end
+
+ context 'group context' do
+ it 'links to a valid reference' do
+ reference = "#{project.full_path}!#{merge.iid}"
+
+ result = reference_filter("See #{reference}", { project: nil, group: create(:group) } )
+
+ expect(result.css('a').first.attr('href')).to eq(urls.project_merge_request_url(project, merge))
+ 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 fe7a8c84c9e..84578668133 100644
--- a/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/milestone_reference_filter_spec.rb
@@ -343,4 +343,15 @@ describe Banzai::Filter::MilestoneReferenceFilter do
expect(doc.css('a')).to be_empty
end
end
+
+ context 'group context' do
+ it 'links to a valid reference' do
+ milestone = create(:milestone, project: project)
+ reference = "#{project.full_path}%#{milestone.iid}"
+
+ result = reference_filter("See #{reference}", { project: nil, group: create(:group) } )
+
+ expect(result.css('a').first.attr('href')).to eq(urls.milestone_url(milestone))
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
index 90ac4c7b238..3a07a6dc179 100644
--- a/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/snippet_reference_filter_spec.rb
@@ -201,4 +201,14 @@ describe Banzai::Filter::SnippetReferenceFilter do
expect(reference_filter(act).to_html).to match(/<a.+>#{Regexp.escape(invalidate_reference(reference))}<\/a>/)
end
end
+
+ context 'group context' do
+ it 'links to a valid reference' do
+ reference = "#{project.full_path}$#{snippet.id}"
+
+ result = reference_filter("See #{reference}", { project: nil, group: create(:group) } )
+
+ expect(result.css('a').first.attr('href')).to eq(urls.project_snippet_url(project, snippet))
+ end
+ end
end
diff --git a/spec/lib/banzai/filter/user_reference_filter_spec.rb b/spec/lib/banzai/filter/user_reference_filter_spec.rb
index 34dac1db69a..fc03741976e 100644
--- a/spec/lib/banzai/filter/user_reference_filter_spec.rb
+++ b/spec/lib/banzai/filter/user_reference_filter_spec.rb
@@ -208,6 +208,39 @@ describe Banzai::Filter::UserReferenceFilter do
end
end
+ context 'in group context' do
+ let(:group) { create(:group) }
+ let(:group_member) { create(:user) }
+
+ before do
+ group.add_developer(group_member)
+ end
+
+ let(:context) { { author: group_member, project: nil, group: group } }
+
+ it 'supports a special @all mention' do
+ reference = User.reference_prefix + 'all'
+ doc = reference_filter("Hey #{reference}", context)
+
+ expect(doc.css('a').length).to eq(1)
+ expect(doc.css('a').first.attr('href')).to eq urls.group_url(group)
+ end
+
+ it 'supports mentioning a single user' do
+ reference = group_member.to_reference
+ doc = reference_filter("Hey #{reference}", context)
+
+ expect(doc.css('a').first.attr('href')).to eq urls.user_url(group_member)
+ end
+
+ it 'supports mentioning a group' do
+ reference = group.to_reference
+ doc = reference_filter("Hey #{reference}", context)
+
+ expect(doc.css('a').first.attr('href')).to eq urls.user_url(group)
+ end
+ end
+
describe '#namespaces' do
it 'returns a Hash containing all Namespaces' do
document = Nokogiri::HTML.fragment("<p>#{user.to_reference}</p>")
diff --git a/spec/lib/gitlab/auth_spec.rb b/spec/lib/gitlab/auth_spec.rb
index af1db2c3455..54a853c9ce3 100644
--- a/spec/lib/gitlab/auth_spec.rb
+++ b/spec/lib/gitlab/auth_spec.rb
@@ -5,7 +5,7 @@ describe Gitlab::Auth do
describe 'constants' do
it 'API_SCOPES contains all scopes for API access' do
- expect(subject::API_SCOPES).to eq [:api, :read_user]
+ expect(subject::API_SCOPES).to eq %i[api read_user sudo]
end
it 'OPENID_SCOPES contains all scopes for OpenID Connect' do
@@ -19,7 +19,7 @@ describe Gitlab::Auth do
it 'optional_scopes contains all non-default scopes' do
stub_container_registry_config(enabled: true)
- expect(subject.optional_scopes).to eq %i[read_user read_registry openid]
+ expect(subject.optional_scopes).to eq %i[read_user sudo read_registry openid]
end
context 'registry_scopes' do
@@ -164,7 +164,7 @@ describe Gitlab::Auth do
personal_access_token = create(:personal_access_token, scopes: ['api'])
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
- expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_token, full_authentication_abilities))
+ expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_access_token, full_authentication_abilities))
end
context 'when registry is enabled' do
@@ -176,7 +176,7 @@ describe Gitlab::Auth do
personal_access_token = create(:personal_access_token, scopes: ['read_registry'])
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
- expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_token, [:read_container_image]))
+ expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_access_token, [:read_container_image]))
end
end
@@ -184,14 +184,14 @@ describe Gitlab::Auth do
impersonation_token = create(:personal_access_token, :impersonation, scopes: ['api'])
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
- expect(gl_auth.find_for_git_client('', impersonation_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(impersonation_token.user, nil, :personal_token, full_authentication_abilities))
+ expect(gl_auth.find_for_git_client('', impersonation_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(impersonation_token.user, nil, :personal_access_token, full_authentication_abilities))
end
it 'limits abilities based on scope' do
personal_access_token = create(:personal_access_token, scopes: ['read_user'])
expect(gl_auth).to receive(:rate_limit!).with('ip', success: true, login: '')
- expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_token, []))
+ expect(gl_auth.find_for_git_client('', personal_access_token.token, project: nil, ip: 'ip')).to eq(Gitlab::Auth::Result.new(personal_access_token.user, nil, :personal_access_token, []))
end
it 'fails if password is nil' do
@@ -234,7 +234,7 @@ describe Gitlab::Auth do
it 'throws an error suggesting user create a PAT when internal auth is disabled' do
allow_any_instance_of(ApplicationSetting).to receive(:password_authentication_enabled?) { false }
- expect { gl_auth.find_for_git_client('foo', 'bar', project: nil, ip: 'ip') }.to raise_error(Gitlab::Auth::MissingPersonalTokenError)
+ expect { gl_auth.find_for_git_client('foo', 'bar', project: nil, ip: 'ip') }.to raise_error(Gitlab::Auth::MissingPersonalAccessTokenError)
end
end
diff --git a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
index 5a7a42d84c0..9cdebaa5cf2 100644
--- a/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/cancelable_spec.rb
@@ -66,7 +66,7 @@ describe Gitlab::Ci::Status::Build::Cancelable do
end
describe '#action_icon' do
- it { expect(subject.action_icon).to eq 'icon_action_cancel' }
+ it { expect(subject.action_icon).to eq 'cancel' }
end
describe '#action_title' do
diff --git a/spec/lib/gitlab/ci/status/build/factory_spec.rb b/spec/lib/gitlab/ci/status/build/factory_spec.rb
index 8768302eda1..2b32e47e9ba 100644
--- a/spec/lib/gitlab/ci/status/build/factory_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/factory_spec.rb
@@ -30,7 +30,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'passed'
- expect(status.icon).to eq 'icon_status_success'
+ expect(status.icon).to eq 'status_success'
expect(status.favicon).to eq 'favicon_status_success'
expect(status.label).to eq 'passed'
expect(status).to have_details
@@ -57,7 +57,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'failed'
- expect(status.icon).to eq 'icon_status_failed'
+ expect(status.icon).to eq 'status_failed'
expect(status.favicon).to eq 'favicon_status_failed'
expect(status.label).to eq 'failed'
expect(status).to have_details
@@ -84,7 +84,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'failed'
- expect(status.icon).to eq 'icon_status_warning'
+ expect(status.icon).to eq 'warning'
expect(status.favicon).to eq 'favicon_status_failed'
expect(status.label).to eq 'failed (allowed to fail)'
expect(status).to have_details
@@ -113,7 +113,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'canceled'
- expect(status.icon).to eq 'icon_status_canceled'
+ expect(status.icon).to eq 'status_canceled'
expect(status.favicon).to eq 'favicon_status_canceled'
expect(status.label).to eq 'canceled'
expect(status).to have_details
@@ -139,7 +139,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'running'
- expect(status.icon).to eq 'icon_status_running'
+ expect(status.icon).to eq 'status_running'
expect(status.favicon).to eq 'favicon_status_running'
expect(status.label).to eq 'running'
expect(status).to have_details
@@ -165,7 +165,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'pending'
- expect(status.icon).to eq 'icon_status_pending'
+ expect(status.icon).to eq 'status_pending'
expect(status.favicon).to eq 'favicon_status_pending'
expect(status.label).to eq 'pending'
expect(status).to have_details
@@ -190,7 +190,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'skipped'
- expect(status.icon).to eq 'icon_status_skipped'
+ expect(status.icon).to eq 'status_skipped'
expect(status.favicon).to eq 'favicon_status_skipped'
expect(status.label).to eq 'skipped'
expect(status).to have_details
@@ -219,7 +219,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'manual'
expect(status.group).to eq 'manual'
- expect(status.icon).to eq 'icon_status_manual'
+ expect(status.icon).to eq 'status_manual'
expect(status.favicon).to eq 'favicon_status_manual'
expect(status.label).to include 'manual play action'
expect(status).to have_details
@@ -274,7 +274,7 @@ describe Gitlab::Ci::Status::Build::Factory do
it 'fabricates status with correct details' do
expect(status.text).to eq 'manual'
expect(status.group).to eq 'manual'
- expect(status.icon).to eq 'icon_status_manual'
+ expect(status.icon).to eq 'status_manual'
expect(status.favicon).to eq 'favicon_status_manual'
expect(status.label).to eq 'manual stop action (not allowed)'
expect(status).to have_details
diff --git a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
index 20f71459738..79a65fc67e8 100644
--- a/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/failed_allowed_spec.rb
@@ -18,7 +18,7 @@ describe Gitlab::Ci::Status::Build::FailedAllowed do
describe '#icon' do
it 'returns a warning icon' do
- expect(subject.icon).to eq 'icon_status_warning'
+ expect(subject.icon).to eq 'warning'
end
end
diff --git a/spec/lib/gitlab/ci/status/build/play_spec.rb b/spec/lib/gitlab/ci/status/build/play_spec.rb
index 32b2e62e4e0..81d5f553fd1 100644
--- a/spec/lib/gitlab/ci/status/build/play_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/play_spec.rb
@@ -46,7 +46,7 @@ describe Gitlab::Ci::Status::Build::Play do
end
describe '#action_icon' do
- it { expect(subject.action_icon).to eq 'icon_action_play' }
+ it { expect(subject.action_icon).to eq 'play' }
end
describe '#action_title' do
diff --git a/spec/lib/gitlab/ci/status/build/retryable_spec.rb b/spec/lib/gitlab/ci/status/build/retryable_spec.rb
index 21026f2c968..14d42e0d70f 100644
--- a/spec/lib/gitlab/ci/status/build/retryable_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/retryable_spec.rb
@@ -66,7 +66,7 @@ describe Gitlab::Ci::Status::Build::Retryable do
end
describe '#action_icon' do
- it { expect(subject.action_icon).to eq 'icon_action_retry' }
+ it { expect(subject.action_icon).to eq 'retry' }
end
describe '#action_title' do
diff --git a/spec/lib/gitlab/ci/status/build/stop_spec.rb b/spec/lib/gitlab/ci/status/build/stop_spec.rb
index e0425103f41..18e250772f0 100644
--- a/spec/lib/gitlab/ci/status/build/stop_spec.rb
+++ b/spec/lib/gitlab/ci/status/build/stop_spec.rb
@@ -38,7 +38,7 @@ describe Gitlab::Ci::Status::Build::Stop do
end
describe '#action_icon' do
- it { expect(subject.action_icon).to eq 'icon_action_stop' }
+ it { expect(subject.action_icon).to eq 'stop' }
end
describe '#action_title' do
diff --git a/spec/lib/gitlab/ci/status/canceled_spec.rb b/spec/lib/gitlab/ci/status/canceled_spec.rb
index 530639a5897..dc74d7e28c5 100644
--- a/spec/lib/gitlab/ci/status/canceled_spec.rb
+++ b/spec/lib/gitlab/ci/status/canceled_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::Canceled do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_canceled' }
+ it { expect(subject.icon).to eq 'status_canceled' }
end
describe '#favicon' do
diff --git a/spec/lib/gitlab/ci/status/created_spec.rb b/spec/lib/gitlab/ci/status/created_spec.rb
index aef982e17f1..ce4333f2aca 100644
--- a/spec/lib/gitlab/ci/status/created_spec.rb
+++ b/spec/lib/gitlab/ci/status/created_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::Created do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_created' }
+ it { expect(subject.icon).to eq 'status_created' }
end
describe '#favicon' do
diff --git a/spec/lib/gitlab/ci/status/failed_spec.rb b/spec/lib/gitlab/ci/status/failed_spec.rb
index 9a25743885c..a4a92117c7f 100644
--- a/spec/lib/gitlab/ci/status/failed_spec.rb
+++ b/spec/lib/gitlab/ci/status/failed_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::Failed do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_failed' }
+ it { expect(subject.icon).to eq 'status_failed' }
end
describe '#favicon' do
diff --git a/spec/lib/gitlab/ci/status/manual_spec.rb b/spec/lib/gitlab/ci/status/manual_spec.rb
index 6fdc3801d71..0463f2e1aff 100644
--- a/spec/lib/gitlab/ci/status/manual_spec.rb
+++ b/spec/lib/gitlab/ci/status/manual_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::Manual do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_manual' }
+ it { expect(subject.icon).to eq 'status_manual' }
end
describe '#favicon' do
diff --git a/spec/lib/gitlab/ci/status/pending_spec.rb b/spec/lib/gitlab/ci/status/pending_spec.rb
index ffc53f0506b..0e25358dd8a 100644
--- a/spec/lib/gitlab/ci/status/pending_spec.rb
+++ b/spec/lib/gitlab/ci/status/pending_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::Pending do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_pending' }
+ it { expect(subject.icon).to eq 'status_pending' }
end
describe '#favicon' do
diff --git a/spec/lib/gitlab/ci/status/running_spec.rb b/spec/lib/gitlab/ci/status/running_spec.rb
index 0babf1fb54e..9c9d431bb5d 100644
--- a/spec/lib/gitlab/ci/status/running_spec.rb
+++ b/spec/lib/gitlab/ci/status/running_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::Running do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_running' }
+ it { expect(subject.icon).to eq 'status_running' }
end
describe '#favicon' do
diff --git a/spec/lib/gitlab/ci/status/skipped_spec.rb b/spec/lib/gitlab/ci/status/skipped_spec.rb
index 670747c9f0b..63694ca0ea6 100644
--- a/spec/lib/gitlab/ci/status/skipped_spec.rb
+++ b/spec/lib/gitlab/ci/status/skipped_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::Skipped do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_skipped' }
+ it { expect(subject.icon).to eq 'status_skipped' }
end
describe '#favicon' do
diff --git a/spec/lib/gitlab/ci/status/success_spec.rb b/spec/lib/gitlab/ci/status/success_spec.rb
index ff65b074808..2f67df71c4f 100644
--- a/spec/lib/gitlab/ci/status/success_spec.rb
+++ b/spec/lib/gitlab/ci/status/success_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::Success do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_success' }
+ it { expect(subject.icon).to eq 'status_success' }
end
describe '#favicon' do
diff --git a/spec/lib/gitlab/ci/status/success_warning_spec.rb b/spec/lib/gitlab/ci/status/success_warning_spec.rb
index 7e2269397c6..4582354e739 100644
--- a/spec/lib/gitlab/ci/status/success_warning_spec.rb
+++ b/spec/lib/gitlab/ci/status/success_warning_spec.rb
@@ -14,7 +14,7 @@ describe Gitlab::Ci::Status::SuccessWarning do
end
describe '#icon' do
- it { expect(subject.icon).to eq 'icon_status_warning' }
+ it { expect(subject.icon).to eq 'status_warning' }
end
describe '#group' do
diff --git a/spec/lib/gitlab/git/blob_spec.rb b/spec/lib/gitlab/git/blob_spec.rb
index 412a0093d97..c04a9688503 100644
--- a/spec/lib/gitlab/git/blob_spec.rb
+++ b/spec/lib/gitlab/git/blob_spec.rb
@@ -143,6 +143,16 @@ describe Gitlab::Git::Blob, seed_helper: true do
expect(blob.loaded_size).to eq(blob_size)
end
end
+
+ context 'when sha references a tree' do
+ it 'returns nil' do
+ tree = Gitlab::Git::Commit.find(repository, 'master').tree
+
+ blob = Gitlab::Git::Blob.raw(repository, tree.oid)
+
+ expect(blob).to be_nil
+ end
+ end
end
describe '.raw' do
@@ -226,6 +236,51 @@ describe Gitlab::Git::Blob, seed_helper: true do
end
end
+ describe '.batch_lfs_pointers' do
+ let(:tree_object) { Gitlab::Git::Commit.find(repository, 'master').tree }
+
+ let(:non_lfs_blob) do
+ Gitlab::Git::Blob.find(
+ repository,
+ 'master',
+ 'README.md'
+ )
+ end
+
+ let(:lfs_blob) do
+ Gitlab::Git::Blob.find(
+ repository,
+ '33bcff41c232a11727ac6d660bd4b0c2ba86d63d',
+ 'files/lfs/image.jpg'
+ )
+ end
+
+ it 'returns a list of Gitlab::Git::Blob' do
+ blobs = described_class.batch_lfs_pointers(repository, [lfs_blob.id])
+
+ expect(blobs.count).to eq(1)
+ expect(blobs).to all( be_a(Gitlab::Git::Blob) )
+ end
+
+ it 'silently ignores tree objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [tree_object.oid])
+
+ expect(blobs).to eq([])
+ end
+
+ it 'silently ignores non lfs objects' do
+ blobs = described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+
+ expect(blobs).to eq([])
+ end
+
+ it 'avoids loading large blobs into memory' do
+ expect(repository).not_to receive(:lookup)
+
+ described_class.batch_lfs_pointers(repository, [non_lfs_blob.id])
+ end
+ end
+
describe 'encoding' do
context 'file with russian text' do
let(:blob) { Gitlab::Git::Blob.find(repository, SeedRepo::Commit::ID, "encoding/russian.rb") }
diff --git a/spec/lib/gitlab/git/lfs_changes_spec.rb b/spec/lib/gitlab/git/lfs_changes_spec.rb
new file mode 100644
index 00000000000..c2d2c6e1bc8
--- /dev/null
+++ b/spec/lib/gitlab/git/lfs_changes_spec.rb
@@ -0,0 +1,48 @@
+require 'spec_helper'
+
+describe Gitlab::Git::LfsChanges do
+ let(:project) { create(:project, :repository) }
+ let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' }
+ let(:blob_object_id) { '0c304a93cb8430108629bbbcaa27db3343299bc0' }
+
+ subject { described_class.new(project.repository, newrev) }
+
+ describe 'new_pointers' do
+ before do
+ allow_any_instance_of(Gitlab::Git::RevList).to receive(:new_objects).and_return([blob_object_id])
+ end
+
+ it 'uses rev-list to find new objects' do
+ rev_list = double
+ allow(Gitlab::Git::RevList).to receive(:new).and_return(rev_list)
+
+ expect(rev_list).to receive(:new_objects).and_return([])
+
+ subject.new_pointers
+ end
+
+ it 'filters new objects to find lfs pointers' do
+ expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).with(project.repository, [blob_object_id])
+
+ subject.new_pointers(object_limit: 1)
+ end
+
+ it 'limits new_objects using object_limit' do
+ expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).with(project.repository, [])
+
+ subject.new_pointers(object_limit: 0)
+ end
+ end
+
+ describe 'all_pointers' do
+ it 'uses rev-list to find all objects' do
+ rev_list = double
+ allow(Gitlab::Git::RevList).to receive(:new).and_return(rev_list)
+ allow(rev_list).to receive(:all_objects).and_return([blob_object_id])
+
+ expect(Gitlab::Git::Blob).to receive(:batch_lfs_pointers).with(project.repository, [blob_object_id])
+
+ subject.all_pointers
+ end
+ end
+end
diff --git a/spec/lib/gitlab/git/repository_spec.rb b/spec/lib/gitlab/git/repository_spec.rb
index b161d25b96c..bf6d199ebe2 100644
--- a/spec/lib/gitlab/git/repository_spec.rb
+++ b/spec/lib/gitlab/git/repository_spec.rb
@@ -1163,6 +1163,7 @@ describe Gitlab::Git::Repository, seed_helper: true do
describe "#ls_files" do
let(:master_file_paths) { repository.ls_files("master") }
+ let(:utf8_file_paths) { repository.ls_files("ls-files-utf8") }
let(:not_existed_branch) { repository.ls_files("not_existed_branch") }
it "read every file paths of master branch" do
@@ -1184,6 +1185,10 @@ describe Gitlab::Git::Repository, seed_helper: true do
it "returns empty array when not existed branch" do
expect(not_existed_branch.length).to equal(0)
end
+
+ it "returns valid utf-8 data" do
+ expect(utf8_file_paths.map { |file| file.force_encoding('utf-8') }).to all(be_valid_encoding)
+ end
end
describe "#copy_gitattributes" do
@@ -1336,6 +1341,24 @@ describe Gitlab::Git::Repository, seed_helper: true do
end
end
+ describe '#batch_existence' do
+ let(:refs) { ['deadbeef', SeedRepo::RubyBlob::ID, '909e6157199'] }
+
+ it 'returns existing refs back' do
+ result = repository.batch_existence(refs)
+
+ expect(result).to eq([SeedRepo::RubyBlob::ID])
+ end
+
+ context 'existing: true' do
+ it 'inverts meaning and returns non-existing refs' do
+ result = repository.batch_existence(refs, existing: false)
+
+ expect(result).to eq(%w(deadbeef 909e6157199))
+ end
+ end
+ end
+
describe '#local_branches' do
before(:all) do
@repo = Gitlab::Git::Repository.new('default', TEST_MUTABLE_REPO_PATH, '')
@@ -1609,39 +1632,57 @@ describe Gitlab::Git::Repository, seed_helper: true do
subject { repository.ff_merge(user, source_sha, target_branch) }
- it 'performs a ff_merge' do
- expect(subject.newrev).to eq(source_sha)
- expect(subject.repo_created).to be(false)
- expect(subject.branch_created).to be(false)
+ shared_examples '#ff_merge' do
+ it 'performs a ff_merge' do
+ expect(subject.newrev).to eq(source_sha)
+ expect(subject.repo_created).to be(false)
+ expect(subject.branch_created).to be(false)
- expect(repository.commit(target_branch).id).to eq(source_sha)
- end
+ expect(repository.commit(target_branch).id).to eq(source_sha)
+ end
- context 'with a non-existing target branch' do
- subject { repository.ff_merge(user, source_sha, 'this-isnt-real') }
+ context 'with a non-existing target branch' do
+ subject { repository.ff_merge(user, source_sha, 'this-isnt-real') }
- it 'throws an ArgumentError' do
- expect { subject }.to raise_error(ArgumentError)
+ it 'throws an ArgumentError' do
+ expect { subject }.to raise_error(ArgumentError)
+ end
end
- end
- context 'with a non-existing source commit' do
- let(:source_sha) { 'f001' }
+ context 'with a non-existing source commit' do
+ let(:source_sha) { 'f001' }
- it 'throws an ArgumentError' do
- expect { subject }.to raise_error(ArgumentError)
+ it 'throws an ArgumentError' do
+ expect { subject }.to raise_error(ArgumentError)
+ end
end
- end
- context 'when the source sha is not a descendant of the branch head' do
- let(:source_sha) { '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863' }
+ context 'when the source sha is not a descendant of the branch head' do
+ let(:source_sha) { '1a0b36b3cdad1d2ee32457c102a8c0b7056fa863' }
- it "doesn't perform the ff_merge" do
- expect { subject }.to raise_error(Gitlab::Git::CommitError)
+ it "doesn't perform the ff_merge" do
+ expect { subject }.to raise_error(Gitlab::Git::CommitError)
- expect(repository.commit(target_branch).id).to eq(branch_head)
+ expect(repository.commit(target_branch).id).to eq(branch_head)
+ end
end
end
+
+ context 'with gitaly' do
+ it "calls Gitaly's OperationService" do
+ expect_any_instance_of(Gitlab::GitalyClient::OperationService)
+ .to receive(:user_ff_branch).with(user, source_sha, target_branch)
+ .and_return(nil)
+
+ subject
+ end
+
+ it_behaves_like '#ff_merge'
+ end
+
+ context 'without gitaly', :skip_gitaly_mock do
+ it_behaves_like '#ff_merge'
+ end
end
def create_remote_branch(repository, remote_name, branch_name, source_branch_name)
diff --git a/spec/lib/gitlab/git/rev_list_spec.rb b/spec/lib/gitlab/git/rev_list_spec.rb
index c0eac98d718..643a4b2d03e 100644
--- a/spec/lib/gitlab/git/rev_list_spec.rb
+++ b/spec/lib/gitlab/git/rev_list_spec.rb
@@ -2,53 +2,82 @@ require 'spec_helper'
describe Gitlab::Git::RevList do
let(:project) { create(:project, :repository) }
+ let(:rev_list) { described_class.new(newrev: 'newrev', path_to_repo: project.repository.path_to_repo) }
before do
- expect(Gitlab::Git::Env).to receive(:all).and_return({
+ allow(Gitlab::Git::Env).to receive(:all).and_return({
GIT_OBJECT_DIRECTORY: 'foo',
GIT_ALTERNATE_OBJECT_DIRECTORIES: 'bar'
})
end
- context "#new_refs" do
- let(:rev_list) { described_class.new(newrev: 'newrev', path_to_repo: project.repository.path_to_repo) }
+ def stub_popen_rev_list(*additional_args, output:)
+ expect(rev_list).to receive(:popen).with([
+ Gitlab.config.git.bin_path,
+ "--git-dir=#{project.repository.path_to_repo}",
+ 'rev-list',
+ *additional_args
+ ],
+ nil,
+ {
+ 'GIT_OBJECT_DIRECTORY' => 'foo',
+ 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
+ }).and_return([output, 0])
+ end
+ context "#new_refs" do
it 'calls out to `popen`' do
- expect(rev_list).to receive(:popen).with([
- Gitlab.config.git.bin_path,
- "--git-dir=#{project.repository.path_to_repo}",
- 'rev-list',
- 'newrev',
- '--not',
- '--all'
- ],
- nil,
- {
- 'GIT_OBJECT_DIRECTORY' => 'foo',
- 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
- }).and_return(["sha1\nsha2", 0])
+ stub_popen_rev_list('newrev', '--not', '--all', output: "sha1\nsha2")
expect(rev_list.new_refs).to eq(%w[sha1 sha2])
end
end
+ context '#new_objects' do
+ it 'fetches list of newly pushed objects using rev-list' do
+ stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
+
+ expect(rev_list.new_objects).to eq(%w[sha1 sha2])
+ end
+
+ it 'can skip pathless objects' do
+ stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2 path/to/file")
+
+ expect(rev_list.new_objects(require_path: true)).to eq(%w[sha2])
+ end
+
+ it 'can return a lazy enumerator' do
+ stub_popen_rev_list('newrev', '--not', '--all', '--objects', output: "sha1\nsha2")
+
+ expect(rev_list.new_objects(lazy: true)).to be_a Enumerator::Lazy
+ end
+
+ it 'can accept list of references to exclude' do
+ stub_popen_rev_list('newrev', '--not', 'master', '--objects', output: "sha1\nsha2")
+
+ expect(rev_list.new_objects(not_in: ['master'])).to eq(%w[sha1 sha2])
+ end
+
+ it 'handles empty list of references to exclude as listing all known objects' do
+ stub_popen_rev_list('newrev', '--objects', output: "sha1\nsha2")
+
+ expect(rev_list.new_objects(not_in: [])).to eq(%w[sha1 sha2])
+ end
+ end
+
+ context '#all_objects' do
+ it 'fetches list of all pushed objects using rev-list' do
+ stub_popen_rev_list('--all', '--objects', output: "sha1\nsha2")
+
+ expect(rev_list.all_objects.force).to eq(%w[sha1 sha2])
+ end
+ end
+
context "#missed_ref" do
let(:rev_list) { described_class.new(oldrev: 'oldrev', newrev: 'newrev', path_to_repo: project.repository.path_to_repo) }
it 'calls out to `popen`' do
- expect(rev_list).to receive(:popen).with([
- Gitlab.config.git.bin_path,
- "--git-dir=#{project.repository.path_to_repo}",
- 'rev-list',
- '--max-count=1',
- 'oldrev',
- '^newrev'
- ],
- nil,
- {
- 'GIT_OBJECT_DIRECTORY' => 'foo',
- 'GIT_ALTERNATE_OBJECT_DIRECTORIES' => 'bar'
- }).and_return(["sha1\nsha2", 0])
+ stub_popen_rev_list('--max-count=1', 'oldrev', '^newrev', output: "sha1\nsha2")
expect(rev_list.missed_ref).to eq(%w[sha1 sha2])
end
diff --git a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
index e144e28b5d8..d9ec28ab02e 100644
--- a/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
+++ b/spec/lib/gitlab/gitaly_client/operation_service_spec.rb
@@ -89,4 +89,38 @@ describe Gitlab::GitalyClient::OperationService do
end
end
end
+
+ describe '#user_ff_branch' do
+ let(:target_branch) { 'my-branch' }
+ let(:source_sha) { 'cfe32cf61b73a0d5e9f13e774abde7ff789b1660' }
+ let(:request) do
+ Gitaly::UserFFBranchRequest.new(
+ repository: repository.gitaly_repository,
+ branch: target_branch,
+ commit_id: source_sha,
+ user: gitaly_user
+ )
+ end
+ let(:branch_update) do
+ Gitaly::OperationBranchUpdate.new(
+ commit_id: source_sha,
+ repo_created: false,
+ branch_created: false
+ )
+ end
+ let(:response) { Gitaly::UserFFBranchResponse.new(branch_update: branch_update) }
+
+ subject { client.user_ff_branch(user, source_sha, target_branch) }
+
+ it 'sends a user_ff_branch message and returns a BranchUpdate object' do
+ expect_any_instance_of(Gitaly::OperationService::Stub)
+ .to receive(:user_ff_branch).with(request, kind_of(Hash))
+ .and_return(response)
+
+ expect(subject).to be_a(Gitlab::Git::OperationService::BranchUpdate)
+ expect(subject.newrev).to eq(source_sha)
+ expect(subject.repo_created).to be(false)
+ expect(subject.branch_created).to be(false)
+ end
+ end
end
diff --git a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
index b576d7173f5..0803ce42fac 100644
--- a/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
+++ b/spec/lib/gitlab/metrics/sidekiq_middleware_spec.rb
@@ -4,35 +4,30 @@ describe Gitlab::Metrics::SidekiqMiddleware do
let(:middleware) { described_class.new }
let(:message) { { 'args' => ['test'], 'enqueued_at' => Time.new(2016, 6, 23, 6, 59).to_f } }
- describe '#call' do
- it 'tracks the transaction' do
- worker = double(:worker, class: double(:class, name: 'TestWorker'))
+ def run(worker, message)
+ expect(Gitlab::Metrics::Transaction).to receive(:new)
+ .with('TestWorker#perform')
+ .and_call_original
+
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
+ .with(:sidekiq_queue_duration, instance_of(Float))
- expect(Gitlab::Metrics::Transaction).to receive(:new)
- .with('TestWorker#perform')
- .and_call_original
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
- .with(:sidekiq_queue_duration, instance_of(Float))
+ middleware.call(worker, message, :test) { nil }
+ end
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
+ describe '#call' do
+ it 'tracks the transaction' do
+ worker = double(:worker, class: double(:class, name: 'TestWorker'))
- middleware.call(worker, message, :test) { nil }
+ run(worker, message)
end
it 'tracks the transaction (for messages without `enqueued_at`)' do
worker = double(:worker, class: double(:class, name: 'TestWorker'))
- expect(Gitlab::Metrics::Transaction).to receive(:new)
- .with('TestWorker#perform')
- .and_call_original
-
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:set)
- .with(:sidekiq_queue_duration, instance_of(Float))
-
- expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:finish)
-
- middleware.call(worker, {}, :test) { nil }
+ run(worker, {})
end
it 'tracks any raised exceptions' do
@@ -50,5 +45,18 @@ describe Gitlab::Metrics::SidekiqMiddleware do
expect { middleware.call(worker, message, :test) }
.to raise_error(RuntimeError)
end
+
+ it 'tags the metrics accordingly' do
+ tags = { one: 1, two: 2 }
+ worker = double(:worker, class: double(:class, name: 'TestWorker'))
+ allow(worker).to receive(:metrics_tags).and_return(tags)
+
+ tags.each do |tag, value|
+ expect_any_instance_of(Gitlab::Metrics::Transaction).to receive(:add_tag)
+ .with(tag, value)
+ end
+
+ run(worker, message)
+ end
end
end
diff --git a/spec/lib/gitlab/middleware/go_spec.rb b/spec/lib/gitlab/middleware/go_spec.rb
index cab662819ac..67121937398 100644
--- a/spec/lib/gitlab/middleware/go_spec.rb
+++ b/spec/lib/gitlab/middleware/go_spec.rb
@@ -17,89 +17,115 @@ describe Gitlab::Middleware::Go do
describe 'when go-get=1' do
let(:current_user) { nil }
- context 'with simple 2-segment project path' do
- let!(:project) { create(:project, :private) }
+ shared_examples 'go-get=1' do |enabled_protocol:|
+ context 'with simple 2-segment project path' do
+ let!(:project) { create(:project, :private) }
- context 'with subpackages' do
- let(:path) { "#{project.full_path}/subpackage" }
+ context 'with subpackages' do
+ let(:path) { "#{project.full_path}/subpackage" }
- it 'returns the full project path' do
- expect_response_with_path(go, project.full_path)
- end
- end
-
- context 'without subpackages' do
- let(:path) { project.full_path }
-
- it 'returns the full project path' do
- expect_response_with_path(go, project.full_path)
+ it 'returns the full project path' do
+ expect_response_with_path(go, enabled_protocol, project.full_path)
+ end
end
- end
- end
- context 'with a nested project path' do
- let(:group) { create(:group, :nested) }
- let!(:project) { create(:project, :public, namespace: group) }
+ context 'without subpackages' do
+ let(:path) { project.full_path }
- shared_examples 'a nested project' do
- context 'when the project is public' do
it 'returns the full project path' do
- expect_response_with_path(go, project.full_path)
+ expect_response_with_path(go, enabled_protocol, project.full_path)
end
end
+ end
- context 'when the project is private' do
- before do
- project.update_attribute(:visibility_level, Project::PRIVATE)
- end
+ context 'with a nested project path' do
+ let(:group) { create(:group, :nested) }
+ let!(:project) { create(:project, :public, namespace: group) }
- context 'with access to the project' do
- let(:current_user) { project.creator }
+ shared_examples 'a nested project' do
+ context 'when the project is public' do
+ it 'returns the full project path' do
+ expect_response_with_path(go, enabled_protocol, project.full_path)
+ end
+ end
+ context 'when the project is private' do
before do
- project.team.add_master(current_user)
+ project.update_attribute(:visibility_level, Project::PRIVATE)
end
- it 'returns the full project path' do
- expect_response_with_path(go, project.full_path)
+ context 'with access to the project' do
+ let(:current_user) { project.creator }
+
+ before do
+ project.team.add_master(current_user)
+ end
+
+ it 'returns the full project path' do
+ expect_response_with_path(go, enabled_protocol, project.full_path)
+ end
end
- end
- context 'without access to the project' do
- it 'returns the 2-segment group path' do
- expect_response_with_path(go, group.full_path)
+ context 'without access to the project' do
+ it 'returns the 2-segment group path' do
+ expect_response_with_path(go, enabled_protocol, group.full_path)
+ end
end
end
end
- end
- context 'with subpackages' do
- let(:path) { "#{project.full_path}/subpackage" }
+ context 'with subpackages' do
+ let(:path) { "#{project.full_path}/subpackage" }
- it_behaves_like 'a nested project'
- end
+ it_behaves_like 'a nested project'
+ end
+
+ context 'with a subpackage that is not a valid project path' do
+ let(:path) { "#{project.full_path}/---subpackage" }
- context 'with a subpackage that is not a valid project path' do
- let(:path) { "#{project.full_path}/---subpackage" }
+ it_behaves_like 'a nested project'
+ end
+
+ context 'without subpackages' do
+ let(:path) { project.full_path }
- it_behaves_like 'a nested project'
+ it_behaves_like 'a nested project'
+ end
end
- context 'without subpackages' do
- let(:path) { project.full_path }
+ context 'with a bogus path' do
+ let(:path) { "http:;url=http:&sol;&sol;www.example.com'http-equiv='refresh'x='?go-get=1" }
+
+ it 'skips go-import generation' do
+ expect(app).to receive(:call).and_return('no-go')
- it_behaves_like 'a nested project'
+ go
+ end
+ end
+ end
+
+ context 'with SSH disabled' do
+ before do
+ stub_application_setting(enabled_git_access_protocol: 'http')
end
+
+ include_examples 'go-get=1', enabled_protocol: :http
end
- context 'with a bogus path' do
- let(:path) { "http:;url=http:&sol;&sol;www.example.com'http-equiv='refresh'x='?go-get=1" }
+ context 'with HTTP disabled' do
+ before do
+ stub_application_setting(enabled_git_access_protocol: 'ssh')
+ end
- it 'skips go-import generation' do
- expect(app).to receive(:call).and_return('no-go')
+ include_examples 'go-get=1', enabled_protocol: :ssh
+ end
- go
+ context 'with nothing disabled' do
+ before do
+ stub_application_setting(enabled_git_access_protocol: nil)
end
+
+ include_examples 'go-get=1', enabled_protocol: nil
end
end
@@ -113,10 +139,16 @@ describe Gitlab::Middleware::Go do
middleware.call(env)
end
- def expect_response_with_path(response, path)
+ def expect_response_with_path(response, protocol, path)
+ repository_url = case protocol
+ when :ssh
+ "ssh://git@#{Gitlab.config.gitlab.host}/#{path}.git"
+ when :http, nil
+ "http://#{Gitlab.config.gitlab.host}/#{path}.git"
+ end
expect(response[0]).to eq(200)
expect(response[1]['Content-Type']).to eq('text/html')
- expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git http://#{Gitlab.config.gitlab.host}/#{path}.git" /></head></html>}
+ expected_body = %{<html><head><meta name="go-import" content="#{Gitlab.config.gitlab.host}/#{path} git #{repository_url}" /></head></html>}
expect(response[2].body).to eq([expected_body])
end
end
diff --git a/spec/lib/gitlab/middleware/read_only_spec.rb b/spec/lib/gitlab/middleware/read_only_spec.rb
index 742a792a1af..86be06ff595 100644
--- a/spec/lib/gitlab/middleware/read_only_spec.rb
+++ b/spec/lib/gitlab/middleware/read_only_spec.rb
@@ -83,6 +83,13 @@ describe Gitlab::Middleware::ReadOnly do
expect(subject).to disallow_request
end
+ it 'expects POST of new file that looks like an LFS batch url to be disallowed' do
+ response = request.post('/root/gitlab-ce/new/master/app/info/lfs/objects/batch')
+
+ expect(response).to be_a_redirect
+ expect(subject).to disallow_request
+ end
+
context 'whitelisted requests' do
it 'expects DELETE request to logout to be allowed' do
response = request.delete('/users/sign_out')
@@ -104,6 +111,25 @@ describe Gitlab::Middleware::ReadOnly do
expect(response).not_to be_a_redirect
expect(subject).not_to disallow_request
end
+
+ it 'expects a POST request to git-upload-pack URL to be allowed' do
+ response = request.post('/root/rouge.git/git-upload-pack')
+
+ expect(response).not_to be_a_redirect
+ expect(subject).not_to disallow_request
+ end
+
+ it 'expects requests to sidekiq admin to be allowed' do
+ response = request.post('/admin/sidekiq')
+
+ expect(response).not_to be_a_redirect
+ expect(subject).not_to disallow_request
+
+ response = request.get('/admin/sidekiq')
+
+ expect(response).not_to be_a_redirect
+ expect(subject).not_to disallow_request
+ end
end
end
diff --git a/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
new file mode 100644
index 00000000000..8fdbbacd04d
--- /dev/null
+++ b/spec/lib/gitlab/sidekiq_middleware/memory_killer_spec.rb
@@ -0,0 +1,63 @@
+require 'spec_helper'
+
+describe Gitlab::SidekiqMiddleware::MemoryKiller do
+ subject { described_class.new }
+ let(:pid) { 999 }
+
+ let(:worker) { double(:worker, class: 'TestWorker') }
+ let(:job) { { 'jid' => 123 } }
+ let(:queue) { 'test_queue' }
+
+ def run
+ thread = subject.call(worker, job, queue) { nil }
+ thread&.join
+ end
+
+ before do
+ allow(subject).to receive(:get_rss).and_return(10.kilobytes)
+ allow(subject).to receive(:pid).and_return(pid)
+ end
+
+ context 'when MAX_RSS is set to 0' do
+ before do
+ stub_const("#{described_class}::MAX_RSS", 0)
+ end
+
+ it 'does nothing' do
+ expect(subject).not_to receive(:sleep)
+
+ run
+ end
+ end
+
+ context 'when MAX_RSS is exceeded' do
+ before do
+ stub_const("#{described_class}::MAX_RSS", 5.kilobytes)
+ end
+
+ it 'sends the STP, TERM and KILL signals at expected times' do
+ expect(subject).to receive(:sleep).with(15 * 60).ordered
+ expect(Process).to receive(:kill).with('SIGSTP', pid).ordered
+
+ expect(subject).to receive(:sleep).with(30).ordered
+ expect(Process).to receive(:kill).with('SIGTERM', pid).ordered
+
+ expect(subject).to receive(:sleep).with(10).ordered
+ expect(Process).to receive(:kill).with('SIGKILL', pid).ordered
+
+ run
+ end
+ end
+
+ context 'when MAX_RSS is not exceeded' do
+ before do
+ stub_const("#{described_class}::MAX_RSS", 15.kilobytes)
+ end
+
+ it 'does nothing' do
+ expect(subject).not_to receive(:sleep)
+
+ run
+ end
+ end
+end
diff --git a/spec/lib/gitlab/workhorse_spec.rb b/spec/lib/gitlab/workhorse_spec.rb
index 80bf7986ee0..249c77dc636 100644
--- a/spec/lib/gitlab/workhorse_spec.rb
+++ b/spec/lib/gitlab/workhorse_spec.rb
@@ -268,7 +268,8 @@ describe Gitlab::Workhorse do
GL_ID: "user-#{user.id}",
GL_USERNAME: user.username,
GL_REPOSITORY: "project-#{project.id}",
- RepoPath: repo_path
+ RepoPath: repo_path,
+ ShowAllRefs: false
}
end
@@ -282,7 +283,8 @@ describe Gitlab::Workhorse do
GL_ID: "user-#{user.id}",
GL_USERNAME: user.username,
GL_REPOSITORY: "wiki-#{project.id}",
- RepoPath: repo_path
+ RepoPath: repo_path,
+ ShowAllRefs: false
}
end
@@ -324,6 +326,12 @@ describe Gitlab::Workhorse do
expect(subject).to include(gitaly_params)
end
+
+ context 'show_all_refs enabled' do
+ subject { described_class.git_http_ok(repository, false, user, action, show_all_refs: true) }
+
+ it { is_expected.to include(ShowAllRefs: true) }
+ end
end
context "when git_receive_pack action is passed" do
@@ -336,6 +344,12 @@ describe Gitlab::Workhorse do
let(:action) { 'info_refs' }
it { expect(subject).to include(gitaly_params) }
+
+ context 'show_all_refs enabled' do
+ subject { described_class.git_http_ok(repository, false, user, action, show_all_refs: true) }
+
+ it { is_expected.to include(ShowAllRefs: true) }
+ end
end
context 'when action passed is not supported by Gitaly' do
diff --git a/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb b/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb
index b4b83b70d1c..a0fb86345f3 100644
--- a/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb
+++ b/spec/lib/system_check/app/git_user_default_ssh_config_check_spec.rb
@@ -39,14 +39,6 @@ describe SystemCheck::App::GitUserDefaultSSHConfigCheck do
it { is_expected.to eq(expected_result) }
end
-
- it 'skips GitLab read-only instances' do
- stub_user
- stub_home_dir
- allow(Gitlab::Database).to receive(:read_only?).and_return(true)
-
- is_expected.to be_truthy
- end
end
describe '#check?' do
diff --git a/spec/migrations/migrate_user_authentication_token_to_personal_access_token_spec.rb b/spec/migrations/migrate_user_authentication_token_to_personal_access_token_spec.rb
new file mode 100644
index 00000000000..b4834705011
--- /dev/null
+++ b/spec/migrations/migrate_user_authentication_token_to_personal_access_token_spec.rb
@@ -0,0 +1,25 @@
+require 'spec_helper'
+require Rails.root.join('db', 'migrate', '20171012125712_migrate_user_authentication_token_to_personal_access_token.rb')
+
+describe MigrateUserAuthenticationTokenToPersonalAccessToken, :migration do
+ let(:users) { table(:users) }
+ let(:personal_access_tokens) { table(:personal_access_tokens) }
+
+ let!(:user) { users.create!(id: 1, email: 'user@example.com', authentication_token: 'user-token', admin: false) }
+ let!(:admin) { users.create!(id: 2, email: 'admin@example.com', authentication_token: 'admin-token', admin: true) }
+
+ it 'migrates private tokens to Personal Access Tokens' do
+ migrate!
+
+ expect(personal_access_tokens.count).to eq(2)
+
+ user_token = personal_access_tokens.find_by(user_id: user.id)
+ admin_token = personal_access_tokens.find_by(user_id: admin.id)
+
+ expect(user_token.token).to eq('user-token')
+ expect(admin_token.token).to eq('admin-token')
+
+ expect(user_token.scopes).to eq(%w[api].to_yaml)
+ expect(admin_token.scopes).to eq(%w[api sudo].to_yaml)
+ end
+end
diff --git a/spec/migrations/populate_merge_requests_latest_merge_request_diff_id_spec.rb b/spec/migrations/populate_merge_requests_latest_merge_request_diff_id_spec.rb
new file mode 100644
index 00000000000..4ea7f441f7c
--- /dev/null
+++ b/spec/migrations/populate_merge_requests_latest_merge_request_diff_id_spec.rb
@@ -0,0 +1,61 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20171026082505_populate_merge_requests_latest_merge_request_diff_id')
+
+describe PopulateMergeRequestsLatestMergeRequestDiffId, :migration do
+ let(:projects_table) { table(:projects) }
+ let(:merge_requests_table) { table(:merge_requests) }
+ let(:merge_request_diffs_table) { table(:merge_request_diffs) }
+
+ let(:project) { projects_table.create!(name: 'gitlab', path: 'gitlab-org/gitlab-ce') }
+
+ def create_mr!(name, diffs: 0)
+ merge_request =
+ merge_requests_table.create!(target_project_id: project.id,
+ target_branch: 'master',
+ source_project_id: project.id,
+ source_branch: name,
+ title: name)
+
+ diffs.times do
+ merge_request_diffs_table.create!(merge_request_id: merge_request.id)
+ end
+
+ merge_request
+ end
+
+ def diffs_for(merge_request)
+ merge_request_diffs_table.where(merge_request_id: merge_request.id)
+ end
+
+ describe '#up' do
+ it 'ignores MRs without diffs' do
+ merge_request_without_diff = create_mr!('without_diff')
+
+ expect(merge_request_without_diff.latest_merge_request_diff_id).to be_nil
+
+ expect { migrate! }
+ .not_to change { merge_request_without_diff.reload.latest_merge_request_diff_id }
+ end
+
+ it 'ignores MRs that have a diff ID already set' do
+ merge_request_with_multiple_diffs = create_mr!('with_multiple_diffs', diffs: 3)
+ diff_id = diffs_for(merge_request_with_multiple_diffs).minimum(:id)
+
+ merge_request_with_multiple_diffs.update!(latest_merge_request_diff_id: diff_id)
+
+ expect { migrate! }
+ .not_to change { merge_request_with_multiple_diffs.reload.latest_merge_request_diff_id }
+ end
+
+ it 'migrates multiple MR diffs to the correct values' do
+ merge_requests = Array.new(3).map.with_index { |_, i| create_mr!(i, diffs: 3) }
+
+ migrate!
+
+ merge_requests.each do |merge_request|
+ expect(merge_request.reload.latest_merge_request_diff_id)
+ .to eq(diffs_for(merge_request).maximum(:id))
+ end
+ end
+ end
+end
diff --git a/spec/models/concerns/token_authenticatable_spec.rb b/spec/models/concerns/token_authenticatable_spec.rb
index 882afeccfc6..dfb83578fce 100644
--- a/spec/models/concerns/token_authenticatable_spec.rb
+++ b/spec/models/concerns/token_authenticatable_spec.rb
@@ -12,7 +12,7 @@ shared_examples 'TokenAuthenticatable' do
end
describe User, 'TokenAuthenticatable' do
- let(:token_field) { :authentication_token }
+ let(:token_field) { :rss_token }
it_behaves_like 'TokenAuthenticatable'
describe 'ensures authentication token' do
diff --git a/spec/models/project_wiki_spec.rb b/spec/models/project_wiki_spec.rb
index 52a2b4b2850..3d46434fc27 100644
--- a/spec/models/project_wiki_spec.rb
+++ b/spec/models/project_wiki_spec.rb
@@ -156,36 +156,35 @@ describe ProjectWiki do
end
describe '#find_file' do
- before do
- file = Gollum::File.new(subject.wiki)
- allow_any_instance_of(Gollum::Wiki)
- .to receive(:file).with('image.jpg', 'master')
- .and_return(file)
- allow_any_instance_of(Gollum::File)
- .to receive(:mime_type)
- .and_return('image/jpeg')
- allow_any_instance_of(Gollum::Wiki)
- .to receive(:file).with('non-existant', 'master')
- .and_return(nil)
- end
+ shared_examples 'finding a wiki file' do
+ before do
+ file = File.open(Rails.root.join('spec', 'fixtures', 'dk.png'))
+ subject.wiki # Make sure the wiki repo exists
- after do
- allow_any_instance_of(Gollum::Wiki).to receive(:file).and_call_original
- allow_any_instance_of(Gollum::File).to receive(:mime_type).and_call_original
- end
+ BareRepoOperations.new(subject.repository.path_to_repo).commit_file(file, 'image.png')
+ end
- it 'returns the latest version of the file if it exists' do
- file = subject.find_file('image.jpg')
- expect(file.mime_type).to eq('image/jpeg')
+ it 'returns the latest version of the file if it exists' do
+ file = subject.find_file('image.png')
+ expect(file.mime_type).to eq('image/png')
+ end
+
+ it 'returns nil if the page does not exist' do
+ expect(subject.find_file('non-existant')).to eq(nil)
+ end
+
+ it 'returns a Gitlab::Git::WikiFile instance' do
+ file = subject.find_file('image.png')
+ expect(file).to be_a Gitlab::Git::WikiFile
+ end
end
- it 'returns nil if the page does not exist' do
- expect(subject.find_file('non-existant')).to eq(nil)
+ context 'when Gitaly wiki_find_file is enabled' do
+ it_behaves_like 'finding a wiki file'
end
- it 'returns a Gitlab::Git::WikiFile instance' do
- file = subject.find_file('image.jpg')
- expect(file).to be_a Gitlab::Git::WikiFile
+ context 'when Gitaly wiki_find_file is disabled', :skip_gitaly_mock do
+ it_behaves_like 'finding a wiki file'
end
end
diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb
index 1c3c9068f12..fb03e320734 100644
--- a/spec/models/user_spec.rb
+++ b/spec/models/user_spec.rb
@@ -346,7 +346,6 @@ describe User do
describe "Respond to" do
it { is_expected.to respond_to(:admin?) }
it { is_expected.to respond_to(:name) }
- it { is_expected.to respond_to(:private_token) }
it { is_expected.to respond_to(:external?) }
end
@@ -526,14 +525,6 @@ describe User do
end
end
- describe 'authentication token' do
- it "has authentication token" do
- user = create(:user)
-
- expect(user.authentication_token).not_to be_blank
- end
- end
-
describe 'ensure incoming email token' do
it 'has incoming email token' do
user = create(:user)
diff --git a/spec/requests/api/doorkeeper_access_spec.rb b/spec/requests/api/doorkeeper_access_spec.rb
index de7ce848a31..308134eba72 100644
--- a/spec/requests/api/doorkeeper_access_spec.rb
+++ b/spec/requests/api/doorkeeper_access_spec.rb
@@ -25,7 +25,7 @@ describe 'doorkeeper access' do
end
end
- describe "authorization by private token" do
+ describe "authorization by OAuth token" do
it "returns authentication success" do
get api("/user", user)
expect(response).to have_gitlab_http_status(200)
@@ -39,20 +39,20 @@ describe 'doorkeeper access' do
end
describe "when user is blocked" do
- it "returns authentication error" do
+ it "returns authorization error" do
user.block
get api("/user"), access_token: token.token
- expect(response).to have_gitlab_http_status(401)
+ expect(response).to have_gitlab_http_status(403)
end
end
describe "when user is ldap_blocked" do
- it "returns authentication error" do
+ it "returns authorization error" do
user.ldap_block
get api("/user"), access_token: token.token
- expect(response).to have_gitlab_http_status(401)
+ expect(response).to have_gitlab_http_status(403)
end
end
end
diff --git a/spec/requests/api/helpers_spec.rb b/spec/requests/api/helpers_spec.rb
index 9f3b5a809d7..6c0996c543d 100644
--- a/spec/requests/api/helpers_spec.rb
+++ b/spec/requests/api/helpers_spec.rb
@@ -28,39 +28,11 @@ describe API::Helpers do
allow_any_instance_of(self.class).to receive(:options).and_return({})
end
- def set_env(user_or_token, identifier)
- clear_env
- clear_param
- env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user_or_token.respond_to?(:private_token) ? user_or_token.private_token : user_or_token
- env[API::Helpers::SUDO_HEADER] = identifier.to_s
- end
-
- def set_param(user_or_token, identifier)
- clear_env
- clear_param
- params[API::APIGuard::PRIVATE_TOKEN_PARAM] = user_or_token.respond_to?(:private_token) ? user_or_token.private_token : user_or_token
- params[API::Helpers::SUDO_PARAM] = identifier.to_s
- end
-
- def clear_env
- env.delete(API::APIGuard::PRIVATE_TOKEN_HEADER)
- env.delete(API::Helpers::SUDO_HEADER)
- end
-
- def clear_param
- params.delete(API::APIGuard::PRIVATE_TOKEN_PARAM)
- params.delete(API::Helpers::SUDO_PARAM)
- end
-
def warden_authenticate_returns(value)
warden = double("warden", authenticate: value)
env['warden'] = warden
end
- def doorkeeper_guard_returns(value)
- allow_any_instance_of(self.class).to receive(:doorkeeper_guard) { value }
- end
-
def error!(message, status, header)
raise Exception.new("#{status} - #{message}")
end
@@ -69,10 +41,6 @@ describe API::Helpers do
subject { current_user }
describe "Warden authentication", :allow_forgery_protection do
- before do
- doorkeeper_guard_returns false
- end
-
context "with invalid credentials" do
context "GET request" do
before do
@@ -160,75 +128,32 @@ describe API::Helpers do
end
end
- describe "when authenticating using a user's private token" do
- it "returns a 401 response for an invalid token" do
- env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token'
- allow_any_instance_of(self.class).to receive(:doorkeeper_guard) { false }
-
- expect { current_user }.to raise_error /401/
- end
-
- it "returns a 401 response for a user without access" do
- env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token
- allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
-
- expect { current_user }.to raise_error /401/
- end
-
- it 'returns a 401 response for a user who is blocked' do
- user.block!
- env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token
-
- expect { current_user }.to raise_error /401/
- end
-
- it "leaves user as is when sudo not specified" do
- env[API::APIGuard::PRIVATE_TOKEN_HEADER] = user.private_token
-
- expect(current_user).to eq(user)
-
- clear_env
-
- params[API::APIGuard::PRIVATE_TOKEN_PARAM] = user.private_token
-
- expect(current_user).to eq(user)
- end
- end
-
describe "when authenticating using a user's personal access tokens" do
let(:personal_access_token) { create(:personal_access_token, user: user) }
- before do
- allow_any_instance_of(self.class).to receive(:doorkeeper_guard) { false }
- end
-
it "returns a 401 response for an invalid token" do
env[API::APIGuard::PRIVATE_TOKEN_HEADER] = 'invalid token'
expect { current_user }.to raise_error /401/
end
- it "returns a 401 response for a user without access" do
+ it "returns a 403 response for a user without access" do
env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token
allow_any_instance_of(Gitlab::UserAccess).to receive(:allowed?).and_return(false)
- expect { current_user }.to raise_error /401/
+ expect { current_user }.to raise_error /403/
end
- it 'returns a 401 response for a user who is blocked' do
+ it 'returns a 403 response for a user who is blocked' do
user.block!
env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token
- expect { current_user }.to raise_error /401/
+ expect { current_user }.to raise_error /403/
end
- it "leaves user as is when sudo not specified" do
+ it "sets current_user" do
env[API::APIGuard::PRIVATE_TOKEN_HEADER] = personal_access_token.token
expect(current_user).to eq(user)
- clear_env
- params[API::APIGuard::PRIVATE_TOKEN_PARAM] = personal_access_token.token
-
- expect(current_user).to eq(user)
end
it "does not allow tokens without the appropriate scope" do
@@ -252,210 +177,6 @@ describe API::Helpers do
expect { current_user }.to raise_error API::APIGuard::ExpiredError
end
end
-
- context 'sudo usage' do
- context 'with admin' do
- context 'with header' do
- context 'with id' do
- it 'changes current_user to sudo' do
- set_env(admin, user.id)
-
- expect(current_user).to eq(user)
- end
-
- it 'memoize the current_user: sudo permissions are not run against the sudoed user' do
- set_env(admin, user.id)
-
- expect(current_user).to eq(user)
- expect(current_user).to eq(user)
- end
-
- it 'handles sudo to oneself' do
- set_env(admin, admin.id)
-
- expect(current_user).to eq(admin)
- end
-
- it 'throws an error when user cannot be found' do
- id = user.id + admin.id
- expect(user.id).not_to eq(id)
- expect(admin.id).not_to eq(id)
-
- set_env(admin, id)
-
- expect { current_user }.to raise_error(Exception)
- end
- end
-
- context 'with username' do
- it 'changes current_user to sudo' do
- set_env(admin, user.username)
-
- expect(current_user).to eq(user)
- end
-
- it 'handles sudo to oneself' do
- set_env(admin, admin.username)
-
- expect(current_user).to eq(admin)
- end
-
- it "throws an error when the user cannot be found for a given username" do
- username = "#{user.username}#{admin.username}"
- expect(user.username).not_to eq(username)
- expect(admin.username).not_to eq(username)
-
- set_env(admin, username)
-
- expect { current_user }.to raise_error(Exception)
- end
- end
- end
-
- context 'with param' do
- context 'with id' do
- it 'changes current_user to sudo' do
- set_param(admin, user.id)
-
- expect(current_user).to eq(user)
- end
-
- it 'handles sudo to oneself' do
- set_param(admin, admin.id)
-
- expect(current_user).to eq(admin)
- end
-
- it 'handles sudo to oneself using string' do
- set_env(admin, user.id.to_s)
-
- expect(current_user).to eq(user)
- end
-
- it 'throws an error when user cannot be found' do
- id = user.id + admin.id
- expect(user.id).not_to eq(id)
- expect(admin.id).not_to eq(id)
-
- set_param(admin, id)
-
- expect { current_user }.to raise_error(Exception)
- end
- end
-
- context 'with username' do
- it 'changes current_user to sudo' do
- set_param(admin, user.username)
-
- expect(current_user).to eq(user)
- end
-
- it 'handles sudo to oneself' do
- set_param(admin, admin.username)
-
- expect(current_user).to eq(admin)
- end
-
- it "throws an error when the user cannot be found for a given username" do
- username = "#{user.username}#{admin.username}"
- expect(user.username).not_to eq(username)
- expect(admin.username).not_to eq(username)
-
- set_param(admin, username)
-
- expect { current_user }.to raise_error(Exception)
- end
- end
- end
-
- context 'when user is blocked' do
- before do
- user.block!
- end
-
- it 'changes current_user to sudo' do
- set_env(admin, user.id)
-
- expect(current_user).to eq(user)
- end
- end
- end
-
- context 'with regular user' do
- context 'with env' do
- it 'changes current_user to sudo when admin and user id' do
- set_env(user, admin.id)
-
- expect { current_user }.to raise_error(Exception)
- end
-
- it 'changes current_user to sudo when admin and user username' do
- set_env(user, admin.username)
-
- expect { current_user }.to raise_error(Exception)
- end
- end
-
- context 'with params' do
- it 'changes current_user to sudo when admin and user id' do
- set_param(user, admin.id)
-
- expect { current_user }.to raise_error(Exception)
- end
-
- it 'changes current_user to sudo when admin and user username' do
- set_param(user, admin.username)
-
- expect { current_user }.to raise_error(Exception)
- end
- end
- end
- end
- end
-
- describe '.sudo?' do
- context 'when no sudo env or param is passed' do
- before do
- doorkeeper_guard_returns(nil)
- end
-
- it 'returns false' do
- expect(sudo?).to be_falsy
- end
- end
-
- context 'when sudo env or param is passed', 'user is not an admin' do
- before do
- set_env(user, '123')
- end
-
- it 'returns an 403 Forbidden' do
- expect { sudo? }.to raise_error '403 - {"message"=>"403 Forbidden - Must be admin to use sudo"}'
- end
- end
-
- context 'when sudo env or param is passed', 'user is admin' do
- context 'personal access token is used' do
- before do
- personal_access_token = create(:personal_access_token, user: admin)
- set_env(personal_access_token.token, user.id)
- end
-
- it 'returns an 403 Forbidden' do
- expect { sudo? }.to raise_error '403 - {"message"=>"403 Forbidden - Private token must be specified in order to use sudo"}'
- end
- end
-
- context 'private access token is used' do
- before do
- set_env(admin.private_token, user.id)
- end
-
- it 'returns true' do
- expect(sudo?).to be_truthy
- end
- end
- end
end
describe '.handle_api_exception' do
@@ -582,4 +303,147 @@ describe API::Helpers do
end
end
end
+
+ context 'sudo' do
+ shared_examples 'successful sudo' do
+ it 'sets current_user' do
+ expect(current_user).to eq(user)
+ end
+
+ it 'sets sudo?' do
+ expect(sudo?).to be_truthy
+ end
+ end
+
+ shared_examples 'sudo' do
+ context 'when admin' do
+ before do
+ token.user = admin
+ token.save!
+ end
+
+ context 'when token has sudo scope' do
+ before do
+ token.scopes = %w[sudo]
+ token.save!
+ end
+
+ context 'when user exists' do
+ context 'when using header' do
+ context 'when providing username' do
+ before do
+ env[API::Helpers::SUDO_HEADER] = user.username
+ end
+
+ it_behaves_like 'successful sudo'
+ end
+
+ context 'when providing user ID' do
+ before do
+ env[API::Helpers::SUDO_HEADER] = user.id.to_s
+ end
+
+ it_behaves_like 'successful sudo'
+ end
+ end
+
+ context 'when using param' do
+ context 'when providing username' do
+ before do
+ params[API::Helpers::SUDO_PARAM] = user.username
+ end
+
+ it_behaves_like 'successful sudo'
+ end
+
+ context 'when providing user ID' do
+ before do
+ params[API::Helpers::SUDO_PARAM] = user.id.to_s
+ end
+
+ it_behaves_like 'successful sudo'
+ end
+ end
+ end
+
+ context 'when user does not exist' do
+ before do
+ params[API::Helpers::SUDO_PARAM] = 'nonexistent'
+ end
+
+ it 'raises an error' do
+ expect { current_user }.to raise_error /User with ID or username 'nonexistent' Not Found/
+ end
+ end
+ end
+
+ context 'when token does not have sudo scope' do
+ before do
+ token.scopes = %w[api]
+ token.save!
+
+ params[API::Helpers::SUDO_PARAM] = user.id.to_s
+ end
+
+ it 'raises an error' do
+ expect { current_user }.to raise_error API::APIGuard::InsufficientScopeError
+ end
+ end
+ end
+
+ context 'when not admin' do
+ before do
+ token.user = user
+ token.save!
+
+ params[API::Helpers::SUDO_PARAM] = user.id.to_s
+ end
+
+ it 'raises an error' do
+ expect { current_user }.to raise_error /Must be admin to use sudo/
+ end
+ end
+ end
+
+ context 'using an OAuth token' do
+ let(:token) { create(:oauth_access_token) }
+
+ before do
+ env['HTTP_AUTHORIZATION'] = "Bearer #{token.token}"
+ end
+
+ it_behaves_like 'sudo'
+ end
+
+ context 'using a personal access token' do
+ let(:token) { create(:personal_access_token) }
+
+ context 'passed as param' do
+ before do
+ params[API::APIGuard::PRIVATE_TOKEN_PARAM] = token.token
+ end
+
+ it_behaves_like 'sudo'
+ end
+
+ context 'passed as header' do
+ before do
+ env[API::APIGuard::PRIVATE_TOKEN_HEADER] = token.token
+ end
+
+ it_behaves_like 'sudo'
+ end
+ end
+
+ context 'using warden authentication' do
+ before do
+ warden_authenticate_returns admin
+ env[API::Helpers::SUDO_HEADER] = user.username
+ end
+
+ it 'raises an error' do
+ expect { current_user }.to raise_error /Must be authenticated using an OAuth or Personal Access Token to use sudo/
+ end
+ end
+ end
end
diff --git a/spec/requests/api/session_spec.rb b/spec/requests/api/session_spec.rb
deleted file mode 100644
index 83d09878813..00000000000
--- a/spec/requests/api/session_spec.rb
+++ /dev/null
@@ -1,107 +0,0 @@
-require 'spec_helper'
-
-describe API::Session do
- let(:user) { create(:user) }
-
- describe "POST /session" do
- context "when valid password" do
- it "returns private token" do
- post api("/session"), email: user.email, password: '12345678'
- expect(response).to have_gitlab_http_status(201)
-
- expect(json_response['email']).to eq(user.email)
- expect(json_response['private_token']).to eq(user.private_token)
- expect(json_response['is_admin']).to eq(user.admin?)
- expect(json_response['can_create_project']).to eq(user.can_create_project?)
- expect(json_response['can_create_group']).to eq(user.can_create_group?)
- end
-
- context 'with 2FA enabled' do
- it 'rejects sign in attempts' do
- user = create(:user, :two_factor)
-
- post api('/session'), email: user.email, password: user.password
-
- expect(response).to have_gitlab_http_status(401)
- expect(response.body).to include('You have 2FA enabled.')
- end
- end
- end
-
- context 'when email has case-typo and password is valid' do
- it 'returns private token' do
- post api('/session'), email: user.email.upcase, password: '12345678'
- expect(response.status).to eq 201
-
- expect(json_response['email']).to eq user.email
- expect(json_response['private_token']).to eq user.private_token
- expect(json_response['is_admin']).to eq user.admin?
- expect(json_response['can_create_project']).to eq user.can_create_project?
- expect(json_response['can_create_group']).to eq user.can_create_group?
- end
- end
-
- context 'when login has case-typo and password is valid' do
- it 'returns private token' do
- post api('/session'), login: user.username.upcase, password: '12345678'
- expect(response.status).to eq 201
-
- expect(json_response['email']).to eq user.email
- expect(json_response['private_token']).to eq user.private_token
- expect(json_response['is_admin']).to eq user.admin?
- expect(json_response['can_create_project']).to eq user.can_create_project?
- expect(json_response['can_create_group']).to eq user.can_create_group?
- end
- end
-
- context "when invalid password" do
- it "returns authentication error" do
- post api("/session"), email: user.email, password: '123'
- expect(response).to have_gitlab_http_status(401)
-
- expect(json_response['email']).to be_nil
- expect(json_response['private_token']).to be_nil
- end
- end
-
- context "when empty password" do
- it "returns authentication error with email" do
- post api("/session"), email: user.email
-
- expect(response).to have_gitlab_http_status(400)
- end
-
- it "returns authentication error with username" do
- post api("/session"), email: user.username
-
- expect(response).to have_gitlab_http_status(400)
- end
- end
-
- context "when empty name" do
- it "returns authentication error" do
- post api("/session"), password: user.password
-
- expect(response).to have_gitlab_http_status(400)
- end
- end
-
- context "when user is blocked" do
- it "returns authentication error" do
- user.block
- post api("/session"), email: user.username, password: user.password
-
- expect(response).to have_gitlab_http_status(401)
- end
- end
-
- context "when user is ldap_blocked" do
- it "returns authentication error" do
- user.ldap_block
- post api("/session"), email: user.username, password: user.password
-
- expect(response).to have_gitlab_http_status(401)
- end
- end
- end
-end
diff --git a/spec/requests/api/users_spec.rb b/spec/requests/api/users_spec.rb
index 4737f034f21..634c8dae0ba 100644
--- a/spec/requests/api/users_spec.rb
+++ b/spec/requests/api/users_spec.rb
@@ -127,8 +127,8 @@ describe API::Users do
context "when admin" do
context 'when sudo is defined' do
it 'does not return 500' do
- admin_personal_access_token = create(:personal_access_token, user: admin).token
- get api("/users?private_token=#{admin_personal_access_token}&sudo=#{user.id}", admin)
+ admin_personal_access_token = create(:personal_access_token, user: admin, scopes: [:sudo])
+ get api("/users?sudo=#{user.id}", admin, personal_access_token: admin_personal_access_token)
expect(response).to have_gitlab_http_status(:success)
end
@@ -1097,14 +1097,6 @@ describe API::Users do
end
end
- context 'with private token' do
- it 'returns 403 without private token when sudo defined' do
- get api("/user?private_token=#{user.private_token}&sudo=123")
-
- expect(response).to have_gitlab_http_status(403)
- end
- end
-
it 'returns current user without private token when sudo not defined' do
get api("/user", user)
@@ -1139,24 +1131,6 @@ describe API::Users do
expect(json_response['id']).to eq(admin.id)
end
end
-
- context 'with private token' do
- it 'returns sudoed user with private token when sudo defined' do
- get api("/user?private_token=#{admin.private_token}&sudo=#{user.id}")
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to match_response_schema('public_api/v4/user/login')
- expect(json_response['id']).to eq(user.id)
- end
-
- it 'returns initial current user without private token but with is_admin when sudo not defined' do
- get api("/user?private_token=#{admin.private_token}")
-
- expect(response).to have_gitlab_http_status(200)
- expect(response).to match_response_schema('public_api/v4/user/admin')
- expect(json_response['id']).to eq(admin.id)
- end
- end
end
context 'with unauthenticated user' do
diff --git a/spec/routing/routing_spec.rb b/spec/routing/routing_spec.rb
index 407d19c3b2a..609481603af 100644
--- a/spec/routing/routing_spec.rb
+++ b/spec/routing/routing_spec.rb
@@ -135,7 +135,6 @@ end
# profile_history GET /profile/history(.:format) profile#history
# profile_password PUT /profile/password(.:format) profile#password_update
# profile_token GET /profile/token(.:format) profile#token
-# profile_reset_private_token PUT /profile/reset_private_token(.:format) profile#reset_private_token
# profile GET /profile(.:format) profile#show
# profile_update PUT /profile/update(.:format) profile#update
describe ProfilesController, "routing" do
@@ -147,10 +146,6 @@ describe ProfilesController, "routing" do
expect(get("/profile/audit_log")).to route_to('profiles#audit_log')
end
- it "to #reset_private_token" do
- expect(put("/profile/reset_private_token")).to route_to('profiles#reset_private_token')
- end
-
it "to #reset_rss_token" do
expect(put("/profile/reset_rss_token")).to route_to('profiles#reset_rss_token')
end
diff --git a/spec/serializers/issue_entity_spec.rb b/spec/serializers/issue_entity_spec.rb
new file mode 100644
index 00000000000..caa3e41402b
--- /dev/null
+++ b/spec/serializers/issue_entity_spec.rb
@@ -0,0 +1,20 @@
+require 'spec_helper'
+
+describe IssueEntity do
+ let(:project) { create(:project) }
+ let(:resource) { create(:issue, project: project) }
+ let(:user) { create(:user) }
+
+ let(:request) { double('request', current_user: user) }
+
+ subject { described_class.new(resource, request: request).as_json }
+
+ it 'has Issuable attributes' do
+ expect(subject).to include(:id, :iid, :author_id, :description, :lock_version, :milestone_id,
+ :title, :updated_by_id, :created_at, :updated_at, :milestone, :labels)
+ end
+
+ it 'has time estimation attributes' do
+ expect(subject).to include(:time_estimate, :total_time_spent, :human_time_estimate, :human_total_time_spent)
+ end
+end
diff --git a/spec/serializers/merge_request_entity_spec.rb b/spec/serializers/merge_request_entity_spec.rb
index 87832b3dca1..f9285049c0d 100644
--- a/spec/serializers/merge_request_entity_spec.rb
+++ b/spec/serializers/merge_request_entity_spec.rb
@@ -30,8 +30,17 @@ describe MergeRequestEntity do
:assign_to_closing)
end
+ it 'has Issuable attributes' do
+ expect(subject).to include(:id, :iid, :author_id, :description, :lock_version, :milestone_id,
+ :title, :updated_by_id, :created_at, :updated_at, :milestone, :labels)
+ end
+
+ it 'has time estimation attributes' do
+ expect(subject).to include(:time_estimate, :total_time_spent, :human_time_estimate, :human_total_time_spent)
+ end
+
it 'has important MergeRequest attributes' do
- expect(subject).to include(:diff_head_sha, :merge_commit_message,
+ expect(subject).to include(:state, :deleted_at, :diff_head_sha, :merge_commit_message,
:has_conflicts, :has_ci, :merge_path,
:conflict_resolution_path,
:cancel_merge_when_pipeline_succeeds_path,
diff --git a/spec/services/issuable/common_system_notes_service_spec.rb b/spec/services/issuable/common_system_notes_service_spec.rb
new file mode 100644
index 00000000000..9f92b662be1
--- /dev/null
+++ b/spec/services/issuable/common_system_notes_service_spec.rb
@@ -0,0 +1,49 @@
+require 'spec_helper'
+
+describe Issuable::CommonSystemNotesService do
+ let(:user) { create(:user) }
+ let(:project) { create(:project) }
+ let(:issuable) { create(:issue) }
+
+ shared_examples 'system note creation' do |update_params, note_text|
+ subject { described_class.new(project, user).execute(issuable, [])}
+
+ before do
+ issuable.assign_attributes(update_params)
+ issuable.save
+ end
+
+ it 'creates 1 system note with the correct content' do
+ expect { subject }.to change { Note.count }.from(0).to(1)
+
+ note = Note.last
+ expect(note.note).to match(note_text)
+ expect(note.noteable_type).to eq('Issue')
+ end
+ end
+
+ describe '#execute' do
+ it_behaves_like 'system note creation', { title: 'New title' }, 'changed title'
+ it_behaves_like 'system note creation', { description: 'New description' }, 'changed the description'
+ it_behaves_like 'system note creation', { discussion_locked: true }, 'locked this issue'
+ it_behaves_like 'system note creation', { time_estimate: 5 }, 'changed time estimate'
+
+ context 'when new label is added' do
+ before do
+ label = create(:label, project: project)
+ issuable.labels << label
+ end
+
+ it_behaves_like 'system note creation', {}, /added ~\w+ label/
+ end
+
+ context 'when new milestone is assigned' do
+ before do
+ milestone = create(:milestone, project: project)
+ issuable.milestone_id = milestone.id
+ end
+
+ it_behaves_like 'system note creation', {}, 'changed milestone'
+ end
+ end
+end
diff --git a/spec/services/projects/group_links/create_service_spec.rb b/spec/services/projects/group_links/create_service_spec.rb
new file mode 100644
index 00000000000..ffb270d277e
--- /dev/null
+++ b/spec/services/projects/group_links/create_service_spec.rb
@@ -0,0 +1,22 @@
+require 'spec_helper'
+
+describe Projects::GroupLinks::CreateService, '#execute' do
+ let(:user) { create :user }
+ let(:group) { create :group }
+ let(:project) { create :project }
+ let(:opts) do
+ {
+ link_group_access: '30',
+ expires_at: nil
+ }
+ end
+ let(:subject) { described_class.new(project, user, opts) }
+
+ it 'adds group to project' do
+ expect { subject.execute(group) }.to change { project.project_group_links.count }.from(0).to(1)
+ end
+
+ it 'returns false if group is blank' do
+ expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
+ end
+end
diff --git a/spec/services/projects/group_links/destroy_service_spec.rb b/spec/services/projects/group_links/destroy_service_spec.rb
new file mode 100644
index 00000000000..336ee01ae50
--- /dev/null
+++ b/spec/services/projects/group_links/destroy_service_spec.rb
@@ -0,0 +1,16 @@
+require 'spec_helper'
+
+describe Projects::GroupLinks::DestroyService, '#execute' do
+ let(:group_link) { create :project_group_link }
+ let(:project) { group_link.project }
+ let(:user) { create :user }
+ let(:subject) { described_class.new(project, user) }
+
+ it 'removes group from project' do
+ expect { subject.execute(group_link) }.to change { project.project_group_links.count }.from(1).to(0)
+ end
+
+ it 'returns false if group_link is blank' do
+ expect { subject.execute(nil) }.not_to change { project.project_group_links.count }
+ end
+end
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb
index 97bd7257e47..7c8331f6c60 100644
--- a/spec/spec_helper.rb
+++ b/spec/spec_helper.rb
@@ -52,6 +52,7 @@ RSpec.configure do |config|
config.include InputHelper, :js
config.include InspectRequests, :js
config.include WaitForRequests, :js
+ config.include LiveDebugger, :js
config.include StubConfiguration
config.include EmailHelpers, :mailer, type: :mailer
config.include TestEnv
diff --git a/spec/support/api_helpers.rb b/spec/support/api_helpers.rb
index 01aca74274c..ac0c7a9b493 100644
--- a/spec/support/api_helpers.rb
+++ b/spec/support/api_helpers.rb
@@ -18,21 +18,23 @@ module ApiHelpers
#
# Returns the relative path to the requested API resource
def api(path, user = nil, version: API::API.version, personal_access_token: nil, oauth_access_token: nil)
- "/api/#{version}#{path}" +
+ full_path = "/api/#{version}#{path}"
- # Normalize query string
- (path.index('?') ? '' : '?') +
+ if oauth_access_token
+ query_string = "access_token=#{oauth_access_token.token}"
+ elsif personal_access_token
+ query_string = "private_token=#{personal_access_token.token}"
+ elsif user
+ personal_access_token = create(:personal_access_token, user: user)
+ query_string = "private_token=#{personal_access_token.token}"
+ end
- if personal_access_token.present?
- "&private_token=#{personal_access_token.token}"
- elsif oauth_access_token.present?
- "&access_token=#{oauth_access_token.token}"
- # Append private_token if given a User object
- elsif user.respond_to?(:private_token)
- "&private_token=#{user.private_token}"
- else
- ''
- end
+ if query_string
+ full_path << (path.index('?') ? '&' : '?')
+ full_path << query_string
+ end
+
+ full_path
end
# Temporary helper method for simplifying V3 exclusive API specs
diff --git a/spec/support/bare_repo_operations.rb b/spec/support/bare_repo_operations.rb
new file mode 100644
index 00000000000..38d11992dc2
--- /dev/null
+++ b/spec/support/bare_repo_operations.rb
@@ -0,0 +1,60 @@
+require 'zlib'
+
+class BareRepoOperations
+ # The ID of empty tree.
+ # See http://stackoverflow.com/a/40884093/1856239 and https://github.com/git/git/blob/3ad8b5bf26362ac67c9020bf8c30eee54a84f56d/cache.h#L1011-L1012
+ EMPTY_TREE_ID = '4b825dc642cb6eb9a060e54bf8d69288fbee4904'.freeze
+
+ include Gitlab::Popen
+
+ def initialize(path_to_repo)
+ @path_to_repo = path_to_repo
+ end
+
+ # Based on https://stackoverflow.com/a/25556917/1856239
+ def commit_file(file, dst_path, branch = 'master')
+ head_id = execute(['show', '--format=format:%H', '--no-patch', branch], allow_failure: true)[0] || EMPTY_TREE_ID
+
+ execute(['read-tree', '--empty'])
+ execute(['read-tree', head_id])
+
+ blob_id = execute(['hash-object', '--stdin', '-w']) do |stdin|
+ stdin.write(file.read)
+ end
+
+ execute(['update-index', '--add', '--cacheinfo', '100644', blob_id[0], dst_path])
+
+ tree_id = execute(['write-tree'])
+
+ commit_tree_args = ['commit-tree', tree_id[0], '-m', "Add #{dst_path}"]
+ commit_tree_args += ['-p', head_id] unless head_id == EMPTY_TREE_ID
+ commit_id = execute(commit_tree_args)
+
+ execute(['update-ref', "refs/heads/#{branch}", commit_id[0]])
+ end
+
+ private
+
+ def execute(args, allow_failure: false)
+ output, status = popen(base_args + args, nil) do |stdin|
+ yield stdin if block_given?
+ end
+
+ unless status.zero?
+ if allow_failure
+ return []
+ else
+ raise "Got a non-zero exit code while calling out `#{args.join(' ')}`: #{output}"
+ end
+ end
+
+ output.split("\n")
+ end
+
+ def base_args
+ [
+ Gitlab.config.git.bin_path,
+ "--git-dir=#{@path_to_repo}"
+ ]
+ end
+end
diff --git a/spec/support/gitlab-git-test.git/objects/88/3e379fcaa5f818fca81cdbabd7a497794d6535 b/spec/support/gitlab-git-test.git/objects/88/3e379fcaa5f818fca81cdbabd7a497794d6535
new file mode 100644
index 00000000000..1c47f34b9a5
--- /dev/null
+++ b/spec/support/gitlab-git-test.git/objects/88/3e379fcaa5f818fca81cdbabd7a497794d6535
Binary files differ
diff --git a/spec/support/gitlab-git-test.git/objects/c8/b1ab16c858c67b680eea4644cf652485f555cf b/spec/support/gitlab-git-test.git/objects/c8/b1ab16c858c67b680eea4644cf652485f555cf
new file mode 100644
index 00000000000..ca13c8df66a
--- /dev/null
+++ b/spec/support/gitlab-git-test.git/objects/c8/b1ab16c858c67b680eea4644cf652485f555cf
Binary files differ
diff --git a/spec/support/gitlab-git-test.git/objects/e3/7697aea12699f0b44544332a7c0f41ace5fb16 b/spec/support/gitlab-git-test.git/objects/e3/7697aea12699f0b44544332a7c0f41ace5fb16
new file mode 100644
index 00000000000..3be244dbda4
--- /dev/null
+++ b/spec/support/gitlab-git-test.git/objects/e3/7697aea12699f0b44544332a7c0f41ace5fb16
@@ -0,0 +1,2 @@
+xK
+0EgNI|ADt*^ mZ qGčY8ZK7"Fc%oHD9rZLsMJ2=ACmeFgVxI9H2XJrp6;N8z??>+zWƏBÞ f}bN@K\SY iSC \ No newline at end of file
diff --git a/spec/support/gitlab-git-test.git/objects/eb/a0c153ed20d927bab00507f356043b6b4be31e b/spec/support/gitlab-git-test.git/objects/eb/a0c153ed20d927bab00507f356043b6b4be31e
new file mode 100644
index 00000000000..2bf27fe5048
--- /dev/null
+++ b/spec/support/gitlab-git-test.git/objects/eb/a0c153ed20d927bab00507f356043b6b4be31e
Binary files differ
diff --git a/spec/support/gitlab-git-test.git/objects/f6/5ad228d96e2a2ae7088e8557fe8906f6dd2b3f b/spec/support/gitlab-git-test.git/objects/f6/5ad228d96e2a2ae7088e8557fe8906f6dd2b3f
new file mode 100644
index 00000000000..8ab8606c6be
--- /dev/null
+++ b/spec/support/gitlab-git-test.git/objects/f6/5ad228d96e2a2ae7088e8557fe8906f6dd2b3f
Binary files differ
diff --git a/spec/support/gitlab_stubs/session.json b/spec/support/gitlab_stubs/session.json
index 688175369ae..658ff5871b0 100644
--- a/spec/support/gitlab_stubs/session.json
+++ b/spec/support/gitlab_stubs/session.json
@@ -14,7 +14,5 @@
"provider":null,
"is_admin":false,
"can_create_group":false,
- "can_create_project":false,
- "private_token":"Wvjy2Krpb7y8xi93owUz",
- "access_token":"Wvjy2Krpb7y8xi93owUz"
+ "can_create_project":false
}
diff --git a/spec/support/gitlab_stubs/user.json b/spec/support/gitlab_stubs/user.json
index ce8dfe5ae75..658ff5871b0 100644
--- a/spec/support/gitlab_stubs/user.json
+++ b/spec/support/gitlab_stubs/user.json
@@ -14,7 +14,5 @@
"provider":null,
"is_admin":false,
"can_create_group":false,
- "can_create_project":false,
- "private_token":"Wvjy2Krpb7y8xi93owUz",
- "access_token":"Wvjy2Krpb7y8xi93owUz"
-} \ No newline at end of file
+ "can_create_project":false
+}
diff --git a/spec/support/live_debugger.rb b/spec/support/live_debugger.rb
new file mode 100644
index 00000000000..911eb48a8ca
--- /dev/null
+++ b/spec/support/live_debugger.rb
@@ -0,0 +1,17 @@
+require 'io/console'
+
+module LiveDebugger
+ def live_debug
+ puts
+ puts "Current example is paused for live debugging."
+ puts "Opening #{current_url} in your default browser..."
+ puts "The current user credentials are: #{@current_user.username} / #{@current_user.password}" if @current_user
+ puts "Press any key to resume the execution of the example!!"
+
+ `open #{current_url}`
+
+ loop until $stdin.getch
+
+ puts "Back to the example!"
+ end
+end
diff --git a/spec/support/login_helpers.rb b/spec/support/login_helpers.rb
index 4aed40bf22d..50702a0ac88 100644
--- a/spec/support/login_helpers.rb
+++ b/spec/support/login_helpers.rb
@@ -3,6 +3,21 @@ require_relative 'devise_helpers'
module LoginHelpers
include DeviseHelpers
+ # Overriding Devise::Test::IntegrationHelpers#sign_in to store @current_user
+ # since we may need it in LiveDebugger#live_debug.
+ def sign_in(resource, scope: nil)
+ super
+
+ @current_user = resource
+ end
+
+ # Overriding Devise::Test::IntegrationHelpers#sign_out to clear @current_user.
+ def sign_out(resource_or_scope)
+ super
+
+ @current_user = nil
+ end
+
# Internal: Log in as a specific user or a new user of a specific role
#
# user_or_role - User object, or a role to create (e.g., :admin, :user)
@@ -28,7 +43,7 @@ module LoginHelpers
gitlab_sign_in_with(user, **kwargs)
- user
+ @current_user = user
end
def gitlab_sign_in_via(provider, user, uid)
@@ -41,6 +56,7 @@ module LoginHelpers
def gitlab_sign_out
find(".header-user-dropdown-toggle").click
click_link "Sign out"
+ @current_user = nil
expect(page).to have_button('Sign in')
end
diff --git a/spec/support/test_env.rb b/spec/support/test_env.rb
index a27bfdee3d2..fff120fcb88 100644
--- a/spec/support/test_env.rb
+++ b/spec/support/test_env.rb
@@ -182,6 +182,8 @@ module TestEnv
return unless @gitaly_pid
Process.kill('KILL', @gitaly_pid)
+ rescue Errno::ESRCH
+ # The process can already be gone if the test run was INTerrupted.
end
def setup_factory_repo
diff --git a/spec/tasks/gitlab/users_rake_spec.rb b/spec/tasks/gitlab/users_rake_spec.rb
deleted file mode 100644
index 972670e7f91..00000000000
--- a/spec/tasks/gitlab/users_rake_spec.rb
+++ /dev/null
@@ -1,38 +0,0 @@
-require 'spec_helper'
-require 'rake'
-
-describe 'gitlab:users namespace rake task' do
- let(:enable_registry) { true }
-
- before :all do
- Rake.application.rake_require 'tasks/gitlab/helpers'
- Rake.application.rake_require 'tasks/gitlab/users'
-
- # empty task as env is already loaded
- Rake::Task.define_task :environment
- end
-
- def run_rake_task(task_name)
- Rake::Task[task_name].reenable
- Rake.application.invoke_task task_name
- end
-
- describe 'clear_all_authentication_tokens' do
- before do
- # avoid writing task output to spec progress
- allow($stdout).to receive :write
- end
-
- context 'gitlab version' do
- it 'clears the authentication token for all users' do
- create_list(:user, 2)
-
- expect(User.pluck(:authentication_token)).to all(be_present)
-
- run_rake_task('gitlab:users:clear_all_authentication_tokens')
-
- expect(User.pluck(:authentication_token)).to all(be_nil)
- end
- end
- end
-end
diff --git a/spec/tasks/tokens_spec.rb b/spec/tasks/tokens_spec.rb
index b84137eb365..51f7a536cbb 100644
--- a/spec/tasks/tokens_spec.rb
+++ b/spec/tasks/tokens_spec.rb
@@ -7,12 +7,6 @@ describe 'tokens rake tasks' do
Rake.application.rake_require 'tasks/tokens'
end
- describe 'reset_all task' do
- it 'invokes create_hooks task' do
- expect { run_rake_task('tokens:reset_all_auth') }.to change { user.reload.authentication_token }
- end
- end
-
describe 'reset_all_email task' do
it 'invokes create_hooks task' do
expect { run_rake_task('tokens:reset_all_email') }.to change { user.reload.incoming_email_token }