summaryrefslogtreecommitdiff
path: root/spec
diff options
context:
space:
mode:
authorMatija Čupić <matteeyah@gmail.com>2018-05-02 15:54:06 +0200
committerMatija Čupić <matteeyah@gmail.com>2018-05-02 15:54:06 +0200
commitfc42ba6c5da7c99cc3ad393cf23299d1051d8447 (patch)
treedff01aa9a0c3d24e65800668c29a565018d04b43 /spec
parentd3327e0dfacd27d547ffdb3b7f4a1a76c76ae281 (diff)
parent75f6e91fcde12029068f48b6b7fb3fd13a612c61 (diff)
downloadgitlab-ce-fc42ba6c5da7c99cc3ad393cf23299d1051d8447.tar.gz
Merge branch 'master' into 44059-specify-variables-when-executing-a-manual-pipeline-from-the-ui
Diffstat (limited to 'spec')
-rw-r--r--spec/controllers/projects/clusters/gcp_controller_spec.rb2
-rw-r--r--spec/controllers/projects/raw_controller_spec.rb4
-rw-r--r--spec/factories/ci/stages.rb1
-rw-r--r--spec/factories/commit_statuses.rb1
-rw-r--r--spec/features/dashboard/groups_list_spec.rb22
-rw-r--r--spec/features/profiles/active_sessions_spec.rb89
-rw-r--r--spec/features/projects/files/user_browses_files_spec.rb240
-rw-r--r--spec/features/projects/issues/user_toggles_subscription_spec.rb8
-rw-r--r--spec/features/projects/settings/lfs_settings_spec.rb18
-rw-r--r--spec/features/projects/wiki/user_creates_wiki_page_spec.rb280
-rw-r--r--spec/features/users/active_sessions_spec.rb69
-rw-r--r--spec/finders/groups_finder_spec.rb84
-rw-r--r--spec/fixtures/api/schemas/public_api/v4/notes.json5
-rw-r--r--spec/helpers/application_helper_spec.rb139
-rw-r--r--spec/helpers/auth_helper_spec.rb24
-rw-r--r--spec/helpers/avatars_helper_spec.rb139
-rw-r--r--spec/javascripts/sidebar/sidebar_subscriptions_spec.js3
-rw-r--r--spec/javascripts/sidebar/subscriptions_spec.js8
-rw-r--r--spec/javascripts/test_bundle.js12
-rw-r--r--spec/lib/banzai/filter/commit_trailers_filter_spec.rb40
-rw-r--r--spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb35
-rw-r--r--spec/lib/gitlab/ci/pipeline/chain/create_spec.rb2
-rw-r--r--spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb3
-rw-r--r--spec/lib/gitlab/import_export/safe_model_attributes.yml1
-rw-r--r--spec/lib/gitlab/kubernetes/helm/base_command_spec.rb18
-rw-r--r--spec/lib/gitlab/kubernetes/helm/init_command_spec.rb20
-rw-r--r--spec/lib/gitlab/kubernetes/helm/install_command_spec.rb66
-rw-r--r--spec/migrations/schedule_stages_index_migration_spec.rb35
-rw-r--r--spec/models/active_session_spec.rb216
-rw-r--r--spec/models/ci/stage_spec.rb34
-rw-r--r--spec/models/diff_note_spec.rb33
-rw-r--r--spec/models/lfs_object_spec.rb4
-rw-r--r--spec/models/notification_setting_spec.rb7
-rw-r--r--spec/requests/api/discussions_spec.rb33
-rw-r--r--spec/requests/api/jobs_spec.rb6
-rw-r--r--spec/requests/api/runner_spec.rb4
-rw-r--r--spec/requests/api/v3/builds_spec.rb4
-rw-r--r--spec/services/applications/create_service_spec.rb14
-rw-r--r--spec/services/ci/retry_build_service_spec.rb4
-rw-r--r--spec/services/notes/resolve_service_spec.rb23
-rw-r--r--spec/services/projects/update_pages_service_spec.rb10
-rw-r--r--spec/support/commit_trailers_spec_helper.rb2
-rw-r--r--spec/support/shared_examples/helm_generated_script.rb19
-rw-r--r--spec/support/shared_examples/requests/api/diff_discussions.rb57
-rw-r--r--spec/support/shared_examples/requests/api/resolvable_discussions.rb87
-rw-r--r--spec/uploaders/lfs_object_uploader_spec.rb12
46 files changed, 1424 insertions, 513 deletions
diff --git a/spec/controllers/projects/clusters/gcp_controller_spec.rb b/spec/controllers/projects/clusters/gcp_controller_spec.rb
index e14ba29fa70..715bb9f5e52 100644
--- a/spec/controllers/projects/clusters/gcp_controller_spec.rb
+++ b/spec/controllers/projects/clusters/gcp_controller_spec.rb
@@ -142,7 +142,7 @@ describe Projects::Clusters::GcpController do
context 'when google project billing is enabled' do
before do
- redis_double = double
+ redis_double = double.as_null_object
allow(Gitlab::Redis::SharedState).to receive(:with).and_yield(redis_double)
allow(redis_double).to receive(:get).with(CheckGcpProjectBillingWorker.redis_shared_state_key_for('token')).and_return('true')
end
diff --git a/spec/controllers/projects/raw_controller_spec.rb b/spec/controllers/projects/raw_controller_spec.rb
index 08e2ccf893a..c3468536ae1 100644
--- a/spec/controllers/projects/raw_controller_spec.rb
+++ b/spec/controllers/projects/raw_controller_spec.rb
@@ -54,9 +54,9 @@ describe Projects::RawController do
end
context 'and lfs uses object storage' do
+ let(:lfs_object) { create(:lfs_object, :with_file, oid: '91eff75a492a3ed0dfcb544d7f31326bc4014c8551849c192fd1e48d4dd2c897', size: '1575078') }
+
before do
- lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png")
- lfs_object.save!
stub_lfs_object_storage
lfs_object.file.migrate!(LfsObjectUploader::Store::REMOTE)
end
diff --git a/spec/factories/ci/stages.rb b/spec/factories/ci/stages.rb
index 25309033571..ce61e6bf759 100644
--- a/spec/factories/ci/stages.rb
+++ b/spec/factories/ci/stages.rb
@@ -21,6 +21,7 @@ FactoryBot.define do
pipeline factory: :ci_empty_pipeline
name 'test'
+ position 1
status 'pending'
end
end
diff --git a/spec/factories/commit_statuses.rb b/spec/factories/commit_statuses.rb
index ce5fbc343ee..53368c64e10 100644
--- a/spec/factories/commit_statuses.rb
+++ b/spec/factories/commit_statuses.rb
@@ -2,6 +2,7 @@ FactoryBot.define do
factory :commit_status, class: CommitStatus do
name 'default'
stage 'test'
+ stage_idx 0
status 'success'
description 'commit status'
pipeline factory: :ci_pipeline_with_one_job
diff --git a/spec/features/dashboard/groups_list_spec.rb b/spec/features/dashboard/groups_list_spec.rb
index a71020002dc..ed47f7ed390 100644
--- a/spec/features/dashboard/groups_list_spec.rb
+++ b/spec/features/dashboard/groups_list_spec.rb
@@ -40,7 +40,7 @@ feature 'Dashboard Groups page', :js do
expect(page).to have_content(nested_group.name)
end
- describe 'when filtering groups', :nested_groups do
+ context 'when filtering groups', :nested_groups do
before do
group.add_owner(user)
nested_group.add_owner(user)
@@ -75,7 +75,7 @@ feature 'Dashboard Groups page', :js do
end
end
- describe 'group with subgroups', :nested_groups do
+ context 'with subgroups', :nested_groups do
let!(:subgroup) { create(:group, :public, parent: group) }
before do
@@ -106,7 +106,7 @@ feature 'Dashboard Groups page', :js do
end
end
- describe 'when using pagination' do
+ context 'when using pagination' do
let(:group) { create(:group, created_at: 5.days.ago) }
let(:group2) { create(:group, created_at: 2.days.ago) }
@@ -141,4 +141,20 @@ feature 'Dashboard Groups page', :js do
expect(page).not_to have_selector("#group-#{group2.id}")
end
end
+
+ context 'when signed in as admin' do
+ let(:admin) { create(:admin) }
+
+ it 'shows only groups admin is member of' do
+ group.add_owner(admin)
+ expect(another_group).to be_persisted
+
+ sign_in(admin)
+ visit dashboard_groups_path
+ wait_for_requests
+
+ expect(page).to have_content(group.name)
+ expect(page).not_to have_content(another_group.name)
+ end
+ end
end
diff --git a/spec/features/profiles/active_sessions_spec.rb b/spec/features/profiles/active_sessions_spec.rb
new file mode 100644
index 00000000000..4045cfd21c4
--- /dev/null
+++ b/spec/features/profiles/active_sessions_spec.rb
@@ -0,0 +1,89 @@
+require 'rails_helper'
+
+feature 'Profile > Active Sessions', :clean_gitlab_redis_shared_state do
+ let(:user) do
+ create(:user).tap do |user|
+ user.current_sign_in_at = Time.current
+ end
+ end
+
+ around do |example|
+ Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do
+ example.run
+ end
+ end
+
+ scenario 'User sees their active sessions' do
+ Capybara::Session.new(:session1)
+ Capybara::Session.new(:session2)
+
+ # note: headers can only be set on the non-js (aka. rack-test) driver
+ using_session :session1 do
+ Capybara.page.driver.header(
+ 'User-Agent',
+ 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:58.0) Gecko/20100101 Firefox/58.0'
+ )
+
+ gitlab_sign_in(user)
+ end
+
+ # set an additional session on another device
+ using_session :session2 do
+ Capybara.page.driver.header(
+ 'User-Agent',
+ 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 (KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]'
+ )
+
+ gitlab_sign_in(user)
+ end
+
+ using_session :session1 do
+ visit profile_active_sessions_path
+
+ expect(page).to have_content(
+ '127.0.0.1 ' \
+ 'This is your current session ' \
+ 'Firefox on Ubuntu ' \
+ 'Signed in on 12 Mar 09:06'
+ )
+
+ expect(page).to have_selector '[title="Desktop"]', count: 1
+
+ expect(page).to have_content(
+ '127.0.0.1 ' \
+ 'Last accessed on 12 Mar 09:06 ' \
+ 'Mobile Safari on iOS ' \
+ 'Signed in on 12 Mar 09:06'
+ )
+
+ expect(page).to have_selector '[title="Smartphone"]', count: 1
+ end
+ end
+
+ scenario 'User can revoke a session', :js, :redis_session_store do
+ Capybara::Session.new(:session1)
+ Capybara::Session.new(:session2)
+
+ # set an additional session in another browser
+ using_session :session2 do
+ gitlab_sign_in(user)
+ end
+
+ using_session :session1 do
+ gitlab_sign_in(user)
+ visit profile_active_sessions_path
+
+ expect(page).to have_link('Revoke', count: 1)
+
+ accept_confirm { click_on 'Revoke' }
+
+ expect(page).not_to have_link('Revoke')
+ end
+
+ using_session :session2 do
+ visit profile_active_sessions_path
+
+ expect(page).to have_content('You need to sign in or sign up before continuing.')
+ end
+ end
+end
diff --git a/spec/features/projects/files/user_browses_files_spec.rb b/spec/features/projects/files/user_browses_files_spec.rb
index 9c1f11f4c12..41f6c52fb8a 100644
--- a/spec/features/projects/files/user_browses_files_spec.rb
+++ b/spec/features/projects/files/user_browses_files_spec.rb
@@ -1,14 +1,12 @@
-require 'spec_helper'
+require "spec_helper"
-describe 'Projects > Files > User browses files' do
+describe "User browses files" do
let(:fork_message) do
"You're not allowed to make changes to this project directly. "\
"A fork of this project has been created that you can make changes in, so you can submit a merge request."
end
- let(:project) { create(:project, :repository, name: 'Shop') }
- let(:project2) { create(:project, :repository, name: 'Another Project', path: 'another-project') }
- let(:project2_tree_path_root_ref) { project_tree_path(project2, project2.repository.root_ref) }
- let(:tree_path_ref_6d39438) { project_tree_path(project, '6d39438') }
+ let(:project) { create(:project, :repository, name: "Shop") }
+ let(:project2) { create(:project, :repository, name: "Another Project", path: "another-project") }
let(:tree_path_root_ref) { project_tree_path(project, project.repository.root_ref) }
let(:user) { project.owner }
@@ -16,57 +14,55 @@ describe 'Projects > Files > User browses files' do
sign_in(user)
end
- it 'shows last commit for current directory' do
+ it "shows last commit for current directory" do
visit(tree_path_root_ref)
- click_link 'files'
+ click_link("files")
- last_commit = project.repository.last_commit_for_path(project.default_branch, 'files')
- page.within('.blob-commit-info') do
- expect(page).to have_content last_commit.short_id
- expect(page).to have_content last_commit.author_name
+ last_commit = project.repository.last_commit_for_path(project.default_branch, "files")
+
+ page.within(".blob-commit-info") do
+ expect(page).to have_content(last_commit.short_id).and have_content(last_commit.author_name)
end
end
- context 'when browsing the master branch' do
+ context "when browsing the master branch" do
before do
visit(tree_path_root_ref)
end
- it 'shows files from a repository' do
- expect(page).to have_content('VERSION')
- expect(page).to have_content('.gitignore')
- expect(page).to have_content('LICENSE')
+ it "shows files from a repository" do
+ expect(page).to have_content("VERSION")
+ .and have_content(".gitignore")
+ .and have_content("LICENSE")
end
- it 'shows the "Browse Directory" link' do
- click_link('files')
- click_link('History')
+ it "shows the `Browse Directory` link" do
+ click_link("files")
+ click_link("History")
- expect(page).to have_link('Browse Directory')
- expect(page).not_to have_link('Browse Code')
+ expect(page).to have_link("Browse Directory").and have_no_link("Browse Code")
end
- it 'shows the "Browse File" link' do
- page.within('.tree-table') do
- click_link('README.md')
+ it "shows the `Browse File` link" do
+ page.within(".tree-table") do
+ click_link("README.md")
end
- click_link('History')
- expect(page).to have_link('Browse File')
- expect(page).not_to have_link('Browse Files')
+ click_link("History")
+
+ expect(page).to have_link("Browse File").and have_no_link("Browse Files")
end
- it 'shows the "Browse Files" link' do
- click_link('History')
+ it "shows the `Browse Files` link" do
+ click_link("History")
- expect(page).to have_link('Browse Files')
- expect(page).not_to have_link('Browse Directory')
+ expect(page).to have_link("Browse Files").and have_no_link("Browse Directory")
end
- it 'redirects to the permalink URL' do
- click_link('.gitignore')
- click_link('Permalink')
+ it "redirects to the permalink URL" do
+ click_link(".gitignore")
+ click_link("Permalink")
permalink_path = project_blob_path(project, "#{project.repository.commit.sha}/.gitignore")
@@ -74,80 +70,180 @@ describe 'Projects > Files > User browses files' do
end
end
- context 'when browsing a specific ref' do
+ context "when browsing the `markdown` branch", :js do
+ context "when browsing the root" do
+ before do
+ visit(project_tree_path(project, "markdown"))
+ end
+
+ it "shows correct files and links" do
+ # rubocop:disable Lint/Void
+ # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
+ find("a", text: /^empty$/)["href"] == project_tree_url(project, "markdown")
+ find("a", text: /^#id$/)["href"] == project_tree_url(project, "markdown", anchor: "#id")
+ find("a", text: %r{^/#id$})["href"] == project_tree_url(project, "markdown", anchor: "#id")
+ find("a", text: /^README.md#id$/)["href"] == project_blob_url(project, "markdown/README.md", anchor: "#id")
+ find("a", text: %r{^d/README.md#id$})["href"] == project_blob_url(project, "d/markdown/README.md", anchor: "#id")
+ # rubocop:enable Lint/Void
+
+ expect(current_path).to eq(project_tree_path(project, "markdown"))
+ expect(page).to have_content("README.md")
+ .and have_content("CHANGELOG")
+ .and have_content("Welcome to GitLab GitLab is a free project and repository management application")
+ .and have_link("GitLab API doc")
+ .and have_link("GitLab API website")
+ .and have_link("Rake tasks")
+ .and have_link("backup and restore procedure")
+ .and have_link("GitLab API doc directory")
+ .and have_link("Maintenance")
+ .and have_header_with_correct_id_and_link(2, "Application details", "application-details")
+ end
+
+ it "shows correct content of file" do
+ click_link("GitLab API doc")
+
+ expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/README.md"))
+ expect(page).to have_content("All API requests require authentication")
+ .and have_content("Contents")
+ .and have_link("Users")
+ .and have_link("Rake tasks")
+ .and have_header_with_correct_id_and_link(1, "GitLab API", "gitlab-api")
+
+ click_link("Users")
+
+ expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/users.md"))
+ expect(page).to have_content("Get a list of users.")
+
+ page.go_back
+
+ click_link("Rake tasks")
+
+ expect(current_path).to eq(project_tree_path(project, "markdown/doc/raketasks"))
+ expect(page).to have_content("backup_restore.md").and have_content("maintenance.md")
+
+ click_link("shop")
+ click_link("Maintenance")
+
+ expect(current_path).to eq(project_blob_path(project, "markdown/doc/raketasks/maintenance.md"))
+ expect(page).to have_content("bundle exec rake gitlab:env:info RAILS_ENV=production")
+
+ click_link("shop")
+
+ page.within(".tree-table") do
+ click_link("README.md")
+ end
+
+ page.go_back
+
+ page.within(".tree-table") do
+ click_link("d")
+ end
+
+ # rubocop:disable Lint/Void
+ # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
+ find("a", text: /^empty$/)["href"] == project_tree_url(project, "markdown/d")
+ # rubocop:enable Lint/Void
+
+ page.within(".tree-table") do
+ click_link("README.md")
+ end
+
+ # rubocop:disable Lint/Void
+ # Test the full URLs of links instead of relative paths by `have_link(text: "...", href: "...")`.
+ find("a", text: /^empty$/)["href"] == project_blob_url(project, "markdown/d/README.md")
+ # rubocop:enable Lint/Void
+ end
+
+ it "shows correct content of directory" do
+ click_link("GitLab API doc directory")
+
+ expect(current_path).to eq(project_tree_path(project, "markdown/doc/api"))
+ expect(page).to have_content("README.md").and have_content("users.md")
+
+ click_link("Users")
+
+ expect(current_path).to eq(project_blob_path(project, "markdown/doc/api/users.md"))
+ expect(page).to have_content("List users").and have_content("Get a list of users.")
+ end
+ end
+ end
+
+ context "when browsing a specific ref" do
+ let(:ref) { project_tree_path(project, "6d39438") }
+
before do
- visit(tree_path_ref_6d39438)
+ visit(ref)
end
- it 'shows files from a repository for "6d39438"' do
- expect(current_path).to eq(tree_path_ref_6d39438)
- expect(page).to have_content('.gitignore')
- expect(page).to have_content('LICENSE')
+ it "shows files from a repository for `6d39438`" do
+ expect(current_path).to eq(ref)
+ expect(page).to have_content(".gitignore").and have_content("LICENSE")
end
- it 'shows files from a repository with apostroph in its name', :js do
- first('.js-project-refs-dropdown').click
+ it "shows files from a repository with apostroph in its name", :js do
+ first(".js-project-refs-dropdown").click
- page.within('.project-refs-form') do
+ page.within(".project-refs-form") do
click_link("'test'")
end
- expect(page).to have_selector('.dropdown-toggle-text', text: "'test'")
+ expect(page).to have_selector(".dropdown-toggle-text", text: "'test'")
visit(project_tree_path(project, "'test'"))
- expect(page).to have_css('.tree-commit-link', visible: true)
- expect(page).not_to have_content('Loading commit data...')
+ expect(page).to have_css(".tree-commit-link").and have_no_content("Loading commit data...")
end
- it 'shows the code with a leading dot in the directory', :js do
- first('.js-project-refs-dropdown').click
+ it "shows the code with a leading dot in the directory", :js do
+ first(".js-project-refs-dropdown").click
- page.within('.project-refs-form') do
- click_link('fix')
+ page.within(".project-refs-form") do
+ click_link("fix")
end
- visit(project_tree_path(project, 'fix/.testdir'))
+ visit(project_tree_path(project, "fix/.testdir"))
- expect(page).to have_css('.tree-commit-link', visible: true)
- expect(page).not_to have_content('Loading commit data...')
+ expect(page).to have_css(".tree-commit-link").and have_no_content("Loading commit data...")
end
- it 'does not show the permalink link' do
- click_link('.gitignore')
+ it "does not show the permalink link" do
+ click_link(".gitignore")
- expect(page).not_to have_link('permalink')
+ expect(page).not_to have_link("permalink")
end
end
- context 'when browsing a file content' do
+ context "when browsing a file content" do
before do
visit(tree_path_root_ref)
- click_link('.gitignore')
+
+ click_link(".gitignore")
end
- it 'shows a file content', :js do
- wait_for_requests
- expect(page).to have_content('*.rbc')
+ it "shows a file content", :js do
+ expect(page).to have_content("*.rbc")
end
- it 'is possible to blame' do
- click_link 'Blame'
+ it "is possible to blame" do
+ click_link("Blame")
- expect(page).to have_content "*.rb"
- expect(page).to have_content "Dmitriy Zaporozhets"
- expect(page).to have_content "Initial commit"
+ expect(page).to have_content("*.rb")
+ .and have_content("Dmitriy Zaporozhets")
+ .and have_content("Initial commit")
end
end
- context 'when browsing a raw file' do
+ context "when browsing a raw file" do
before do
- visit(project_blob_path(project, File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path)))
+ path = File.join(RepoHelpers.sample_commit.id, RepoHelpers.sample_blob.path)
+
+ visit(project_blob_path(project, path))
end
- it 'shows a raw file content' do
- click_link('Open raw')
- expect(source).to eq('') # Body is filled in by gitlab-workhorse
+ it "shows a raw file content" do
+ click_link("Open raw")
+
+ expect(source).to eq("") # Body is filled in by gitlab-workhorse
end
end
end
diff --git a/spec/features/projects/issues/user_toggles_subscription_spec.rb b/spec/features/projects/issues/user_toggles_subscription_spec.rb
index 117a614b980..c2b2a193682 100644
--- a/spec/features/projects/issues/user_toggles_subscription_spec.rb
+++ b/spec/features/projects/issues/user_toggles_subscription_spec.rb
@@ -1,9 +1,9 @@
require "spec_helper"
describe "User toggles subscription", :js do
- set(:project) { create(:project_empty_repo, :public) }
- set(:user) { create(:user) }
- set(:issue) { create(:issue, project: project, author: user) }
+ let(:project) { create(:project_empty_repo, :public) }
+ let(:user) { create(:user) }
+ let(:issue) { create(:issue, project: project, author: user) }
before do
project.add_developer(user)
@@ -12,7 +12,7 @@ describe "User toggles subscription", :js do
visit(project_issue_path(project, issue))
end
- it "unsibscribes from issue" do
+ it "unsubscribes from issue" do
subscription_button = find(".js-issuable-subscribe-button")
# Check we're subscribed.
diff --git a/spec/features/projects/settings/lfs_settings_spec.rb b/spec/features/projects/settings/lfs_settings_spec.rb
index 0fd28a5681c..342be1d2a9d 100644
--- a/spec/features/projects/settings/lfs_settings_spec.rb
+++ b/spec/features/projects/settings/lfs_settings_spec.rb
@@ -1,21 +1,27 @@
require 'rails_helper'
describe 'Projects > Settings > LFS settings' do
- let(:admin) { create(:admin) }
let(:project) { create(:project) }
+ let(:user) { create(:user) }
+ let(:role) { :master }
context 'LFS enabled setting' do
before do
allow(Gitlab.config.lfs).to receive(:enabled).and_return(true)
- sign_in(admin)
+ sign_in(user)
+ project.add_role(user, role)
end
- it 'displays the correct elements', :js do
- visit edit_project_path(project)
+ context 'for master' do
+ let(:role) { :master }
- expect(page).to have_content('Git Large File Storage')
- expect(page).to have_selector('input[name="project[lfs_enabled]"] + button', visible: true)
+ it 'displays the correct elements', :js do
+ visit edit_project_path(project)
+
+ expect(page).to have_content('Git Large File Storage')
+ expect(page).to have_selector('input[name="project[lfs_enabled]"] + button', visible: true)
+ end
end
end
end
diff --git a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
index 4a9d1cb87e1..fe6fa55fa75 100644
--- a/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
+++ b/spec/features/projects/wiki/user_creates_wiki_page_spec.rb
@@ -1,6 +1,6 @@
-require 'spec_helper'
+require "spec_helper"
-describe 'User creates wiki page' do
+describe "User creates wiki page" do
let(:user) { create(:user) }
before do
@@ -10,67 +10,104 @@ describe 'User creates wiki page' do
visit(project_wikis_path(project))
end
- context 'when wiki is empty' do
- context 'in a user namespace' do
+ context "when wiki is empty" do
+ context "in a user namespace" do
let(:project) { create(:project, namespace: user.namespace) }
- it 'shows validation error message' do
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: '')
- click_on('Create page')
+ it "shows validation error message" do
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "")
+
+ click_on("Create page")
end
- expect(page).to have_content('The form contains the following error:')
- expect(page).to have_content("Content can't be blank")
+ expect(page).to have_content("The form contains the following error:").and have_content("Content can't be blank")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "[link test](test)")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: '[link test](test)')
- click_on('Create page')
+ click_on("Create page")
end
- expect(page).to have_content('Home')
- expect(page).to have_content('link test')
+ expect(page).to have_content("Home").and have_content("link test")
- click_link('link test')
+ click_link("link test")
- expect(page).to have_content('Create Page')
+ expect(page).to have_content("Create Page")
end
- it 'shows non-escaped link in the pages list', :js do
- click_link('New page')
+ it "shows non-escaped link in the pages list", :js do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'one/two/three-test')
- click_on('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "one/two/three-test")
+
+ click_on("Create page")
end
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'wiki content')
- click_on('Create page')
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "wiki content")
+
+ click_on("Create page")
end
- expect(current_path).to include('one/two/three-test')
+ expect(current_path).to include("one/two/three-test")
expect(page).to have_xpath("//a[@href='/#{project.full_path}/wikis/one/two/three-test']")
end
- it 'has "Create home" as a commit message' do
- expect(page).to have_field('wiki[message]', with: 'Create home')
+ it "has `Create home` as a commit message" do
+ expect(page).to have_field("wiki[message]", with: "Create home")
end
- it 'creates a page from the home page' do
- fill_in(:wiki_content, with: 'My awesome wiki!')
+ it "creates a page from the home page" do
+ fill_in(:wiki_content, with: "[test](test)\n[GitLab API doc](api)\n[Rake tasks](raketasks)\n# Wiki header\n")
+ fill_in(:wiki_message, with: "Adding links to wiki")
+
+ page.within(".wiki-form") do
+ click_button("Create page")
+ end
+
+ expect(current_path).to eq(project_wiki_path(project, "home"))
+ expect(page).to have_content("test GitLab API doc Rake tasks Wiki header")
+ .and have_content("Home")
+ .and have_content("Last edited by #{user.name}")
+ .and have_header_with_correct_id_and_link(1, "Wiki header", "wiki-header")
+
+ click_link("test")
- page.within('.wiki-form') do
- click_button('Create page')
+ expect(current_path).to eq(project_wiki_path(project, "test"))
+
+ page.within(:css, ".nav-text") do
+ expect(page).to have_content("Test").and have_content("Create Page")
+ end
+
+ click_link("Home")
+
+ expect(current_path).to eq(project_wiki_path(project, "home"))
+
+ click_link("GitLab API")
+
+ expect(current_path).to eq(project_wiki_path(project, "api"))
+
+ page.within(:css, ".nav-text") do
+ expect(page).to have_content("Create").and have_content("Api")
end
- expect(page).to have_content('Home')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ click_link("Home")
+
+ expect(current_path).to eq(project_wiki_path(project, "home"))
+
+ click_link("Rake tasks")
+
+ expect(current_path).to eq(project_wiki_path(project, "raketasks"))
+
+ page.within(:css, ".nav-text") do
+ expect(page).to have_content("Create").and have_content("Rake")
+ end
end
- it 'creates ASCII wiki with LaTeX blocks', :js do
- stub_application_setting(plantuml_url: 'http://localhost', plantuml_enabled: true)
+ it "creates ASCII wiki with LaTeX blocks", :js do
+ stub_application_setting(plantuml_url: "http://localhost", plantuml_enabled: true)
ascii_content = <<~MD
:stem: latexmath
@@ -90,153 +127,164 @@ describe 'User creates wiki page' do
stem:[2+2] is 4
MD
- find('#wiki_format option[value=asciidoc]').select_option
+ find("#wiki_format option[value=asciidoc]").select_option
+
fill_in(:wiki_content, with: ascii_content)
- page.within('.wiki-form') do
- click_button('Create page')
+ page.within(".wiki-form") do
+ click_button("Create page")
end
- page.within '.wiki' do
- expect(page).to have_selector('.katex', count: 3)
- expect(page).to have_content('2+2 is 4')
+ page.within ".wiki" do
+ expect(page).to have_selector(".katex", count: 3).and have_content("2+2 is 4")
end
end
end
- context 'in a group namespace', :js do
+ context "in a group namespace", :js do
let(:project) { create(:project, namespace: create(:group, :public)) }
- it 'has "Create home" as a commit message' do
- expect(page).to have_field('wiki[message]', with: 'Create home')
+ it "has `Create home` as a commit message" do
+ expect(page).to have_field("wiki[message]", with: "Create home")
end
- it 'creates a page from from the home page' do
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ it "creates a page from from the home page" do
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
+
+ click_button("Create page")
end
- expect(page).to have_content('Home')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Home")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
end
end
- context 'when wiki is not empty', :js do
+ context "when wiki is not empty", :js do
before do
- create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: 'home', content: 'Home page' })
+ create(:wiki_page, wiki: create(:project, namespace: user.namespace).wiki, attrs: { title: "home", content: "Home page" })
end
- context 'in a user namespace' do
+ context "in a user namespace" do
let(:project) { create(:project, namespace: user.namespace) }
- context 'via the "new wiki page" page' do
- it 'creates a page with a single word' do
- click_link('New page')
+ context "via the `new wiki page` page" do
+ it "creates a page with a single word" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'foo')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "foo")
+
+ click_button("Create page")
end
# Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Create foo')
+ expect(page).to have_field("wiki[message]", with: "Create foo")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ click_button("Create page")
end
- expect(page).to have_content('Foo')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Foo")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
- it 'creates a page with spaces in the name' do
- click_link('New page')
+ it "creates a page with spaces in the name" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'Spaces in the name')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "Spaces in the name")
+
+ click_button("Create page")
end
# Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Create spaces in the name')
+ expect(page).to have_field("wiki[message]", with: "Create spaces in the name")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ click_button("Create page")
end
- expect(page).to have_content('Spaces in the name')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Spaces in the name")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
- it 'creates a page with hyphens in the name' do
- click_link('New page')
+ it "creates a page with hyphens in the name" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'hyphens-in-the-name')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "hyphens-in-the-name")
+
+ click_button("Create page")
end
# Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Create hyphens in the name')
+ expect(page).to have_field("wiki[message]", with: "Create hyphens in the name")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ click_button("Create page")
end
- expect(page).to have_content('Hyphens in the name')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Hyphens in the name")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
end
- it 'shows the autocompletion dropdown' do
- click_link('New page')
+ it "shows the autocompletion dropdown" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'test-autocomplete')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "test-autocomplete")
+
+ click_button("Create page")
end
- page.within('.wiki-form') do
- find('#wiki_content').native.send_keys('')
- fill_in(:wiki_content, with: '@')
+ page.within(".wiki-form") do
+ find("#wiki_content").native.send_keys("")
+
+ fill_in(:wiki_content, with: "@")
end
- expect(page).to have_selector('.atwho-view')
+ expect(page).to have_selector(".atwho-view")
end
end
- context 'in a group namespace' do
+ context "in a group namespace" do
let(:project) { create(:project, namespace: create(:group, :public)) }
- context 'via the "new wiki page" page' do
- it 'creates a page' do
- click_link('New page')
+ context "via the `new wiki page` page" do
+ it "creates a page" do
+ click_link("New page")
- page.within('#modal-new-wiki') do
- fill_in(:new_wiki_path, with: 'foo')
- click_button('Create page')
+ page.within("#modal-new-wiki") do
+ fill_in(:new_wiki_path, with: "foo")
+
+ click_button("Create page")
end
# Commit message field should have correct value.
- expect(page).to have_field('wiki[message]', with: 'Create foo')
+ expect(page).to have_field("wiki[message]", with: "Create foo")
+
+ page.within(".wiki-form") do
+ fill_in(:wiki_content, with: "My awesome wiki!")
- page.within('.wiki-form') do
- fill_in(:wiki_content, with: 'My awesome wiki!')
- click_button('Create page')
+ click_button("Create page")
end
- expect(page).to have_content('Foo')
- expect(page).to have_content("Last edited by #{user.name}")
- expect(page).to have_content('My awesome wiki!')
+ expect(page).to have_content("Foo")
+ .and have_content("Last edited by #{user.name}")
+ .and have_content("My awesome wiki!")
end
end
end
diff --git a/spec/features/users/active_sessions_spec.rb b/spec/features/users/active_sessions_spec.rb
new file mode 100644
index 00000000000..631d7e3bced
--- /dev/null
+++ b/spec/features/users/active_sessions_spec.rb
@@ -0,0 +1,69 @@
+require 'spec_helper'
+
+feature 'Active user sessions', :clean_gitlab_redis_shared_state do
+ scenario 'Successful login adds a new active user login' do
+ now = Time.zone.parse('2018-03-12 09:06')
+ Timecop.freeze(now) do
+ user = create(:user)
+ gitlab_sign_in(user)
+ expect(current_path).to eq root_path
+
+ sessions = ActiveSession.list(user)
+ expect(sessions.count).to eq 1
+
+ # refresh the current page updates the updated_at
+ Timecop.freeze(now + 1.minute) do
+ visit current_path
+
+ sessions = ActiveSession.list(user)
+ expect(sessions.first).to have_attributes(
+ created_at: Time.zone.parse('2018-03-12 09:06'),
+ updated_at: Time.zone.parse('2018-03-12 09:07')
+ )
+ end
+ end
+ end
+
+ scenario 'Successful login cleans up obsolete entries' do
+ user = create(:user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
+ end
+
+ gitlab_sign_in(user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).not_to include '59822c7d9fcdfa03725eff41782ad97d'
+ end
+ end
+
+ scenario 'Sessionless login does not clean up obsolete entries' do
+ user = create(:user)
+ personal_access_token = create(:personal_access_token, user: user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
+ end
+
+ visit user_path(user, :atom, private_token: personal_access_token.token)
+ expect(page.status_code).to eq 200
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to include '59822c7d9fcdfa03725eff41782ad97d'
+ end
+ end
+
+ scenario 'Logout deletes the active user login' do
+ user = create(:user)
+ gitlab_sign_in(user)
+ expect(current_path).to eq root_path
+
+ expect(ActiveSession.list(user).count).to eq 1
+
+ gitlab_sign_out
+ expect(current_path).to eq new_user_session_path
+
+ expect(ActiveSession.list(user)).to be_empty
+ end
+end
diff --git a/spec/finders/groups_finder_spec.rb b/spec/finders/groups_finder_spec.rb
index abc470788e1..16c0d418d98 100644
--- a/spec/finders/groups_finder_spec.rb
+++ b/spec/finders/groups_finder_spec.rb
@@ -2,43 +2,71 @@ require 'spec_helper'
describe GroupsFinder do
describe '#execute' do
- let(:user) { create(:user) }
-
- context 'root level groups' do
- let!(:private_group) { create(:group, :private) }
- let!(:internal_group) { create(:group, :internal) }
- let!(:public_group) { create(:group, :public) }
-
- context 'without a user' do
- subject { described_class.new.execute }
-
- it { is_expected.to eq([public_group]) }
+ let(:user) { create(:user) }
+
+ describe 'root level groups' do
+ using RSpec::Parameterized::TableSyntax
+
+ where(:user_type, :params, :results) do
+ nil | { all_available: true } | %i(public_group user_public_group)
+ nil | { all_available: false } | %i(public_group user_public_group)
+ nil | {} | %i(public_group user_public_group)
+
+ :regular | { all_available: true } | %i(public_group internal_group user_public_group user_internal_group
+ user_private_group)
+ :regular | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :regular | {} | %i(public_group internal_group user_public_group user_internal_group user_private_group)
+
+ :external | { all_available: true } | %i(public_group user_public_group user_internal_group user_private_group)
+ :external | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :external | {} | %i(public_group user_public_group user_internal_group user_private_group)
+
+ :admin | { all_available: true } | %i(public_group internal_group private_group user_public_group
+ user_internal_group user_private_group)
+ :admin | { all_available: false } | %i(user_public_group user_internal_group user_private_group)
+ :admin | {} | %i(public_group internal_group private_group user_public_group user_internal_group
+ user_private_group)
end
- context 'with a user' do
- subject { described_class.new(user).execute }
-
- context 'normal user' do
- it { is_expected.to contain_exactly(public_group, internal_group) }
- end
-
- context 'external user' do
- let(:user) { create(:user, external: true) }
-
- it { is_expected.to contain_exactly(public_group) }
+ with_them do
+ before do
+ # Fixme: Because of an issue: https://github.com/tomykaira/rspec-parameterized/issues/8#issuecomment-381888428
+ # The groups need to be created here, not with let syntax, and also compared by name and not ids
+
+ @groups = {
+ private_group: create(:group, :private, name: 'private_group'),
+ internal_group: create(:group, :internal, name: 'internal_group'),
+ public_group: create(:group, :public, name: 'public_group'),
+
+ user_private_group: create(:group, :private, name: 'user_private_group'),
+ user_internal_group: create(:group, :internal, name: 'user_internal_group'),
+ user_public_group: create(:group, :public, name: 'user_public_group')
+ }
+
+ if user_type
+ user =
+ case user_type
+ when :regular
+ create(:user)
+ when :external
+ create(:user, external: true)
+ when :admin
+ create(:user, :admin)
+ end
+ @groups.values_at(:user_private_group, :user_internal_group, :user_public_group).each do |group|
+ group.add_developer(user)
+ end
+ end
end
- context 'user is member of the private group' do
- before do
- private_group.add_guest(user)
- end
+ subject { described_class.new(User.last, params).execute.to_a }
- it { is_expected.to contain_exactly(public_group, internal_group, private_group) }
- end
+ it { is_expected.to match_array(@groups.values_at(*results)) }
end
end
context 'subgroups', :nested_groups do
+ let(:user) { create(:user) }
let!(:parent_group) { create(:group, :public) }
let!(:public_subgroup) { create(:group, :public, parent: parent_group) }
let!(:internal_subgroup) { create(:group, :internal, parent: parent_group) }
diff --git a/spec/fixtures/api/schemas/public_api/v4/notes.json b/spec/fixtures/api/schemas/public_api/v4/notes.json
index 4c4ca3b582f..0f9c221fd6d 100644
--- a/spec/fixtures/api/schemas/public_api/v4/notes.json
+++ b/spec/fixtures/api/schemas/public_api/v4/notes.json
@@ -24,7 +24,10 @@
"system": { "type": "boolean" },
"noteable_id": { "type": "integer" },
"noteable_iid": { "type": "integer" },
- "noteable_type": { "type": "string" }
+ "noteable_type": { "type": "string" },
+ "resolved": { "type": "boolean" },
+ "resolvable": { "type": "boolean" },
+ "resolved_by": { "type": ["string", "null"] }
},
"required": [
"id", "body", "attachment", "author", "created_at", "updated_at",
diff --git a/spec/helpers/application_helper_spec.rb b/spec/helpers/application_helper_spec.rb
index 43cb0dfe163..5e454f8b310 100644
--- a/spec/helpers/application_helper_spec.rb
+++ b/spec/helpers/application_helper_spec.rb
@@ -2,8 +2,6 @@
require 'spec_helper'
describe ApplicationHelper do
- include UploadHelpers
-
describe 'current_controller?' do
it 'returns true when controller matches argument' do
stub_controller_name('foo')
@@ -54,143 +52,6 @@ describe ApplicationHelper do
end
end
- describe 'project_icon' do
- it 'returns an url for the avatar' do
- project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
-
- expect(helper.project_icon(project.full_path).to_s)
- .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
- end
- end
-
- describe 'avatar_icon_for' do
- let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') }
- let(:email) { 'foo@example.com' }
- let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) }
-
- it 'prefers the user to retrieve the avatar_url' do
- expect(helper.avatar_icon_for(user, email).to_s)
- .to eq(user.avatar.url)
- end
-
- it 'falls back to email lookup if no user given' do
- expect(helper.avatar_icon_for(nil, email).to_s)
- .to eq(another_user.avatar.url)
- end
- end
-
- describe 'avatar_icon_for_email' do
- let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
-
- context 'using an email' do
- context 'when there is a matching user' do
- it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon_for_email(user.email).to_s)
- .to eq(user.avatar.url)
- end
- end
-
- context 'when no user exists for the email' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
-
- helper.avatar_icon_for_email('foo@example.com', 20, 2)
- end
- end
-
- context 'without an email passed' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
-
- helper.avatar_icon_for_email(nil, 20, 2)
- end
- end
- end
- end
-
- describe 'avatar_icon_for_user' do
- let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
-
- context 'with a user object passed' do
- it 'returns a relative URL for the avatar' do
- expect(helper.avatar_icon_for_user(user).to_s)
- .to eq(user.avatar.url)
- end
- end
-
- context 'without a user object passed' do
- it 'calls gravatar_icon' do
- expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
-
- helper.avatar_icon_for_user(nil, 20, 2)
- end
- end
- end
-
- describe 'gravatar_icon' do
- let(:user_email) { 'user@email.com' }
-
- context 'with Gravatar disabled' do
- before do
- stub_application_setting(gravatar_enabled?: false)
- end
-
- it 'returns a generic avatar' do
- expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png')
- end
- end
-
- context 'with Gravatar enabled' do
- before do
- stub_application_setting(gravatar_enabled?: true)
- end
-
- it 'returns a generic avatar when email is blank' do
- expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png')
- end
-
- it 'returns a valid Gravatar URL' do
- stub_config_setting(https: false)
-
- expect(helper.gravatar_icon(user_email))
- .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
- end
-
- it 'uses HTTPs when configured' do
- stub_config_setting(https: true)
-
- expect(helper.gravatar_icon(user_email))
- .to match('https://secure.gravatar.com')
- end
-
- it 'returns custom gravatar path when gravatar_url is set' do
- stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
-
- expect(gravatar_icon(user_email, 20))
- .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
- end
-
- it 'accepts a custom size argument' do
- expect(helper.gravatar_icon(user_email, 64)).to include '?s=128'
- end
-
- it 'defaults size to 40@2x when given an invalid size' do
- expect(helper.gravatar_icon(user_email, nil)).to include '?s=80'
- end
-
- it 'accepts a scaling factor' do
- expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120'
- end
-
- it 'ignores case and surrounding whitespace' do
- normal = helper.gravatar_icon('foo@example.com')
- upcase = helper.gravatar_icon(' FOO@EXAMPLE.COM ')
-
- expect(normal).to eq upcase
- end
- end
- end
-
describe 'simple_sanitize' do
let(:a_tag) { '<a href="#">Foo</a>' }
diff --git a/spec/helpers/auth_helper_spec.rb b/spec/helpers/auth_helper_spec.rb
index c94fedd615b..120b23e66ac 100644
--- a/spec/helpers/auth_helper_spec.rb
+++ b/spec/helpers/auth_helper_spec.rb
@@ -18,6 +18,30 @@ describe AuthHelper do
end
end
+ describe "providers_for_base_controller" do
+ it 'returns all enabled providers from devise' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :github] }
+ expect(helper.providers_for_base_controller).to include(*[:twitter, :github])
+ end
+
+ it 'excludes ldap providers' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :ldapmain] }
+ expect(helper.providers_for_base_controller).not_to include(:ldapmain)
+ end
+ end
+
+ describe "form_based_providers" do
+ it 'includes LDAP providers' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :ldapmain] }
+ expect(helper.form_based_providers).to eq %i(ldapmain)
+ end
+
+ it 'includes crowd provider' do
+ allow(helper).to receive(:auth_providers) { [:twitter, :crowd] }
+ expect(helper.form_based_providers).to eq %i(crowd)
+ end
+ end
+
describe 'enabled_button_based_providers' do
before do
allow(helper).to receive(:auth_providers) { [:twitter, :github] }
diff --git a/spec/helpers/avatars_helper_spec.rb b/spec/helpers/avatars_helper_spec.rb
index 04c6d259135..5856bccb5b8 100644
--- a/spec/helpers/avatars_helper_spec.rb
+++ b/spec/helpers/avatars_helper_spec.rb
@@ -1,10 +1,147 @@
require 'rails_helper'
describe AvatarsHelper do
- include ApplicationHelper
+ include UploadHelpers
let(:user) { create(:user) }
+ describe '#project_icon' do
+ it 'returns an url for the avatar' do
+ project = create(:project, :public, avatar: File.open(uploaded_image_temp_path))
+
+ expect(helper.project_icon(project.full_path).to_s)
+ .to eq "<img data-src=\"#{project.avatar.url}\" class=\" lazy\" src=\"#{LazyImageTagHelper.placeholder_image}\" />"
+ end
+ end
+
+ describe '#avatar_icon_for' do
+ let!(:user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: 'bar@example.com') }
+ let(:email) { 'foo@example.com' }
+ let!(:another_user) { create(:user, avatar: File.open(uploaded_image_temp_path), email: email) }
+
+ it 'prefers the user to retrieve the avatar_url' do
+ expect(helper.avatar_icon_for(user, email).to_s)
+ .to eq(user.avatar.url)
+ end
+
+ it 'falls back to email lookup if no user given' do
+ expect(helper.avatar_icon_for(nil, email).to_s)
+ .to eq(another_user.avatar.url)
+ end
+ end
+
+ describe '#avatar_icon_for_email' do
+ let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
+
+ context 'using an email' do
+ context 'when there is a matching user' do
+ it 'returns a relative URL for the avatar' do
+ expect(helper.avatar_icon_for_email(user.email).to_s)
+ .to eq(user.avatar.url)
+ end
+ end
+
+ context 'when no user exists for the email' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with('foo@example.com', 20, 2)
+
+ helper.avatar_icon_for_email('foo@example.com', 20, 2)
+ end
+ end
+
+ context 'without an email passed' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
+
+ helper.avatar_icon_for_email(nil, 20, 2)
+ end
+ end
+ end
+ end
+
+ describe '#avatar_icon_for_user' do
+ let(:user) { create(:user, avatar: File.open(uploaded_image_temp_path)) }
+
+ context 'with a user object passed' do
+ it 'returns a relative URL for the avatar' do
+ expect(helper.avatar_icon_for_user(user).to_s)
+ .to eq(user.avatar.url)
+ end
+ end
+
+ context 'without a user object passed' do
+ it 'calls gravatar_icon' do
+ expect(helper).to receive(:gravatar_icon).with(nil, 20, 2)
+
+ helper.avatar_icon_for_user(nil, 20, 2)
+ end
+ end
+ end
+
+ describe '#gravatar_icon' do
+ let(:user_email) { 'user@email.com' }
+
+ context 'with Gravatar disabled' do
+ before do
+ stub_application_setting(gravatar_enabled?: false)
+ end
+
+ it 'returns a generic avatar' do
+ expect(helper.gravatar_icon(user_email)).to match_asset_path('no_avatar.png')
+ end
+ end
+
+ context 'with Gravatar enabled' do
+ before do
+ stub_application_setting(gravatar_enabled?: true)
+ end
+
+ it 'returns a generic avatar when email is blank' do
+ expect(helper.gravatar_icon('')).to match_asset_path('no_avatar.png')
+ end
+
+ it 'returns a valid Gravatar URL' do
+ stub_config_setting(https: false)
+
+ expect(helper.gravatar_icon(user_email))
+ .to match('https://www.gravatar.com/avatar/b58c6f14d292556214bd64909bcdb118')
+ end
+
+ it 'uses HTTPs when configured' do
+ stub_config_setting(https: true)
+
+ expect(helper.gravatar_icon(user_email))
+ .to match('https://secure.gravatar.com')
+ end
+
+ it 'returns custom gravatar path when gravatar_url is set' do
+ stub_gravatar_setting(plain_url: 'http://example.local/?s=%{size}&hash=%{hash}')
+
+ expect(gravatar_icon(user_email, 20))
+ .to eq('http://example.local/?s=40&hash=b58c6f14d292556214bd64909bcdb118')
+ end
+
+ it 'accepts a custom size argument' do
+ expect(helper.gravatar_icon(user_email, 64)).to include '?s=128'
+ end
+
+ it 'defaults size to 40@2x when given an invalid size' do
+ expect(helper.gravatar_icon(user_email, nil)).to include '?s=80'
+ end
+
+ it 'accepts a scaling factor' do
+ expect(helper.gravatar_icon(user_email, 40, 3)).to include '?s=120'
+ end
+
+ it 'ignores case and surrounding whitespace' do
+ normal = helper.gravatar_icon('foo@example.com')
+ upcase = helper.gravatar_icon(' FOO@EXAMPLE.COM ')
+
+ expect(normal).to eq upcase
+ end
+ end
+ end
+
describe '#user_avatar' do
subject { helper.user_avatar(user: user) }
diff --git a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
index 56a2543660b..9e437084224 100644
--- a/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
+++ b/spec/javascripts/sidebar/sidebar_subscriptions_spec.js
@@ -3,7 +3,6 @@ import sidebarSubscriptions from '~/sidebar/components/subscriptions/sidebar_sub
import SidebarMediator from '~/sidebar/sidebar_mediator';
import SidebarService from '~/sidebar/services/sidebar_service';
import SidebarStore from '~/sidebar/stores/sidebar_store';
-import eventHub from '~/sidebar/event_hub';
import mountComponent from 'spec/helpers/vue_mount_component_helper';
import Mock from './mock_data';
@@ -32,7 +31,7 @@ describe('Sidebar Subscriptions', function () {
mediator,
});
- eventHub.$emit('toggleSubscription');
+ vm.onToggleSubscription();
expect(mediator.toggleSubscription).toHaveBeenCalled();
});
diff --git a/spec/javascripts/sidebar/subscriptions_spec.js b/spec/javascripts/sidebar/subscriptions_spec.js
index aee8f0acbb9..11ff126dfb5 100644
--- a/spec/javascripts/sidebar/subscriptions_spec.js
+++ b/spec/javascripts/sidebar/subscriptions_spec.js
@@ -39,4 +39,12 @@ describe('Subscriptions', function () {
expect(vm.$refs.toggleButton.$el.querySelector('.project-feature-toggle')).toHaveClass('is-checked');
});
+
+ it('toggleSubscription method emits `toggleSubscription` event on component', () => {
+ vm = mountComponent(Subscriptions, { subscribed: true });
+ spyOn(vm, '$emit');
+
+ vm.toggleSubscription();
+ expect(vm.$emit).toHaveBeenCalledWith('toggleSubscription', jasmine.any(Object));
+ });
});
diff --git a/spec/javascripts/test_bundle.js b/spec/javascripts/test_bundle.js
index bcd15f5eae2..2411d33a496 100644
--- a/spec/javascripts/test_bundle.js
+++ b/spec/javascripts/test_bundle.js
@@ -84,21 +84,11 @@ beforeEach(() => {
const axiosDefaultAdapter = getDefaultAdapter();
-let testFiles = process.env.TEST_FILES || [];
-if (testFiles.length > 0) {
- testFiles = testFiles.map(path => path.replace(/^spec\/javascripts\//, '').replace(/\.js$/, ''));
- console.log(`Running only tests matching: ${testFiles}`);
-} else {
- console.log('Running all tests');
-}
-
// render all of our tests
const testsContext = require.context('.', true, /_spec$/);
testsContext.keys().forEach(function(path) {
try {
- if (testFiles.length === 0 || testFiles.some(p => path.includes(p))) {
- testsContext(path);
- }
+ testsContext(path);
} catch (err) {
console.error('[ERROR] Unable to load spec: ', path);
describe('Test bundle', function() {
diff --git a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
index 1fd145116df..068cdc85a07 100644
--- a/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
+++ b/spec/lib/banzai/filter/commit_trailers_filter_spec.rb
@@ -47,16 +47,36 @@ describe Banzai::Filter::CommitTrailersFilter do
)
end
- it 'non GitLab users and replaces them with mailto links' do
- _, message_html = build_commit_message(
- trailer: trailer,
- name: FFaker::Name.name,
- email: email
- )
+ context 'non GitLab users' do
+ shared_examples 'mailto links' do
+ it 'replaces them with mailto links' do
+ _, message_html = build_commit_message(
+ trailer: trailer,
+ name: FFaker::Name.name,
+ email: email
+ )
- doc = filter(message_html)
+ doc = filter(message_html)
- expect_to_have_mailto_link(doc, email: email, trailer: trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: trailer)
+ end
+ end
+
+ context 'when Gravatar is disabled' do
+ before do
+ stub_application_setting(gravatar_enabled: false)
+ end
+
+ it_behaves_like 'mailto links'
+ end
+
+ context 'when Gravatar is enabled' do
+ before do
+ stub_application_setting(gravatar_enabled: true)
+ end
+
+ it_behaves_like 'mailto links'
+ end
end
it 'multiple trailers in the same message' do
@@ -69,7 +89,7 @@ describe Banzai::Filter::CommitTrailersFilter do
doc = filter(message)
expect_to_have_user_link_with_avatar(doc, user: user, trailer: trailer)
- expect_to_have_mailto_link(doc, email: email, trailer: different_trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: different_trailer)
end
context 'special names' do
@@ -90,7 +110,7 @@ describe Banzai::Filter::CommitTrailersFilter do
doc = filter(message_html)
- expect_to_have_mailto_link(doc, email: email, trailer: trailer)
+ expect_to_have_mailto_link_with_avatar(doc, email: email, trailer: trailer)
expect(doc.text).to match Regexp.escape(message)
end
end
diff --git a/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
new file mode 100644
index 00000000000..f8107dd40b9
--- /dev/null
+++ b/spec/lib/gitlab/background_migration/migrate_stage_index_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+
+describe Gitlab::BackgroundMigration::MigrateStageIndex, :migration, schema: 20180420080616 do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:stages) { table(:ci_stages) }
+ let(:jobs) { table(:ci_builds) }
+
+ before do
+ namespaces.create(id: 10, name: 'gitlab-org', path: 'gitlab-org')
+ projects.create!(id: 11, namespace_id: 10, name: 'gitlab', path: 'gitlab')
+ pipelines.create!(id: 12, project_id: 11, ref: 'master', sha: 'adf43c3a')
+
+ stages.create(id: 100, project_id: 11, pipeline_id: 12, name: 'build')
+ stages.create(id: 101, project_id: 11, pipeline_id: 12, name: 'test')
+
+ jobs.create!(id: 121, commit_id: 12, project_id: 11,
+ stage_idx: 2, stage_id: 100)
+ jobs.create!(id: 122, commit_id: 12, project_id: 11,
+ stage_idx: 2, stage_id: 100)
+ jobs.create!(id: 123, commit_id: 12, project_id: 11,
+ stage_idx: 10, stage_id: 100)
+ jobs.create!(id: 124, commit_id: 12, project_id: 11,
+ stage_idx: 3, stage_id: 101)
+ end
+
+ it 'correctly migrates stages indices' do
+ expect(stages.all.pluck(:position)).to all(be_nil)
+
+ described_class.new.perform(100, 101)
+
+ expect(stages.all.pluck(:position)).to eq [2, 3]
+ end
+end
diff --git a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
index dc12ba076bc..0edc3f315bb 100644
--- a/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/chain/create_spec.rb
@@ -17,7 +17,7 @@ describe Gitlab::Ci::Pipeline::Chain::Create do
context 'when pipeline is ready to be saved' do
before do
- pipeline.stages.build(name: 'test', project: project)
+ pipeline.stages.build(name: 'test', position: 0, project: project)
step.perform!
end
diff --git a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
index eb1b285c7bd..05ce3412fd8 100644
--- a/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
+++ b/spec/lib/gitlab/ci/pipeline/seed/stage_spec.rb
@@ -24,7 +24,8 @@ describe Gitlab::Ci::Pipeline::Seed::Stage do
describe '#attributes' do
it 'returns hash attributes of a stage' do
expect(subject.attributes).to be_a Hash
- expect(subject.attributes).to include(:name, :project)
+ expect(subject.attributes)
+ .to include(:name, :position, :pipeline, :project)
end
end
diff --git a/spec/lib/gitlab/import_export/safe_model_attributes.yml b/spec/lib/gitlab/import_export/safe_model_attributes.yml
index 31141807cb2..62da967cf96 100644
--- a/spec/lib/gitlab/import_export/safe_model_attributes.yml
+++ b/spec/lib/gitlab/import_export/safe_model_attributes.yml
@@ -232,6 +232,7 @@ Ci::Stage:
- id
- name
- status
+- position
- lock_version
- project_id
- pipeline_id
diff --git a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
index 3cfdae794f6..7be8be54d5e 100644
--- a/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/base_command_spec.rb
@@ -4,22 +4,10 @@ describe Gitlab::Kubernetes::Helm::BaseCommand do
let(:application) { create(:clusters_applications_helm) }
let(:base_command) { described_class.new(application.name) }
- describe '#generate_script' do
- let(:helm_version) { Gitlab::Kubernetes::Helm::HELM_VERSION }
- let(:command) do
- <<~HEREDOC
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v#{helm_version}-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- HEREDOC
- end
-
- subject { base_command.generate_script }
+ subject { base_command }
- it 'should return a command that prepares the environment for helm-cli' do
- expect(subject).to eq(command)
- end
+ it_behaves_like 'helm commands' do
+ let(:commands) { '' }
end
describe '#pod_resource' do
diff --git a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
index e6920b0a76f..89e36a298f8 100644
--- a/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/init_command_spec.rb
@@ -2,23 +2,9 @@ require 'spec_helper'
describe Gitlab::Kubernetes::Helm::InitCommand do
let(:application) { create(:clusters_applications_helm) }
- let(:init_command) { described_class.new(application.name) }
+ let(:commands) { 'helm init >/dev/null' }
- describe '#generate_script' do
- let(:command) do
- <<~MSG.chomp
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init >/dev/null
- MSG
- end
+ subject { described_class.new(application.name) }
- subject { init_command.generate_script }
-
- it 'should return the appropriate command' do
- is_expected.to eq(command)
- end
- end
+ it_behaves_like 'helm commands'
end
diff --git a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
index 137b8f718de..547f3f1752c 100644
--- a/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
+++ b/spec/lib/gitlab/kubernetes/helm/install_command_spec.rb
@@ -12,50 +12,36 @@ describe Gitlab::Kubernetes::Helm::InstallCommand do
)
end
- describe '#generate_script' do
- let(:command) do
- <<~MSG
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init --client-only >/dev/null
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
- MSG
- end
-
- subject { install_command.generate_script }
+ subject { install_command }
- it 'should return appropriate command' do
- is_expected.to eq(command)
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
end
+ end
- context 'with an application with a repository' do
- let(:ci_runner) { create(:ci_runner) }
- let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
- let(:install_command) do
- described_class.new(
- application.name,
- chart: application.chart,
- values: application.values,
- repository: application.repository
- )
- end
-
- let(:command) do
- <<~MSG
- set -eo pipefail
- apk add -U ca-certificates openssl >/dev/null
- wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
- mv /tmp/linux-amd64/helm /usr/bin/
- helm init --client-only >/dev/null
- helm repo add #{application.name} #{application.repository}
- helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
- MSG
- end
+ context 'with an application with a repository' do
+ let(:ci_runner) { create(:ci_runner) }
+ let(:application) { create(:clusters_applications_runner, runner: ci_runner) }
+ let(:install_command) do
+ described_class.new(
+ application.name,
+ chart: application.chart,
+ values: application.values,
+ repository: application.repository
+ )
+ end
- it 'should return appropriate command' do
- is_expected.to eq(command)
+ it_behaves_like 'helm commands' do
+ let(:commands) do
+ <<~EOS
+ helm init --client-only >/dev/null
+ helm repo add #{application.name} #{application.repository}
+ helm install #{application.chart} --name #{application.name} --namespace #{namespace} -f /data/helm/#{application.name}/config/values.yaml >/dev/null
+ EOS
end
end
end
diff --git a/spec/migrations/schedule_stages_index_migration_spec.rb b/spec/migrations/schedule_stages_index_migration_spec.rb
new file mode 100644
index 00000000000..710264da375
--- /dev/null
+++ b/spec/migrations/schedule_stages_index_migration_spec.rb
@@ -0,0 +1,35 @@
+require 'spec_helper'
+require Rails.root.join('db', 'post_migrate', '20180420080616_schedule_stages_index_migration')
+
+describe ScheduleStagesIndexMigration, :sidekiq, :migration do
+ let(:namespaces) { table(:namespaces) }
+ let(:projects) { table(:projects) }
+ let(:pipelines) { table(:ci_pipelines) }
+ let(:stages) { table(:ci_stages) }
+
+ before do
+ stub_const("#{described_class}::BATCH_SIZE", 1)
+
+ namespaces.create(id: 12, name: 'gitlab-org', path: 'gitlab-org')
+ projects.create!(id: 123, namespace_id: 12, name: 'gitlab', path: 'gitlab')
+ pipelines.create!(id: 1, project_id: 123, ref: 'master', sha: 'adf43c3a')
+ stages.create!(id: 121, project_id: 123, pipeline_id: 1, name: 'build')
+ stages.create!(id: 122, project_id: 123, pipeline_id: 1, name: 'test')
+ stages.create!(id: 123, project_id: 123, pipeline_id: 1, name: 'deploy')
+ end
+
+ it 'schedules delayed background migrations in batches' do
+ Sidekiq::Testing.fake! do
+ Timecop.freeze do
+ expect(stages.all).to all(have_attributes(position: be_nil))
+
+ migrate!
+
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(5.minutes, 121, 121)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(10.minutes, 122, 122)
+ expect(described_class::MIGRATION).to be_scheduled_delayed_migration(15.minutes, 123, 123)
+ expect(BackgroundMigrationWorker.jobs.size).to eq 3
+ end
+ end
+ end
+end
diff --git a/spec/models/active_session_spec.rb b/spec/models/active_session_spec.rb
new file mode 100644
index 00000000000..129b2f92683
--- /dev/null
+++ b/spec/models/active_session_spec.rb
@@ -0,0 +1,216 @@
+require 'rails_helper'
+
+RSpec.describe ActiveSession, :clean_gitlab_redis_shared_state do
+ let(:user) do
+ create(:user).tap do |user|
+ user.current_sign_in_at = Time.current
+ end
+ end
+
+ let(:session) { double(:session, id: '6919a6f1bb119dd7396fadc38fd18d0d') }
+
+ let(:request) do
+ double(:request, {
+ user_agent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 8_1_3 like Mac OS X) AppleWebKit/600.1.4 ' \
+ '(KHTML, like Gecko) Mobile/12B466 [FBDV/iPhone7,2]',
+ ip: '127.0.0.1',
+ session: session
+ })
+ end
+
+ describe '#current?' do
+ it 'returns true if the active session matches the current session' do
+ active_session = ActiveSession.new(session_id: '6919a6f1bb119dd7396fadc38fd18d0d')
+
+ expect(active_session.current?(session)).to be true
+ end
+
+ it 'returns false if the active session does not match the current session' do
+ active_session = ActiveSession.new(session_id: '59822c7d9fcdfa03725eff41782ad97d')
+
+ expect(active_session.current?(session)).to be false
+ end
+
+ it 'returns false if the session id is nil' do
+ active_session = ActiveSession.new(session_id: nil)
+ session = double(:session, id: nil)
+
+ expect(active_session.current?(session)).to be false
+ end
+ end
+
+ describe '.list' do
+ it 'returns all sessions by user' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' }))
+ redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", Marshal.dump({ session_id: 'b' }))
+ redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
+
+ redis.sadd(
+ "session:lookup:user:gitlab:#{user.id}",
+ %w[
+ 6919a6f1bb119dd7396fadc38fd18d0d
+ 59822c7d9fcdfa03725eff41782ad97d
+ ]
+ )
+ end
+
+ expect(ActiveSession.list(user)).to match_array [{ session_id: 'a' }, { session_id: 'b' }]
+ end
+
+ it 'does not return obsolete entries and cleans them up' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", Marshal.dump({ session_id: 'a' }))
+
+ redis.sadd(
+ "session:lookup:user:gitlab:#{user.id}",
+ %w[
+ 6919a6f1bb119dd7396fadc38fd18d0d
+ 59822c7d9fcdfa03725eff41782ad97d
+ ]
+ )
+ end
+
+ expect(ActiveSession.list(user)).to eq [{ session_id: 'a' }]
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.sscan_each("session:lookup:user:gitlab:#{user.id}").to_a).to eq ['6919a6f1bb119dd7396fadc38fd18d0d']
+ end
+ end
+
+ it 'returns an empty array if the use does not have any active session' do
+ expect(ActiveSession.list(user)).to eq []
+ end
+ end
+
+ describe '.set' do
+ it 'sets a new redis entry for the user session and a lookup entry' do
+ ActiveSession.set(user, request)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each.to_a).to match_array [
+ "session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d",
+ "session:lookup:user:gitlab:#{user.id}"
+ ]
+ end
+ end
+
+ it 'adds timestamps and information from the request' do
+ Timecop.freeze(Time.zone.parse('2018-03-12 09:06')) do
+ ActiveSession.set(user, request)
+
+ session = ActiveSession.list(user)
+
+ expect(session.count).to eq 1
+ expect(session.first).to have_attributes(
+ ip_address: '127.0.0.1',
+ browser: 'Mobile Safari',
+ os: 'iOS',
+ device_name: 'iPhone 6',
+ device_type: 'smartphone',
+ created_at: Time.zone.parse('2018-03-12 09:06'),
+ updated_at: Time.zone.parse('2018-03-12 09:06'),
+ session_id: '6919a6f1bb119dd7396fadc38fd18d0d'
+ )
+ end
+ end
+
+ it 'keeps the created_at from the login on consecutive requests' do
+ now = Time.zone.parse('2018-03-12 09:06')
+
+ Timecop.freeze(now) do
+ ActiveSession.set(user, request)
+
+ Timecop.freeze(now + 1.minute) do
+ ActiveSession.set(user, request)
+
+ session = ActiveSession.list(user)
+
+ expect(session.first).to have_attributes(
+ created_at: Time.zone.parse('2018-03-12 09:06'),
+ updated_at: Time.zone.parse('2018-03-12 09:07')
+ )
+ end
+ end
+ end
+ end
+
+ describe '.destroy' do
+ it 'removes the entry associated with the currently killed user session' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ redis.set("session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d", '')
+ redis.set("session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358", '')
+ end
+
+ ActiveSession.destroy(user, request.session.id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each(match: "session:user:gitlab:*")).to match_array [
+ "session:user:gitlab:#{user.id}:59822c7d9fcdfa03725eff41782ad97d",
+ "session:user:gitlab:9999:5c8611e4f9c69645ad1a1492f4131358"
+ ]
+ end
+ end
+
+ it 'removes the lookup entry' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
+ end
+
+ ActiveSession.destroy(user, request.session.id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each(match: "session:lookup:user:gitlab:#{user.id}").to_a).to be_empty
+ end
+ end
+
+ it 'removes the devise session' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ end
+
+ ActiveSession.destroy(user, request.session.id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each(match: "session:gitlab:*").to_a).to be_empty
+ end
+ end
+
+ it 'does not remove the devise session if the active session could not be found' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:gitlab:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ end
+
+ other_user = create(:user)
+
+ ActiveSession.destroy(other_user, request.session.id)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.scan_each(match: "session:gitlab:*").to_a).not_to be_empty
+ end
+ end
+ end
+
+ describe '.cleanup' do
+ it 'removes obsolete lookup entries' do
+ Gitlab::Redis::SharedState.with do |redis|
+ redis.set("session:user:gitlab:#{user.id}:6919a6f1bb119dd7396fadc38fd18d0d", '')
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '6919a6f1bb119dd7396fadc38fd18d0d')
+ redis.sadd("session:lookup:user:gitlab:#{user.id}", '59822c7d9fcdfa03725eff41782ad97d')
+ end
+
+ ActiveSession.cleanup(user)
+
+ Gitlab::Redis::SharedState.with do |redis|
+ expect(redis.smembers("session:lookup:user:gitlab:#{user.id}")).to eq ['6919a6f1bb119dd7396fadc38fd18d0d']
+ end
+ end
+
+ it 'does not bail if there are no lookup entries' do
+ ActiveSession.cleanup(user)
+ end
+ end
+end
diff --git a/spec/models/ci/stage_spec.rb b/spec/models/ci/stage_spec.rb
index 586d073eb5e..a00db1d2bfc 100644
--- a/spec/models/ci/stage_spec.rb
+++ b/spec/models/ci/stage_spec.rb
@@ -51,7 +51,7 @@ describe Ci::Stage, :models do
end
end
- describe 'update_status' do
+ describe '#update_status' do
context 'when stage objects needs to be updated' do
before do
create(:ci_build, :success, stage_id: stage.id)
@@ -87,4 +87,36 @@ describe Ci::Stage, :models do
end
end
end
+
+ describe '#index' do
+ context 'when stage has been imported and does not have position index set' do
+ before do
+ stage.update_column(:position, nil)
+ end
+
+ context 'when stage has statuses' do
+ before do
+ create(:ci_build, :running, stage_id: stage.id, stage_idx: 10)
+ end
+
+ it 'recalculates index before updating status' do
+ expect(stage.reload.position).to be_nil
+
+ stage.update_status
+
+ expect(stage.reload.position).to eq 10
+ end
+ end
+
+ context 'when stage does not have statuses' do
+ it 'fallbacks to zero' do
+ expect(stage.reload.position).to be_nil
+
+ stage.update_status
+
+ expect(stage.reload.position).to eq 0
+ end
+ end
+ end
+ end
end
diff --git a/spec/models/diff_note_spec.rb b/spec/models/diff_note_spec.rb
index 2705421e540..fb51c0172ab 100644
--- a/spec/models/diff_note_spec.rb
+++ b/spec/models/diff_note_spec.rb
@@ -85,12 +85,35 @@ describe DiffNote do
end
describe "#diff_file" do
- it "returns the correct diff file" do
- diff_file = subject.diff_file
+ context 'when the discussion was created in the diff' do
+ it 'returns correct diff file' do
+ diff_file = subject.diff_file
- expect(diff_file.old_path).to eq(position.old_path)
- expect(diff_file.new_path).to eq(position.new_path)
- expect(diff_file.diff_refs).to eq(position.diff_refs)
+ expect(diff_file.old_path).to eq(position.old_path)
+ expect(diff_file.new_path).to eq(position.new_path)
+ expect(diff_file.diff_refs).to eq(position.diff_refs)
+ end
+ end
+
+ context 'when discussion is outdated or not created in the diff' do
+ let(:diff_refs) { project.commit(sample_commit.id).diff_refs }
+ let(:position) do
+ Gitlab::Diff::Position.new(
+ old_path: "files/ruby/popen.rb",
+ new_path: "files/ruby/popen.rb",
+ old_line: nil,
+ new_line: 14,
+ diff_refs: diff_refs
+ )
+ end
+
+ it 'returns the correct diff file' do
+ diff_file = subject.diff_file
+
+ expect(diff_file.old_path).to eq(position.old_path)
+ expect(diff_file.new_path).to eq(position.new_path)
+ expect(diff_file.diff_refs).to eq(position.diff_refs)
+ end
end
end
diff --git a/spec/models/lfs_object_spec.rb b/spec/models/lfs_object_spec.rb
index ba06ff42d87..6e35511e848 100644
--- a/spec/models/lfs_object_spec.rb
+++ b/spec/models/lfs_object_spec.rb
@@ -62,9 +62,7 @@ describe LfsObject do
.with('LfsObjectUploader', described_class.name, :file, kind_of(Numeric))
.once
- lfs_object = create(:lfs_object)
- lfs_object.file = fixture_file_upload(Rails.root + "spec/fixtures/dk.png", "`/png")
- lfs_object.save!
+ create(:lfs_object, :with_file)
end
end
end
diff --git a/spec/models/notification_setting_spec.rb b/spec/models/notification_setting_spec.rb
index 2a0d102d3fe..12681a147b4 100644
--- a/spec/models/notification_setting_spec.rb
+++ b/spec/models/notification_setting_spec.rb
@@ -40,7 +40,12 @@ RSpec.describe NotificationSetting do
expect(notification_setting.new_issue).to eq(true)
expect(notification_setting.close_issue).to eq(true)
expect(notification_setting.merge_merge_request).to eq(true)
- expect(notification_setting.close_merge_request).to eq(false)
+
+ # In Rails 5 assigning a value which is not explicitly `true` or `false` ("nil" in this case)
+ # to a boolean column transforms it to `true`.
+ # In Rails 4 it transforms the value to `false` with deprecation warning.
+ # Replace `eq(Gitlab.rails5?)` with `eq(true)` when removing rails5? code.
+ expect(notification_setting.close_merge_request).to eq(Gitlab.rails5?)
expect(notification_setting.reopen_merge_request).to eq(false)
end
end
diff --git a/spec/requests/api/discussions_spec.rb b/spec/requests/api/discussions_spec.rb
index 4a44b219a67..ef34192f888 100644
--- a/spec/requests/api/discussions_spec.rb
+++ b/spec/requests/api/discussions_spec.rb
@@ -2,32 +2,53 @@ require 'spec_helper'
describe API::Discussions do
let(:user) { create(:user) }
- let!(:project) { create(:project, :public, namespace: user.namespace) }
+ let!(:project) { create(:project, :public, :repository, namespace: user.namespace) }
let(:private_user) { create(:user) }
before do
- project.add_reporter(user)
+ project.add_developer(user)
end
- context "when noteable is an Issue" do
+ context 'when noteable is an Issue' do
let!(:issue) { create(:issue, project: project, author: user) }
let!(:issue_note) { create(:discussion_note_on_issue, noteable: issue, project: project, author: user) }
- it_behaves_like "discussions API", 'projects', 'issues', 'iid' do
+ it_behaves_like 'discussions API', 'projects', 'issues', 'iid' do
let(:parent) { project }
let(:noteable) { issue }
let(:note) { issue_note }
end
end
- context "when noteable is a Snippet" do
+ context 'when noteable is a Snippet' do
let!(:snippet) { create(:project_snippet, project: project, author: user) }
let!(:snippet_note) { create(:discussion_note_on_snippet, noteable: snippet, project: project, author: user) }
- it_behaves_like "discussions API", 'projects', 'snippets', 'id' do
+ it_behaves_like 'discussions API', 'projects', 'snippets', 'id' do
let(:parent) { project }
let(:noteable) { snippet }
let(:note) { snippet_note }
end
end
+
+ context 'when noteable is a Merge Request' do
+ let!(:noteable) { create(:merge_request_with_diffs, source_project: project, target_project: project, author: user) }
+ let!(:note) { create(:discussion_note_on_merge_request, noteable: noteable, project: project, author: user) }
+ let!(:diff_note) { create(:diff_note_on_merge_request, noteable: noteable, project: project, author: user) }
+ let(:parent) { project }
+
+ it_behaves_like 'discussions API', 'projects', 'merge_requests', 'iid'
+ it_behaves_like 'diff discussions API', 'projects', 'merge_requests', 'iid'
+ it_behaves_like 'resolvable discussions API', 'projects', 'merge_requests', 'iid'
+ end
+
+ context 'when noteable is a Commit' do
+ let!(:noteable) { create(:commit, project: project, author: user) }
+ let!(:note) { create(:discussion_note_on_commit, commit_id: noteable.id, project: project, author: user) }
+ let!(:diff_note) { create(:diff_note_on_commit, commit_id: noteable.id, project: project, author: user) }
+ let(:parent) { project }
+
+ it_behaves_like 'discussions API', 'projects', 'repository/commits', 'id'
+ it_behaves_like 'diff discussions API', 'projects', 'repository/commits', 'id'
+ end
end
diff --git a/spec/requests/api/jobs_spec.rb b/spec/requests/api/jobs_spec.rb
index 3ffdfdc0e9a..0a2963452e4 100644
--- a/spec/requests/api/jobs_spec.rb
+++ b/spec/requests/api/jobs_spec.rb
@@ -281,7 +281,7 @@ describe API::Jobs do
get_artifact_file(artifact)
expect(response).to have_gitlab_http_status(200)
- expect(response.headers)
+ expect(response.headers.to_h)
.to include('Content-Type' => 'application/json',
'Gitlab-Workhorse-Send-Data' => /artifacts-entry/)
end
@@ -311,7 +311,7 @@ describe API::Jobs do
it 'returns specific job artifacts' do
expect(response).to have_gitlab_http_status(200)
- expect(response.headers).to include(download_headers)
+ expect(response.headers.to_h).to include(download_headers)
expect(response.body).to match_file(job.artifacts_file.file.file)
end
end
@@ -462,7 +462,7 @@ describe API::Jobs do
end
it { expect(response).to have_http_status(:ok) }
- it { expect(response.headers).to include(download_headers) }
+ it { expect(response.headers.to_h).to include(download_headers) }
end
context 'when artifacts are stored remotely' do
diff --git a/spec/requests/api/runner_spec.rb b/spec/requests/api/runner_spec.rb
index 17c7a511857..6c1c13c9d80 100644
--- a/spec/requests/api/runner_spec.rb
+++ b/spec/requests/api/runner_spec.rb
@@ -1330,7 +1330,7 @@ describe API::Runner do
it 'download artifacts' do
expect(response).to have_http_status(200)
- expect(response.headers).to include download_headers
+ expect(response.headers.to_h).to include download_headers
end
end
@@ -1345,7 +1345,7 @@ describe API::Runner do
it 'uses workhorse send-url' do
expect(response).to have_gitlab_http_status(200)
- expect(response.headers).to include(
+ expect(response.headers.to_h).to include(
'Gitlab-Workhorse-Send-Data' => /send-url:/)
end
end
diff --git a/spec/requests/api/v3/builds_spec.rb b/spec/requests/api/v3/builds_spec.rb
index 00f067889a0..485d7c2cc43 100644
--- a/spec/requests/api/v3/builds_spec.rb
+++ b/spec/requests/api/v3/builds_spec.rb
@@ -232,7 +232,7 @@ describe API::V3::Builds do
it 'returns specific job artifacts' do
expect(response).to have_http_status(200)
- expect(response.headers).to include(download_headers)
+ expect(response.headers.to_h).to include(download_headers)
expect(response.body).to match_file(build.artifacts_file.file.file)
end
end
@@ -332,7 +332,7 @@ describe API::V3::Builds do
end
it { expect(response).to have_http_status(200) }
- it { expect(response.headers).to include(download_headers) }
+ it { expect(response.headers.to_h).to include(download_headers) }
end
context 'when artifacts are stored remotely' do
diff --git a/spec/services/applications/create_service_spec.rb b/spec/services/applications/create_service_spec.rb
index 47a2a9d6403..9c43b56744b 100644
--- a/spec/services/applications/create_service_spec.rb
+++ b/spec/services/applications/create_service_spec.rb
@@ -1,13 +1,17 @@
-require 'spec_helper'
+require "spec_helper"
describe ::Applications::CreateService do
let(:user) { create(:user) }
let(:params) { attributes_for(:application) }
- let(:request) { ActionController::TestRequest.new(remote_ip: '127.0.0.1') }
+ let(:request) do
+ if Gitlab.rails5?
+ ActionController::TestRequest.new({ remote_ip: "127.0.0.1" }, ActionController::TestSession.new)
+ else
+ ActionController::TestRequest.new(remote_ip: "127.0.0.1")
+ end
+ end
subject { described_class.new(user, params) }
- it 'creates an application' do
- expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1)
- end
+ it { expect { subject.execute(request) }.to change { Doorkeeper::Application.count }.by(1) }
end
diff --git a/spec/services/ci/retry_build_service_spec.rb b/spec/services/ci/retry_build_service_spec.rb
index 8de0bdf92e2..5bc6031388e 100644
--- a/spec/services/ci/retry_build_service_spec.rb
+++ b/spec/services/ci/retry_build_service_spec.rb
@@ -6,7 +6,9 @@ describe Ci::RetryBuildService do
set(:pipeline) { create(:ci_pipeline, project: project) }
let(:stage) do
- Ci::Stage.create!(project: project, pipeline: pipeline, name: 'test')
+ create(:ci_stage_entity, project: project,
+ pipeline: pipeline,
+ name: 'test')
end
let(:build) { create(:ci_build, pipeline: pipeline, stage_id: stage.id) }
diff --git a/spec/services/notes/resolve_service_spec.rb b/spec/services/notes/resolve_service_spec.rb
new file mode 100644
index 00000000000..b54d40a7a5c
--- /dev/null
+++ b/spec/services/notes/resolve_service_spec.rb
@@ -0,0 +1,23 @@
+require 'spec_helper'
+
+describe Notes::ResolveService do
+ let(:merge_request) { create(:merge_request) }
+ let(:note) { create(:diff_note_on_merge_request, noteable: merge_request, project: merge_request.project) }
+ let(:user) { merge_request.author }
+
+ describe '#execute' do
+ it "resolves the note" do
+ described_class.new(merge_request.project, user).execute(note)
+ note.reload
+
+ expect(note.resolved?).to be true
+ expect(note.resolved_by).to eq(user)
+ end
+
+ it "sends notifications if all discussions are resolved" do
+ expect_any_instance_of(MergeRequests::ResolvedDiscussionNotificationService).to receive(:execute).with(merge_request)
+
+ described_class.new(merge_request.project, user).execute(note)
+ end
+ end
+end
diff --git a/spec/services/projects/update_pages_service_spec.rb b/spec/services/projects/update_pages_service_spec.rb
index a418808fd26..347ac13828c 100644
--- a/spec/services/projects/update_pages_service_spec.rb
+++ b/spec/services/projects/update_pages_service_spec.rb
@@ -123,11 +123,13 @@ describe Projects::UpdatePagesService do
expect(execute).not_to eq(:success)
end
- it 'fails for empty file fails' do
- build.job_artifacts_archive.update_attributes(file: empty_file)
+ context 'when using empty file' do
+ let(:file) { empty_file }
- expect { execute }
- .to raise_error(Projects::UpdatePagesService::FailedToExtractError)
+ it 'fails to extract' do
+ expect { execute }
+ .to raise_error(Projects::UpdatePagesService::FailedToExtractError)
+ end
end
context 'when timeout happens by DNS error' do
diff --git a/spec/support/commit_trailers_spec_helper.rb b/spec/support/commit_trailers_spec_helper.rb
index add359946db..efa317fd2f9 100644
--- a/spec/support/commit_trailers_spec_helper.rb
+++ b/spec/support/commit_trailers_spec_helper.rb
@@ -8,7 +8,7 @@ module CommitTrailersSpecHelper
expect(wrapper.attribute('data-user').value).to eq user.id.to_s
end
- def expect_to_have_mailto_link(doc, email:, trailer:)
+ def expect_to_have_mailto_link_with_avatar(doc, email:, trailer:)
wrapper = find_user_wrapper(doc, trailer)
expect_to_have_links_with_url_and_avatar(wrapper, "mailto:#{CGI.escape_html(email)}", email)
diff --git a/spec/support/shared_examples/helm_generated_script.rb b/spec/support/shared_examples/helm_generated_script.rb
new file mode 100644
index 00000000000..56e86a87ab9
--- /dev/null
+++ b/spec/support/shared_examples/helm_generated_script.rb
@@ -0,0 +1,19 @@
+shared_examples 'helm commands' do
+ describe '#generate_script' do
+ let(:helm_setup) do
+ <<~EOS
+ set -eo pipefail
+ ALPINE_VERSION=$(cat /etc/alpine-release | cut -d '.' -f 1,2)
+ echo http://mirror.clarkson.edu/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
+ echo http://mirror1.hs-esslingen.de/pub/Mirrors/alpine/v$ALPINE_VERSION/main >> /etc/apk/repositories
+ apk add -U ca-certificates openssl >/dev/null
+ wget -q -O - https://kubernetes-helm.storage.googleapis.com/helm-v2.7.0-linux-amd64.tar.gz | tar zxC /tmp >/dev/null
+ mv /tmp/linux-amd64/helm /usr/bin/
+ EOS
+ end
+
+ it 'should return appropriate command' do
+ expect(subject.generate_script).to eq(helm_setup + commands)
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/diff_discussions.rb b/spec/support/shared_examples/requests/api/diff_discussions.rb
new file mode 100644
index 00000000000..85a4bd8ca27
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/diff_discussions.rb
@@ -0,0 +1,57 @@
+shared_examples 'diff discussions API' do |parent_type, noteable_type, id_name|
+ describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do
+ it "includes diff discussions" do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user)
+
+ discussion = json_response.find { |record| record['id'] == diff_note.discussion_id }
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(discussion).not_to be_nil
+ expect(discussion['individual_note']).to eq(false)
+ expect(discussion['notes'].first['body']).to eq(diff_note.note)
+ end
+ end
+
+ describe "GET /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id" do
+ it "returns a discussion by id" do
+ get api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions/#{diff_note.discussion_id}", user)
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['id']).to eq(diff_note.discussion_id)
+ expect(json_response['notes'].first['body']).to eq(diff_note.note)
+ expect(json_response['notes'].first['position']).to eq(diff_note.position.to_h.stringify_keys)
+ end
+ end
+
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions" do
+ it "creates a new diff note" do
+ position = diff_note.position.to_h
+
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user), body: 'hi!', position: position
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['notes'].first['body']).to eq('hi!')
+ expect(json_response['notes'].first['type']).to eq('DiffNote')
+ expect(json_response['notes'].first['position']).to eq(position.stringify_keys)
+ end
+
+ it "returns a 400 bad request error when position is invalid" do
+ position = diff_note.position.to_h.merge(new_line: '100000')
+
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/discussions", user), body: 'hi!', position: position
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+ end
+
+ describe "POST /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id/notes" do
+ it 'adds a new note to the diff discussion' do
+ post api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{diff_note.discussion_id}/notes", user), body: 'hi!'
+
+ expect(response).to have_gitlab_http_status(201)
+ expect(json_response['body']).to eq('hi!')
+ expect(json_response['type']).to eq('DiffNote')
+ end
+ end
+end
diff --git a/spec/support/shared_examples/requests/api/resolvable_discussions.rb b/spec/support/shared_examples/requests/api/resolvable_discussions.rb
new file mode 100644
index 00000000000..408ad08cc48
--- /dev/null
+++ b/spec/support/shared_examples/requests/api/resolvable_discussions.rb
@@ -0,0 +1,87 @@
+shared_examples 'resolvable discussions API' do |parent_type, noteable_type, id_name|
+ describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id" do
+ it "resolves discussion if resolved is true" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user), resolved: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['notes'].size).to eq(1)
+ expect(json_response['notes'][0]['resolved']).to eq(true)
+ end
+
+ it "unresolves discussion if resolved is false" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user), resolved: false
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['notes'].size).to eq(1)
+ expect(json_response['notes'][0]['resolved']).to eq(false)
+ end
+
+ it "returns a 400 bad request error if resolved parameter is not passed" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it "returns a 401 unauthorized error if user is not authenticated" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}"), resolved: true
+
+ expect(response).to have_gitlab_http_status(401)
+ end
+
+ it "returns a 403 error if user resolves discussion of someone else" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+
+ context 'when user does not have access to read the discussion' do
+ before do
+ parent.update!(visibility_level: Gitlab::VisibilityLevel::PRIVATE)
+ end
+
+ it 'responds with 404' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+ end
+ end
+
+ describe "PUT /#{parent_type}/:id/#{noteable_type}/:noteable_id/discussions/:discussion_id/notes/:note_id" do
+ it 'returns resolved note when resolved parameter is true' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", user), resolved: true
+
+ expect(response).to have_gitlab_http_status(200)
+ expect(json_response['resolved']).to eq(true)
+ end
+
+ it 'returns a 404 error when note id not found' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/12345", user),
+ body: 'Hello!'
+
+ expect(response).to have_gitlab_http_status(404)
+ end
+
+ it 'returns a 400 bad request error if neither body nor resolved parameter is given' do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", user)
+
+ expect(response).to have_gitlab_http_status(400)
+ end
+
+ it "returns a 403 error if user resolves note of someone else" do
+ put api("/#{parent_type}/#{parent.id}/#{noteable_type}/#{noteable[id_name]}/"\
+ "discussions/#{note.discussion_id}/notes/#{note.id}", private_user), resolved: true
+
+ expect(response).to have_gitlab_http_status(403)
+ end
+ end
+end
diff --git a/spec/uploaders/lfs_object_uploader_spec.rb b/spec/uploaders/lfs_object_uploader_spec.rb
index a2fb3886610..9f28510c3e4 100644
--- a/spec/uploaders/lfs_object_uploader_spec.rb
+++ b/spec/uploaders/lfs_object_uploader_spec.rb
@@ -46,8 +46,7 @@ describe LfsObjectUploader do
end
describe 'remote file' do
- let(:remote) { described_class::Store::REMOTE }
- let(:lfs_object) { create(:lfs_object, file_store: remote) }
+ let(:lfs_object) { create(:lfs_object, :object_storage, :with_file) }
context 'with object storage enabled' do
before do
@@ -57,16 +56,11 @@ describe LfsObjectUploader do
it 'can store file remotely' do
allow(ObjectStorage::BackgroundMoveWorker).to receive(:perform_async)
- store_file(lfs_object)
+ lfs_object
- expect(lfs_object.file_store).to eq remote
+ expect(lfs_object.file_store).to eq(described_class::Store::REMOTE)
expect(lfs_object.file.path).not_to be_blank
end
end
end
-
- def store_file(lfs_object)
- lfs_object.file = fixture_file_upload(Rails.root.join("spec/fixtures/dk.png"), "`/png")
- lfs_object.save!
- end
end