diff options
author | Grzegorz Bizon <grzegorz@gitlab.com> | 2018-12-20 14:47:01 +0300 |
---|---|---|
committer | Grzegorz Bizon <grzegorz@gitlab.com> | 2018-12-20 14:47:01 +0300 |
commit | c111e2657df22c811191135369d599923dc89f54 (patch) | |
tree | 2de468666124191dcf815cf4dd92ea21fa76ca16 /spec/support | |
parent | cad0661aadff50b4d2c2b4cc7b012809b945213c (diff) | |
parent | 37c934e089508e053e6ad4cf075b00cfaab53f3c (diff) |
Merge branch 'master' into 'feature/option-to-make-variables-protected'
Conflicts:
db/schema.rb
Diffstat (limited to 'spec/support')
36 files changed, 643 insertions, 71 deletions
diff --git a/spec/support/active_record_enum.rb b/spec/support/active_record_enum.rb new file mode 100644 index 00000000000..fb1189c7f17 --- /dev/null +++ b/spec/support/active_record_enum.rb @@ -0,0 +1,12 @@ +# frozen_string_literal: true + +shared_examples 'having unique enum values' do + described_class.defined_enums.each do |name, enum| + it "has unique values in #{name.inspect}" do + duplicated = enum.group_by(&:last).select { |key, value| value.size > 1 } + + expect(duplicated).to be_empty, + "Duplicated values detected: #{duplicated.values.map(&Hash.method(:[]))}" + end + end +end diff --git a/spec/support/carrierwave.rb b/spec/support/carrierwave.rb index b4b016e408f..b376822d530 100644 --- a/spec/support/carrierwave.rb +++ b/spec/support/carrierwave.rb @@ -1,7 +1,7 @@ CarrierWave.root = File.expand_path('tmp/tests/public', Rails.root) RSpec.configure do |config| - config.after(:each) do + config.after do FileUtils.rm_rf(CarrierWave.root) end end diff --git a/spec/support/controllers/sessionless_auth_controller_shared_examples.rb b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb new file mode 100644 index 00000000000..355555d9d19 --- /dev/null +++ b/spec/support/controllers/sessionless_auth_controller_shared_examples.rb @@ -0,0 +1,92 @@ +shared_examples 'authenticates sessionless user' do |path, format, params| + params ||= {} + + before do + stub_authentication_activity_metrics(debug: false) + end + + let(:user) { create(:user) } + let(:personal_access_token) { create(:personal_access_token, user: user) } + let(:default_params) { { format: format }.merge(params.except(:public) || {}) } + + context "when the 'personal_access_token' param is populated with the personal access token" do + it 'logs the user in' do + expect(authentication_metrics) + .to increment(:user_authenticated_counter) + .and increment(:user_session_override_counter) + .and increment(:user_sessionless_authentication_counter) + + get path, params: default_params.merge(private_token: personal_access_token.token) + + expect(response).to have_gitlab_http_status(200) + expect(controller.current_user).to eq(user) + end + + it 'does not log the user in if page is public', if: params[:public] do + get path, params: default_params + + expect(response).to have_gitlab_http_status(200) + expect(controller.current_user).to be_nil + end + end + + context 'when the personal access token has no api scope', unless: params[:public] do + it 'does not log the user in' do + expect(authentication_metrics) + .to increment(:user_unauthenticated_counter) + + personal_access_token.update(scopes: [:read_user]) + + get path, params: default_params.merge(private_token: personal_access_token.token) + + expect(response).not_to have_gitlab_http_status(200) + end + end + + context "when the 'PERSONAL_ACCESS_TOKEN' header is populated with the personal access token" do + it 'logs the user in' do + expect(authentication_metrics) + .to increment(:user_authenticated_counter) + .and increment(:user_session_override_counter) + .and increment(:user_sessionless_authentication_counter) + + @request.headers['PRIVATE-TOKEN'] = personal_access_token.token + get path, params: default_params + + expect(response).to have_gitlab_http_status(200) + end + end + + context "when the 'feed_token' param is populated with the feed token", if: format == :rss do + it "logs the user in" do + expect(authentication_metrics) + .to increment(:user_authenticated_counter) + .and increment(:user_session_override_counter) + .and increment(:user_sessionless_authentication_counter) + + get path, params: default_params.merge(feed_token: user.feed_token) + + expect(response).to have_gitlab_http_status 200 + end + end + + context "when the 'feed_token' param is populated with an invalid feed token", if: format == :rss, unless: params[:public] do + it "logs the user" do + expect(authentication_metrics) + .to increment(:user_unauthenticated_counter) + + get path, params: default_params.merge(feed_token: 'token') + + expect(response.status).not_to eq 200 + end + end + + it "doesn't log the user in otherwise", unless: params[:public] do + expect(authentication_metrics) + .to increment(:user_unauthenticated_counter) + + get path, params: default_params.merge(private_token: 'token') + + expect(response.status).not_to eq(200) + end +end diff --git a/spec/support/db_cleaner.rb b/spec/support/db_cleaner.rb index 5edc5de2a09..34b9efaaecd 100644 --- a/spec/support/db_cleaner.rb +++ b/spec/support/db_cleaner.rb @@ -23,7 +23,7 @@ RSpec.configure do |config| DatabaseCleaner.clean_with(:deletion, cache_tables: false) end - config.before(:each) do + config.before do DatabaseCleaner.strategy = :transaction end @@ -39,11 +39,11 @@ RSpec.configure do |config| DatabaseCleaner.strategy = :deletion, { cache_tables: false } end - config.before(:each) do + config.before do DatabaseCleaner.start end - config.append_after(:each) do + config.append_after do DatabaseCleaner.clean end end diff --git a/spec/support/features/discussion_comments_shared_example.rb b/spec/support/features/discussion_comments_shared_example.rb index 18cf08f0b9e..42a086d58d2 100644 --- a/spec/support/features/discussion_comments_shared_example.rb +++ b/spec/support/features/discussion_comments_shared_example.rb @@ -142,6 +142,14 @@ shared_examples 'discussion comments' do |resource_name| find(comments_selector, match: :first) end + def submit_reply(text) + find("#{comments_selector} .js-vue-discussion-reply").click + find("#{comments_selector} .note-textarea").send_keys(text) + + click_button "Comment" + wait_for_requests + end + it 'clicking "Start discussion" will post a discussion' do new_comment = all(comments_selector).last @@ -149,17 +157,40 @@ shared_examples 'discussion comments' do |resource_name| expect(new_comment).to have_selector '.discussion' end + if resource_name =~ /(issue|merge request)/ + it 'can be replied to' do + submit_reply('some text') + + expect(page).to have_css('.discussion-notes .note', count: 2) + expect(page).to have_content 'Collapse replies' + end + + it 'can be collapsed' do + submit_reply('another text') + + find('.js-collapse-replies').click + expect(page).to have_css('.discussion-notes .note', count: 1) + expect(page).to have_content '1 reply' + end + end + if resource_name == 'merge request' let(:note_id) { find("#{comments_selector} .note:first-child", match: :first)['data-note-id'] } let(:reply_id) { find("#{comments_selector} .note:last-child", match: :first)['data-note-id'] } - it 'shows resolved discussion when toggled' do - find("#{comments_selector} .js-vue-discussion-reply").click - find("#{comments_selector} .note-textarea").send_keys('a') + it 'can be replied to after resolving' do + click_button "Resolve discussion" + wait_for_requests - click_button "Comment" + refresh wait_for_requests + submit_reply('to reply or not reply') + end + + it 'shows resolved discussion when toggled' do + submit_reply('a') + click_button "Resolve discussion" wait_for_requests diff --git a/spec/support/helpers/email_helpers.rb b/spec/support/helpers/email_helpers.rb index 1fb8252459f..ad6e1064499 100644 --- a/spec/support/helpers/email_helpers.rb +++ b/spec/support/helpers/email_helpers.rb @@ -34,4 +34,13 @@ module EmailHelpers def find_email_for(user) ActionMailer::Base.deliveries.find { |d| d.to.include?(user.notification_email) } end + + def have_referable_subject(referable, include_project: true, reply: false) + prefix = (include_project && referable.project ? "#{referable.project.name} | " : '').freeze + prefix = "Re: #{prefix}" if reply + + suffix = "#{referable.title} (#{referable.to_reference})" + + have_subject [prefix, suffix].compact.join + end end diff --git a/spec/support/helpers/fake_migration_classes.rb b/spec/support/helpers/fake_migration_classes.rb index b0fc8422857..c7766df7a52 100644 --- a/spec/support/helpers/fake_migration_classes.rb +++ b/spec/support/helpers/fake_migration_classes.rb @@ -1,4 +1,4 @@ -class FakeRenameReservedPathMigrationV1 < ActiveRecord::Migration +class FakeRenameReservedPathMigrationV1 < ActiveRecord::Migration[4.2] include Gitlab::Database::RenameReservedPathsMigration::V1 def version diff --git a/spec/support/helpers/features/list_rows_helpers.rb b/spec/support/helpers/features/list_rows_helpers.rb new file mode 100644 index 00000000000..0626415361c --- /dev/null +++ b/spec/support/helpers/features/list_rows_helpers.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true +# These helpers allow you to access rows in the list +# +# Usage: +# describe "..." do +# include Spec::Support::Helpers::Features::ListRowsHelpers +# ... +# +# expect(first_row.text).to include("John Doe") +# expect(second_row.text).to include("John Smith") +# +module Spec + module Support + module Helpers + module Features + module ListRowsHelpers + def first_row + page.all('ul.content-list > li')[0] + end + + def second_row + page.all('ul.content-list > li')[1] + end + end + end + end + end +end diff --git a/spec/support/helpers/features/sorting_helpers.rb b/spec/support/helpers/features/sorting_helpers.rb index ad0053ec5cf..003ecb251fe 100644 --- a/spec/support/helpers/features/sorting_helpers.rb +++ b/spec/support/helpers/features/sorting_helpers.rb @@ -13,9 +13,9 @@ module Spec module Features module SortingHelpers def sort_by(value) - find('button.dropdown-toggle').click + find('.filter-dropdown-container .dropdown').click - page.within('.content ul.dropdown-menu.dropdown-menu-right li') do + page.within('ul.dropdown-menu.dropdown-menu-right li') do click_link(value) end end diff --git a/spec/support/helpers/git_http_helpers.rb b/spec/support/helpers/git_http_helpers.rb index b8289e6c5f1..9a5845af90c 100644 --- a/spec/support/helpers/git_http_helpers.rb +++ b/spec/support/helpers/git_http_helpers.rb @@ -60,9 +60,4 @@ module GitHttpHelpers message = Gitlab::GitAccessWiki::ERROR_MESSAGES[error_key] message || raise("GitAccessWiki error message key '#{error_key}' not found") end - - def change_access_error(error_key) - message = Gitlab::Checks::ChangeAccess::ERROR_MESSAGES[error_key] - message || raise("ChangeAccess error message key '#{error_key}' not found") - end end diff --git a/spec/support/helpers/javascript_fixtures_helpers.rb b/spec/support/helpers/javascript_fixtures_helpers.rb index 086a345dca8..89c5ec7a718 100644 --- a/spec/support/helpers/javascript_fixtures_helpers.rb +++ b/spec/support/helpers/javascript_fixtures_helpers.rb @@ -6,6 +6,13 @@ module JavaScriptFixturesHelpers FIXTURE_PATH = 'spec/javascripts/fixtures'.freeze + def self.included(base) + base.around do |example| + # pick an arbitrary date from the past, so tests are not time dependent + Timecop.freeze(Time.utc(2015, 7, 3, 10)) { example.run } + end + end + # Public: Removes all fixture files from given directory # # directory_name - directory of the fixtures (relative to FIXTURE_PATH) diff --git a/spec/support/helpers/kubernetes_helpers.rb b/spec/support/helpers/kubernetes_helpers.rb index ccaf86aa3a6..39bd305d88a 100644 --- a/spec/support/helpers/kubernetes_helpers.rb +++ b/spec/support/helpers/kubernetes_helpers.rb @@ -34,6 +34,17 @@ module KubernetesHelpers WebMock.stub_request(:get, deployments_url).to_return(response || kube_deployments_response) end + def stub_kubeclient_knative_services(**options) + options[:name] ||= "kubetest" + options[:namespace] ||= "default" + options[:domain] ||= "example.com" + + stub_kubeclient_discover(service.api_url) + knative_url = service.api_url + "/apis/serving.knative.dev/v1alpha1/services" + + WebMock.stub_request(:get, knative_url).to_return(kube_response(kube_knative_services_body(options))) + end + def stub_kubeclient_get_secret(api_url, **options) options[:metadata_name] ||= "default-token-1" options[:namespace] ||= "default" @@ -47,6 +58,11 @@ module KubernetesHelpers .to_return(status: [status, "Internal Server Error"]) end + def stub_kubeclient_get_service_account_error(api_url, name, namespace: 'default', status: 404) + WebMock.stub_request(:get, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts/#{name}") + .to_return(status: [status, "Internal Server Error"]) + end + def stub_kubeclient_create_service_account(api_url, namespace: 'default') WebMock.stub_request(:post, api_url + "/api/v1/namespaces/#{namespace}/serviceaccounts") .to_return(kube_response({})) @@ -62,11 +78,26 @@ module KubernetesHelpers .to_return(kube_response({})) end + def stub_kubeclient_put_secret(api_url, name, namespace: 'default') + WebMock.stub_request(:put, api_url + "/api/v1/namespaces/#{namespace}/secrets/#{name}") + .to_return(kube_response({})) + end + + def stub_kubeclient_get_cluster_role_binding_error(api_url, name, status: 404) + WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/clusterrolebindings/#{name}") + .to_return(status: [status, "Internal Server Error"]) + end + def stub_kubeclient_create_cluster_role_binding(api_url) WebMock.stub_request(:post, api_url + '/apis/rbac.authorization.k8s.io/v1/clusterrolebindings') .to_return(kube_response({})) end + def stub_kubeclient_get_role_binding_error(api_url, name, namespace: 'default', status: 404) + WebMock.stub_request(:get, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings/#{name}") + .to_return(status: [status, "Internal Server Error"]) + end + def stub_kubeclient_create_role_binding(api_url, namespace: 'default') WebMock.stub_request(:post, api_url + "/apis/rbac.authorization.k8s.io/v1/namespaces/#{namespace}/rolebindings") .to_return(kube_response({})) @@ -161,6 +192,13 @@ module KubernetesHelpers } end + def kube_knative_services_body(**options) + { + "kind" => "List", + "items" => [kube_service(options)] + } + end + # This is a partial response, it will have many more elements in reality but # these are the ones we care about at the moment def kube_pod(name: "kube-pod", app: "valid-pod-label", status: "Running", track: nil) @@ -204,6 +242,54 @@ module KubernetesHelpers } end + def kube_service(name: "kubetest", namespace: "default", domain: "example.com") + { + "metadata" => { + "creationTimestamp" => "2018-11-21T06:16:33Z", + "name" => name, + "namespace" => namespace, + "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}" + }, + "spec" => { + "generation" => 2 + }, + "status" => { + "domain" => "#{name}.#{namespace}.#{domain}", + "domainInternal" => "#{name}.#{namespace}.svc.cluster.local", + "latestCreatedRevisionName" => "#{name}-00002", + "latestReadyRevisionName" => "#{name}-00002", + "observedGeneration" => 2 + } + } + end + + def kube_service_full(name: "kubetest", namespace: "kube-ns", domain: "example.com") + { + "metadata" => { + "creationTimestamp" => "2018-11-21T06:16:33Z", + "name" => name, + "namespace" => namespace, + "selfLink" => "/apis/serving.knative.dev/v1alpha1/namespaces/#{namespace}/services/#{name}", + "annotation" => { + "description" => "This is a test description" + } + }, + "spec" => { + "generation" => 2, + "build" => { + "template" => "go-1.10.3" + } + }, + "status" => { + "domain" => "#{name}.#{namespace}.#{domain}", + "domainInternal" => "#{name}.#{namespace}.svc.cluster.local", + "latestCreatedRevisionName" => "#{name}-00002", + "latestReadyRevisionName" => "#{name}-00002", + "observedGeneration" => 2 + } + } + end + def kube_terminals(service, pod) pod_name = pod['metadata']['name'] containers = pod['spec']['containers'] diff --git a/spec/support/helpers/prometheus_helpers.rb b/spec/support/helpers/prometheus_helpers.rb index 4212be2cc88..ce1f9fce10d 100644 --- a/spec/support/helpers/prometheus_helpers.rb +++ b/spec/support/helpers/prometheus_helpers.rb @@ -49,11 +49,11 @@ module PrometheusHelpers "https://prometheus.example.com/api/v1/series?#{query}" end - def stub_prometheus_request(url, body: {}, status: 200) + def stub_prometheus_request(url, body: {}, status: 200, headers: {}) WebMock.stub_request(:get, url) .to_return({ status: status, - headers: { 'Content-Type' => 'application/json' }, + headers: { 'Content-Type' => 'application/json' }.merge(headers), body: body.to_json }) end diff --git a/spec/support/helpers/sorting_helper.rb b/spec/support/helpers/sorting_helper.rb index 9496a94d8f4..e505a6b7258 100644 --- a/spec/support/helpers/sorting_helper.rb +++ b/spec/support/helpers/sorting_helper.rb @@ -10,7 +10,7 @@ # module SortingHelper def sorting_by(value) - find('button.dropdown-toggle').click + find('.filter-dropdown-container button.dropdown-menu-toggle').click page.within('.content ul.dropdown-menu.dropdown-menu-right li') do click_link value end diff --git a/spec/support/helpers/stub_configuration.rb b/spec/support/helpers/stub_configuration.rb index 776119564ec..2851cd9733c 100644 --- a/spec/support/helpers/stub_configuration.rb +++ b/spec/support/helpers/stub_configuration.rb @@ -27,6 +27,11 @@ module StubConfiguration allow(Gitlab.config.gitlab).to receive_messages(to_settings(messages)) end + def stub_default_url_options(host: "localhost", protocol: "http") + url_options = { host: host, protocol: protocol } + allow(Rails.application.routes).to receive(:default_url_options).and_return(url_options) + end + def stub_gravatar_setting(messages) allow(Gitlab.config.gravatar).to receive_messages(to_settings(messages)) end diff --git a/spec/support/helpers/test_env.rb b/spec/support/helpers/test_env.rb index 1f00cdf7e92..d52c40ff4f1 100644 --- a/spec/support/helpers/test_env.rb +++ b/spec/support/helpers/test_env.rb @@ -54,6 +54,9 @@ module TestEnv 'add_images_and_changes' => '010d106', 'update-gitlab-shell-v-6-0-1' => '2f61d70', 'update-gitlab-shell-v-6-0-3' => 'de78448', + 'merge-commit-analyze-before' => '1adbdef', + 'merge-commit-analyze-side-branch' => '8a99451', + 'merge-commit-analyze-after' => '646ece5', '2-mb-file' => 'bf12d25', 'before-create-delete-modify-move' => '845009f', 'between-create-delete-modify-move' => '3f5f443', diff --git a/spec/support/helpers/test_request_helpers.rb b/spec/support/helpers/test_request_helpers.rb index 187a0e07891..5a84d67bdfc 100644 --- a/spec/support/helpers/test_request_helpers.rb +++ b/spec/support/helpers/test_request_helpers.rb @@ -2,10 +2,6 @@ module TestRequestHelpers def test_request(remote_ip: '127.0.0.1') - if Gitlab.rails5? - ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new) - else - ActionController::TestRequest.new(remote_ip: remote_ip) - end + ActionController::TestRequest.new({ remote_ip: remote_ip }, ActionController::TestSession.new) end end diff --git a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb index 1478c6b5a47..62ae95df8c0 100644 --- a/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb +++ b/spec/support/services/migrate_to_ghost_user_service_shared_examples.rb @@ -86,7 +86,7 @@ shared_examples "migrating a deleted user's associated records to the ghost user end it "blocks the user before #{record_class_name} migration begins" do - expect(service).to receive("migrate_#{record_class_name.parameterize('_').pluralize}".to_sym) do + expect(service).to receive("migrate_#{record_class_name.parameterize(separator: '_').pluralize}".to_sym) do expect(user.reload).to be_blocked end diff --git a/spec/support/setup_builds_storage.rb b/spec/support/setup_builds_storage.rb index 2e7c88bfc09..1d2a4856724 100644 --- a/spec/support/setup_builds_storage.rb +++ b/spec/support/setup_builds_storage.rb @@ -11,7 +11,7 @@ RSpec.configure do |config| FileUtils.mkdir_p(builds_path) end - config.before(:each) do + config.before do FileUtils.rm_rf(builds_path) FileUtils.mkdir_p(builds_path) end diff --git a/spec/support/shared_contexts/change_access_checks_shared_context.rb b/spec/support/shared_contexts/change_access_checks_shared_context.rb new file mode 100644 index 00000000000..aca18b0c73b --- /dev/null +++ b/spec/support/shared_contexts/change_access_checks_shared_context.rb @@ -0,0 +1,29 @@ +# frozen_string_literal: true + +shared_context 'change access checks context' do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:user_access) { Gitlab::UserAccess.new(user, project: project) } + let(:oldrev) { 'be93687618e4b132087f430a4d8fc3a609c9b77c' } + let(:newrev) { '54fcc214b94e78d7a41a9a8fe6d87a5e59500e51' } + let(:ref) { 'refs/heads/master' } + let(:changes) { { oldrev: oldrev, newrev: newrev, ref: ref } } + let(:protocol) { 'ssh' } + let(:timeout) { Gitlab::GitAccess::INTERNAL_TIMEOUT } + let(:logger) { Gitlab::Checks::TimedLogger.new(timeout: timeout) } + let(:change_access) do + Gitlab::Checks::ChangeAccess.new( + changes, + project: project, + user_access: user_access, + protocol: protocol, + logger: logger + ) + end + + subject { described_class.new(change_access) } + + before do + project.add_developer(user) + end +end diff --git a/spec/support/shared_contexts/url_shared_context.rb b/spec/support/shared_contexts/url_shared_context.rb new file mode 100644 index 00000000000..1b1f67daac3 --- /dev/null +++ b/spec/support/shared_contexts/url_shared_context.rb @@ -0,0 +1,17 @@ +shared_context 'invalid urls' do + let(:urls_with_CRLF) do + ["http://127.0.0.1:333/pa\rth", + "http://127.0.0.1:333/pa\nth", + "http://127.0a.0.1:333/pa\r\nth", + "http://127.0.0.1:333/path?param=foo\r\nbar", + "http://127.0.0.1:333/path?param=foo\rbar", + "http://127.0.0.1:333/path?param=foo\nbar", + "http://127.0.0.1:333/pa%0dth", + "http://127.0.0.1:333/pa%0ath", + "http://127.0a.0.1:333/pa%0d%0th", + "http://127.0.0.1:333/pa%0D%0Ath", + "http://127.0.0.1:333/path?param=foo%0Abar", + "http://127.0.0.1:333/path?param=foo%0Dbar", + "http://127.0.0.1:333/path?param=foo%0D%0Abar"] + end +end diff --git a/spec/support/shared_examples/ci_trace_shared_examples.rb b/spec/support/shared_examples/ci_trace_shared_examples.rb index 377bd82b67e..c603421d748 100644 --- a/spec/support/shared_examples/ci_trace_shared_examples.rb +++ b/spec/support/shared_examples/ci_trace_shared_examples.rb @@ -180,10 +180,9 @@ shared_examples_for 'common trace features' do end context 'runners token' do - let(:token) { 'my_secret_token' } + let(:token) { build.project.runners_token } before do - build.project.update(runners_token: token) trace.set(token) end @@ -193,10 +192,9 @@ shared_examples_for 'common trace features' do end context 'hides build token' do - let(:token) { 'my_secret_token' } + let(:token) { build.token } before do - build.update(token: token) trace.set(token) end diff --git a/spec/support/shared_examples/common_system_notes_examples.rb b/spec/support/shared_examples/common_system_notes_examples.rb index 96ef30b7513..da5a4f3e319 100644 --- a/spec/support/shared_examples/common_system_notes_examples.rb +++ b/spec/support/shared_examples/common_system_notes_examples.rb @@ -1,5 +1,5 @@ shared_examples 'system note creation' do |update_params, note_text| - subject { described_class.new(project, user).execute(issuable, [])} + subject { described_class.new(project, user).execute(issuable, old_labels: []) } before do issuable.assign_attributes(update_params) @@ -16,7 +16,7 @@ shared_examples 'system note creation' do |update_params, note_text| end shared_examples 'WIP notes creation' do |wip_action| - subject { described_class.new(project, user).execute(issuable, []) } + subject { described_class.new(project, user).execute(issuable, old_labels: []) } it 'creates WIP toggle and title change notes' do expect { subject }.to change { Note.count }.from(0).to(2) diff --git a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb index 95e69328080..dbdca99b5aa 100644 --- a/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb +++ b/spec/support/shared_examples/controllers/issuable_notes_filter_shared_examples.rb @@ -2,7 +2,7 @@ shared_examples 'issuable notes filter' do it 'sets discussion filter' do notes_filter = UserPreference::NOTES_FILTERS[:only_comments] - get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter } expect(user.reload.notes_filter_for(issuable)).to eq(notes_filter) expect(UserPreference.count).to eq(1) @@ -13,7 +13,7 @@ shared_examples 'issuable notes filter' do expect_any_instance_of(issuable.class).to receive(:expire_note_etag_cache) - get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter } end it 'does not expires notes e-tag cache for issuable if filter did not change' do @@ -22,14 +22,14 @@ shared_examples 'issuable notes filter' do expect_any_instance_of(issuable.class).not_to receive(:expire_note_etag_cache) - get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter } end it 'does not set notes filter when database is in read only mode' do allow(Gitlab::Database).to receive(:read_only?).and_return(true) notes_filter = UserPreference::NOTES_FILTERS[:only_comments] - get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issuable.iid, notes_filter: notes_filter } expect(user.reload.notes_filter_for(issuable)).to eq(0) end @@ -37,7 +37,7 @@ shared_examples 'issuable notes filter' do it 'returns only user comments' do user.set_notes_filter(UserPreference::NOTES_FILTERS[:only_comments], issuable) - get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issuable.iid } discussions = JSON.parse(response.body) expect(discussions.count).to eq(1) @@ -47,7 +47,7 @@ shared_examples 'issuable notes filter' do it 'returns only activity notes' do user.set_notes_filter(UserPreference::NOTES_FILTERS[:only_activity], issuable) - get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issuable.iid } discussions = JSON.parse(response.body) expect(discussions.count).to eq(1) @@ -60,7 +60,7 @@ shared_examples 'issuable notes filter' do expect(ResourceEvents::MergeIntoNotesService).not_to receive(:new) - get :discussions, namespace_id: project.namespace, project_id: project, id: issuable.iid + get :discussions, params: { namespace_id: project.namespace, project_id: project, id: issuable.iid } end end end diff --git a/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb b/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb new file mode 100644 index 00000000000..d86838719d4 --- /dev/null +++ b/spec/support/shared_examples/controllers/set_sort_order_from_user_preference_shared_examples.rb @@ -0,0 +1,32 @@ +shared_examples 'set sort order from user preference' do + describe '#set_sort_order_from_user_preference' do + # There is no issuable_sorting_field defined in any CE controllers yet, + # however any other field present in user_preferences table can be used for testing. + let(:sorting_field) { :issue_notes_filter } + let(:sorting_param) { 'any' } + + before do + allow(controller).to receive(:issuable_sorting_field).and_return(sorting_field) + end + + context 'when database is in read-only mode' do + it 'it does not update user preference' do + allow(Gitlab::Database).to receive(:read_only?).and_return(true) + + expect_any_instance_of(UserPreference).not_to receive(:update_attribute).with(sorting_field, sorting_param) + + get :index, params: { namespace_id: project.namespace, project_id: project, sort: sorting_param } + end + end + + context 'when database is not in read-only mode' do + it 'updates user preference' do + allow(Gitlab::Database).to receive(:read_only?).and_return(false) + + expect_any_instance_of(UserPreference).to receive(:update_attribute).with(sorting_field, sorting_param) + + get :index, params: { namespace_id: project.namespace, project_id: project, sort: sorting_param } + end + end + end +end diff --git a/spec/support/shared_examples/diff_file_collections.rb b/spec/support/shared_examples/diff_file_collections.rb index 55ce160add0..367ddf06c28 100644 --- a/spec/support/shared_examples/diff_file_collections.rb +++ b/spec/support/shared_examples/diff_file_collections.rb @@ -45,3 +45,19 @@ shared_examples 'diff statistics' do |test_include_stats_flag: true| end end end + +shared_examples 'unfoldable diff' do + let(:subject) { described_class.new(diffable, diff_options: nil) } + + it 'calls Gitlab::Diff::File#unfold_diff_lines with correct position' do + position = instance_double(Gitlab::Diff::Position, file_path: 'README') + readme_file = instance_double(Gitlab::Diff::File, file_path: 'README') + other_file = instance_double(Gitlab::Diff::File, file_path: 'foo.rb') + nil_path_file = instance_double(Gitlab::Diff::File, file_path: nil) + + allow(subject).to receive(:diff_files) { [readme_file, other_file, nil_path_file] } + expect(readme_file).to receive(:unfold_diff_lines).with(position) + + subject.unfold_diff_files([position]) + end +end diff --git a/spec/support/shared_examples/file_finder.rb b/spec/support/shared_examples/file_finder.rb index ef144bdf61c..0dc351b5149 100644 --- a/spec/support/shared_examples/file_finder.rb +++ b/spec/support/shared_examples/file_finder.rb @@ -3,18 +3,19 @@ shared_examples 'file finder' do let(:search_results) { subject.find(query) } it 'finds by name' do - filename, blob = search_results.find { |_, blob| blob.filename == expected_file_by_name } - expect(filename).to eq(expected_file_by_name) - expect(blob).to be_a(Gitlab::SearchResults::FoundBlob) + blob = search_results.find { |blob| blob.filename == expected_file_by_name } + + expect(blob.filename).to eq(expected_file_by_name) + expect(blob).to be_a(Gitlab::Search::FoundBlob) expect(blob.ref).to eq(subject.ref) expect(blob.data).not_to be_empty end it 'finds by content' do - filename, blob = search_results.find { |_, blob| blob.filename == expected_file_by_content } + blob = search_results.find { |blob| blob.filename == expected_file_by_content } - expect(filename).to eq(expected_file_by_content) - expect(blob).to be_a(Gitlab::SearchResults::FoundBlob) + expect(blob.filename).to eq(expected_file_by_content) + expect(blob).to be_a(Gitlab::Search::FoundBlob) expect(blob.ref).to eq(subject.ref) expect(blob.data).not_to be_empty end diff --git a/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb b/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb index f4bc6f8efa5..90d67fd00fc 100644 --- a/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb +++ b/spec/support/shared_examples/issuables_list_metadata_shared_examples.rb @@ -3,9 +3,9 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| def get_action(action, project) if action - get action, author_id: project.creator.id + get action, params: { author_id: project.creator.id } else - get :index, namespace_id: project.namespace, project_id: project + get :index, params: { namespace_id: project.namespace, project_id: project } end end @@ -51,9 +51,9 @@ shared_examples 'issuables list meta-data' do |issuable_type, action = nil| it "doesn't execute any queries with false conditions" do get_empty = if action - proc { get action, author_id: project.creator.id } + proc { get action, params: { author_id: project.creator.id } } else - proc { get :index, namespace_id: project2.namespace, project_id: project2 } + proc { get :index, params: { namespace_id: project2.namespace, project_id: project2 } } end expect(&get_empty).not_to make_queries_matching(/WHERE (?:1=0|0=1)/) diff --git a/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb new file mode 100644 index 00000000000..1f688c0f9d3 --- /dev/null +++ b/spec/support/shared_examples/lib/gitlab/background_migration/backfill_project_repositories_examples.rb @@ -0,0 +1,44 @@ +shared_examples 'backfill migration for project repositories' do |storage| + describe '#perform' do + let(:storage_versions) { storage == :legacy ? [nil, 0] : [1, 2] } + let(:storage_version) { storage_versions.first } + let(:namespaces) { table(:namespaces) } + let(:project_repositories) { table(:project_repositories) } + let(:projects) { table(:projects) } + let(:shards) { table(:shards) } + let(:group) { namespaces.create!(name: 'foo', path: 'foo') } + let(:shard) { shards.create!(name: 'default') } + + it "creates a project_repository row for projects on #{storage} storage that needs one" do + storage_versions.each_with_index do |storage_version, index| + projects.create!(name: "foo-#{index}", path: "foo-#{index}", namespace_id: group.id, storage_version: storage_version) + end + + expect { described_class.new.perform(1, projects.last.id) }.to change(project_repositories, :count).by(2) + end + + it "does nothing for projects on #{storage} storage that have already a project_repository row" do + projects.create!(id: 1, name: 'foo', path: 'foo', namespace_id: group.id, storage_version: storage_version) + project_repositories.create!(project_id: 1, disk_path: 'phony/foo/bar', shard_id: shard.id) + + expect { described_class.new.perform(1, projects.last.id) }.not_to change(project_repositories, :count) + end + + it "does nothing for projects on #{storage == :legacy ? 'hashed' : 'legacy'} storage" do + projects.create!(name: 'foo', path: 'foo', namespace_id: group.id, storage_version: storage == :legacy ? 1 : nil) + + expect { described_class.new.perform(1, projects.last.id) }.not_to change(project_repositories, :count) + end + + it 'inserts rows in a single query' do + projects.create!(name: 'foo', path: 'foo', namespace_id: group.id, storage_version: storage_version, repository_storage: shard.name) + + control_count = ActiveRecord::QueryRecorder.new { described_class.new.perform(1, projects.last.id) } + + projects.create!(name: 'bar', path: 'bar', namespace_id: group.id, storage_version: storage_version, repository_storage: shard.name) + projects.create!(name: 'zoo', path: 'zoo', namespace_id: group.id, storage_version: storage_version, repository_storage: shard.name) + + expect { described_class.new.perform(1, projects.last.id) }.not_to exceed_query_limit(control_count) + end + end +end diff --git a/spec/support/shared_examples/models/member_shared_examples.rb b/spec/support/shared_examples/models/member_shared_examples.rb new file mode 100644 index 00000000000..77376496854 --- /dev/null +++ b/spec/support/shared_examples/models/member_shared_examples.rb @@ -0,0 +1,77 @@ +# frozen_string_literal: true + +shared_examples_for 'inherited access level as a member of entity' do + let(:parent_entity) { create(:group) } + let(:user) { create(:user) } + let(:member) { entity.is_a?(Group) ? entity.group_member(user) : entity.project_member(user) } + + context 'with root parent_entity developer member' do + before do + parent_entity.add_developer(user) + end + + it 'is allowed to be a maintainer of the entity' do + entity.add_maintainer(user) + + expect(member.access_level).to eq(Gitlab::Access::MAINTAINER) + end + + it 'is not allowed to be a reporter of the entity' do + entity.add_reporter(user) + + expect(member).to be_nil + end + + it 'is allowed to change to be a developer of the entity' do + entity.add_maintainer(user) + + expect { member.update(access_level: Gitlab::Access::DEVELOPER) } + .to change { member.access_level }.to(Gitlab::Access::DEVELOPER) + end + + it 'is not allowed to change to be a guest of the entity' do + entity.add_maintainer(user) + + expect { member.update(access_level: Gitlab::Access::GUEST) } + .not_to change { member.reload.access_level } + end + + it "shows an error if the member can't be updated" do + entity.add_maintainer(user) + + member.update(access_level: Gitlab::Access::REPORTER) + + expect(member.errors.full_messages).to eq(["Access level should be higher than Developer inherited membership from group #{parent_entity.name}"]) + end + + it 'allows changing the level from a non existing member' do + non_member_user = create(:user) + + entity.add_maintainer(non_member_user) + + non_member = entity.is_a?(Group) ? entity.group_member(non_member_user) : entity.project_member(non_member_user) + + expect { non_member.update(access_level: Gitlab::Access::GUEST) } + .to change { non_member.reload.access_level } + end + end +end + +shared_examples_for '#valid_level_roles' do |entity_name| + let(:member_user) { create(:user) } + let(:group) { create(:group) } + let(:entity) { create(entity_name) } + let(:entity_member) { create("#{entity_name}_member", :developer, source: entity, user: member_user) } + let(:presenter) { described_class.new(entity_member, current_user: member_user) } + let(:expected_roles) { { 'Developer' => 30, 'Maintainer' => 40, 'Reporter' => 20 } } + + it 'returns all roles when no parent member is present' do + expect(presenter.valid_level_roles).to eq(entity_member.class.access_level_roles) + end + + it 'returns higher roles when a parent member is present' do + group.add_reporter(member_user) + + expect(presenter.valid_level_roles).to eq(expected_roles) + end +end diff --git a/spec/support/shared_examples/models/with_uploads_shared_examples.rb b/spec/support/shared_examples/models/with_uploads_shared_examples.rb index 47ad0c6345d..1d11b855459 100644 --- a/spec/support/shared_examples/models/with_uploads_shared_examples.rb +++ b/spec/support/shared_examples/models/with_uploads_shared_examples.rb @@ -1,6 +1,6 @@ require 'spec_helper' -shared_examples_for 'model with mounted uploader' do |supports_fileuploads| +shared_examples_for 'model with uploads' do |supports_fileuploads| describe '.destroy' do before do stub_uploads_object_storage(uploader_class) @@ -8,16 +8,62 @@ shared_examples_for 'model with mounted uploader' do |supports_fileuploads| model_object.public_send(upload_attribute).migrate!(ObjectStorage::Store::REMOTE) end - it 'deletes remote uploads' do - expect_any_instance_of(CarrierWave::Storage::Fog::File).to receive(:delete).and_call_original + context 'with mounted uploader' do + it 'deletes remote uploads' do + expect_any_instance_of(CarrierWave::Storage::Fog::File).to receive(:delete).and_call_original - expect { model_object.destroy }.to change { Upload.count }.by(-1) + expect { model_object.destroy }.to change { Upload.count }.by(-1) + end end - it 'deletes any FileUploader uploads which are not mounted', skip: !supports_fileuploads do - create(:upload, uploader: FileUploader, model: model_object) + context 'with not mounted uploads', :sidekiq, skip: !supports_fileuploads do + context 'with local files' do + let!(:uploads) { create_list(:upload, 2, uploader: FileUploader, model: model_object) } - expect { model_object.destroy }.to change { Upload.count }.by(-2) + it 'deletes any FileUploader uploads which are not mounted' do + expect { model_object.destroy }.to change { Upload.count }.by(-3) + end + + it 'deletes local files' do + expect_any_instance_of(Uploads::Local).to receive(:delete_keys).with(uploads.map(&:absolute_path)) + + model_object.destroy + end + end + + context 'with remote files' do + let!(:uploads) { create_list(:upload, 2, :object_storage, uploader: FileUploader, model: model_object) } + + it 'deletes any FileUploader uploads which are not mounted' do + expect { model_object.destroy }.to change { Upload.count }.by(-3) + end + + it 'deletes remote files' do + expect_any_instance_of(Uploads::Fog).to receive(:delete_keys).with(uploads.map(&:path)) + + model_object.destroy + end + end + + describe 'destroy strategy depending on feature flag' do + let!(:upload) { create(:upload, uploader: FileUploader, model: model_object) } + + it 'does not destroy uploads by default' do + expect(model_object).to receive(:delete_uploads) + expect(model_object).not_to receive(:destroy_uploads) + + model_object.destroy + end + + it 'uses before destroy callback if feature flag is disabled' do + stub_feature_flags(fast_destroy_uploads: false) + + expect(model_object).to receive(:destroy_uploads) + expect(model_object).not_to receive(:delete_uploads) + + model_object.destroy + end + end end end end diff --git a/spec/support/shared_examples/notify_shared_examples.rb b/spec/support/shared_examples/notify_shared_examples.rb index 66536e80db2..a38354060cf 100644 --- a/spec/support/shared_examples/notify_shared_examples.rb +++ b/spec/support/shared_examples/notify_shared_examples.rb @@ -1,5 +1,5 @@ shared_context 'gitlab email notification' do - set(:project) { create(:project, :repository) } + set(:project) { create(:project, :repository, name: 'a-known-name') } set(:recipient) { create(:user, email: 'recipient@example.com') } let(:gitlab_sender_display_name) { Gitlab.config.gitlab.email_display_name } @@ -62,9 +62,11 @@ end shared_examples 'an email with X-GitLab headers containing project details' do it 'has X-GitLab-Project headers' do aggregate_failures do + full_path_as_domain = "#{project.name}.#{project.namespace.path}" is_expected.to have_header('X-GitLab-Project', /#{project.name}/) is_expected.to have_header('X-GitLab-Project-Id', /#{project.id}/) is_expected.to have_header('X-GitLab-Project-Path', /#{project.full_path}/) + is_expected.to have_header('List-Id', "#{project.full_path} <#{project.id}.#{full_path_as_domain}.#{Gitlab.config.gitlab.host}>") end end end diff --git a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb index 9fc2fbef449..8a7fcf856a1 100644 --- a/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/custom_attributes_shared_examples.rb @@ -9,7 +9,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| context 'with an unauthorized user' do it 'does not filter by custom attributes' do - get api("/#{attributable_name}", user), custom_attributes: { foo: 'foo', bar: 'bar' } + get api("/#{attributable_name}", user), params: { custom_attributes: { foo: 'foo', bar: 'bar' } } expect(response).to have_gitlab_http_status(200) expect(json_response.size).to be 2 @@ -19,7 +19,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| context 'with an authorized user' do it 'filters by custom attributes' do - get api("/#{attributable_name}", admin), custom_attributes: { foo: 'foo', bar: 'bar' } + get api("/#{attributable_name}", admin), params: { custom_attributes: { foo: 'foo', bar: 'bar' } } expect(response).to have_gitlab_http_status(200) expect(json_response.size).to be 1 @@ -35,7 +35,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| context 'with an unauthorized user' do it 'does not include custom attributes' do - get api("/#{attributable_name}", user), with_custom_attributes: true + get api("/#{attributable_name}", user), params: { with_custom_attributes: true } expect(response).to have_gitlab_http_status(200) expect(json_response.size).to be 2 @@ -54,7 +54,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| end it 'includes custom attributes if requested' do - get api("/#{attributable_name}", admin), with_custom_attributes: true + get api("/#{attributable_name}", admin), params: { with_custom_attributes: true } expect(response).to have_gitlab_http_status(200) expect(json_response.size).to be 2 @@ -75,7 +75,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| describe "GET /#{attributable_name}/:id with custom attributes" do context 'with an unauthorized user' do it 'does not include custom attributes' do - get api("/#{attributable_name}/#{attributable.id}", user), with_custom_attributes: true + get api("/#{attributable_name}/#{attributable.id}", user), params: { with_custom_attributes: true } expect(response).to have_gitlab_http_status(200) expect(json_response).not_to include 'custom_attributes' @@ -91,7 +91,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| end it 'includes custom attributes if requested' do - get api("/#{attributable_name}/#{attributable.id}", admin), with_custom_attributes: true + get api("/#{attributable_name}/#{attributable.id}", admin), params: { with_custom_attributes: true } expect(response).to have_gitlab_http_status(200) expect(json_response['custom_attributes']).to contain_exactly( @@ -141,7 +141,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| describe "PUT /#{attributable_name}/:id/custom_attributes/:key" do context 'with an unauthorized user' do - subject { put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", user), value: 'new' } + subject { put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", user), params: { value: 'new' } } it_behaves_like 'an unauthorized API user' end @@ -149,7 +149,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| context 'with an authorized user' do it 'creates a new custom attribute' do expect do - put api("/#{attributable_name}/#{attributable.id}/custom_attributes/new", admin), value: 'new' + put api("/#{attributable_name}/#{attributable.id}/custom_attributes/new", admin), params: { value: 'new' } end.to change { attributable.custom_attributes.count }.by(1) expect(response).to have_gitlab_http_status(200) @@ -159,7 +159,7 @@ shared_examples 'custom attributes endpoints' do |attributable_name| it 'updates an existing custom attribute' do expect do - put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin), value: 'new' + put api("/#{attributable_name}/#{attributable.id}/custom_attributes/foo", admin), params: { value: 'new' } end.not_to change { attributable.custom_attributes.count } expect(response).to have_gitlab_http_status(200) diff --git a/spec/support/shared_examples/requests/api/status_shared_examples.rb b/spec/support/shared_examples/requests/api/status_shared_examples.rb index 0ed917e448a..ebfc5fed3bb 100644 --- a/spec/support/shared_examples/requests/api/status_shared_examples.rb +++ b/spec/support/shared_examples/requests/api/status_shared_examples.rb @@ -54,7 +54,7 @@ shared_examples_for '412 response' do context 'for a modified ressource' do before do - delete request, params, { 'HTTP_IF_UNMODIFIED_SINCE' => '1990-01-12T00:00:48-0600' } + delete request, params: params, headers: { 'HTTP_IF_UNMODIFIED_SINCE' => '1990-01-12T00:00:48-0600' } end it 'returns 412' do @@ -64,7 +64,7 @@ shared_examples_for '412 response' do context 'for an unmodified ressource' do before do - delete request, params, { 'HTTP_IF_UNMODIFIED_SINCE' => Time.now } + delete request, params: params, headers: { 'HTTP_IF_UNMODIFIED_SINCE' => Time.now } end it 'returns accepted' do diff --git a/spec/support/shared_examples/serializers/diff_file_entity_examples.rb b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb new file mode 100644 index 00000000000..1770308f789 --- /dev/null +++ b/spec/support/shared_examples/serializers/diff_file_entity_examples.rb @@ -0,0 +1,46 @@ +# frozen_string_literal: true + +shared_examples 'diff file base entity' do + it 'exposes essential attributes' do + expect(subject).to include(:content_sha, :submodule, :submodule_link, + :submodule_tree_url, :old_path_html, + :new_path_html, :blob, :can_modify_blob, + :file_hash, :file_path, :old_path, :new_path, + :collapsed, :text, :diff_refs, :stored_externally, + :external_storage, :renamed_file, :deleted_file, + :mode_changed, :a_mode, :b_mode, :new_file) + end + + # Converted diff files from GitHub import does not contain blob file + # and content sha. + context 'when diff file does not have a blob and content sha' do + it 'exposes some attributes as nil' do + allow(diff_file).to receive(:content_sha).and_return(nil) + allow(diff_file).to receive(:blob).and_return(nil) + + expect(subject[:context_lines_path]).to be_nil + expect(subject[:view_path]).to be_nil + expect(subject[:highlighted_diff_lines]).to be_nil + expect(subject[:can_modify_blob]).to be_nil + end + end +end + +shared_examples 'diff file entity' do + it_behaves_like 'diff file base entity' + + it 'exposes correct attributes' do + expect(subject).to include(:too_large, :added_lines, :removed_lines, + :context_lines_path, :highlighted_diff_lines, + :parallel_diff_lines, :empty) + end + + it 'includes viewer' do + expect(subject[:viewer].with_indifferent_access) + .to match_schema('entities/diff_viewer') + end +end + +shared_examples 'diff file discussion entity' do + it_behaves_like 'diff file base entity' +end diff --git a/spec/support/shared_examples/update_invalid_issuable.rb b/spec/support/shared_examples/update_invalid_issuable.rb index 1490287681b..64568de424e 100644 --- a/spec/support/shared_examples/update_invalid_issuable.rb +++ b/spec/support/shared_examples/update_invalid_issuable.rb @@ -26,7 +26,7 @@ shared_examples 'update invalid issuable' do |klass| end it 'renders edit when format is html' do - put :update, params + put :update, params: params expect(response).to render_template(:edit) expect(assigns[:conflict]).to be_truthy @@ -35,7 +35,7 @@ shared_examples 'update invalid issuable' do |klass| it 'renders json error message when format is json' do params[:format] = "json" - put :update, params + put :update, params: params expect(response.status).to eq(409) expect(JSON.parse(response.body)).to have_key('errors') @@ -49,7 +49,7 @@ shared_examples 'update invalid issuable' do |klass| end it 'renders edit when merge request is invalid' do - put :update, params + put :update, params: params expect(response).to render_template(:edit) end |