From 6145ddf515f93fcc7ed873b911b6369cc2bc0462 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?R=C3=A9my=20Coutable?= Date: Wed, 22 May 2019 14:16:49 +0200 Subject: Revert "Merge branch 'revert-04c3c6dd' into 'master'" This reverts commit 744f1f2e7037f5c70c3168d9e2e89b1c327465d2, reversing changes made to c4d930e5f54e7da07c80cc028dfc0f5c08719146. --- .gitlab-ci.yml | 2 - .gitlab/ci/rails.gitlab-ci.yml | 58 ++++- .gitlab/ci/test-metadata.gitlab-ci.yml | 6 +- Gemfile | 2 +- Gemfile.lock | 10 +- doc/development/rake_tasks.md | 13 +- doc/development/testing_guide/testing_levels.md | 12 +- lib/quality/test_level.rb | 75 +++++++ lib/tasks/spec.rake | 44 ++-- scripts/prepare_build.sh | 1 + spec/lib/quality/test_level_spec.rb | 105 +++++++++ spec/spec_helper.rb | 5 +- .../finders/assignees_filter_shared_examples.rb | 49 ++++ .../finders/assignees_filter_spec.rb | 49 ---- .../models/atomic_internal_id_shared_examples.rb | 113 ++++++++++ .../models/atomic_internal_id_spec.rb | 113 ---------- .../models/chat_service_shared_examples.rb | 248 +++++++++++++++++++++ .../shared_examples/models/chat_service_spec.rb | 248 --------------------- .../update_project_statistics_shared_examples.rb | 76 +++++++ .../models/update_project_statistics_spec.rb | 76 ------- .../requests/api/issues_shared_example_spec.rb | 44 ---- .../requests/api/issues_shared_examples.rb | 44 ++++ 22 files changed, 816 insertions(+), 577 deletions(-) create mode 100644 lib/quality/test_level.rb create mode 100644 spec/lib/quality/test_level_spec.rb create mode 100644 spec/support/shared_examples/finders/assignees_filter_shared_examples.rb delete mode 100644 spec/support/shared_examples/finders/assignees_filter_spec.rb create mode 100644 spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb delete mode 100644 spec/support/shared_examples/models/atomic_internal_id_spec.rb create mode 100644 spec/support/shared_examples/models/chat_service_shared_examples.rb delete mode 100644 spec/support/shared_examples/models/chat_service_spec.rb create mode 100644 spec/support/shared_examples/models/update_project_statistics_shared_examples.rb delete mode 100644 spec/support/shared_examples/models/update_project_statistics_spec.rb delete mode 100644 spec/support/shared_examples/requests/api/issues_shared_example_spec.rb create mode 100644 spec/support/shared_examples/requests/api/issues_shared_examples.rb diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c971df3ba5f..cc71ae9245a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -13,10 +13,8 @@ variables: BUILD_ASSETS_IMAGE: "false" before_script: - - bundle --version - date - source scripts/utils.sh - - date - source scripts/prepare_build.sh - date diff --git a/.gitlab/ci/rails.gitlab-ci.yml b/.gitlab/ci/rails.gitlab-ci.yml index 29534e40a14..346fa47cbb5 100644 --- a/.gitlab/ci/rails.gitlab-ci.yml +++ b/.gitlab/ci/rails.gitlab-ci.yml @@ -6,8 +6,9 @@ .use-pg-10: &use-pg-10 services: - - postgres:10.7 - - redis:alpine + - name: postgres:10.7 + command: ["postgres", "-c", "fsync=off", "-c", "synchronous_commit=off", "-c", "full_page_writes=off"] + - name: redis:alpine .use-mysql: &use-mysql services: @@ -52,8 +53,10 @@ script: - JOB_NAME=( $CI_JOB_NAME ) - TEST_TOOL=${JOB_NAME[0]} - - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - - export KNAPSACK_GENERATE_REPORT=true + - TEST_LEVEL=${JOB_NAME[1]} + - DATABASE=${JOB_NAME[2]} + - export KNAPSACK_REPORT_PATH=knapsack/${CI_PROJECT_NAME}/${TEST_TOOL}_${TEST_LEVEL}_${DATABASE}_node_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json + - export KNAPSACK_GENERATE_REPORT=true KNAPSACK_LOG_LEVEL=debug KNAPSACK_TEST_DIR=spec - export SUITE_FLAKY_RSPEC_REPORT_PATH=${FLAKY_RSPEC_SUITE_REPORT_PATH} - export FLAKY_RSPEC_REPORT_PATH=rspec_flaky/all_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json - export NEW_FLAKY_RSPEC_REPORT_PATH=rspec_flaky/new_${TEST_TOOL}_${CI_NODE_INDEX}_${CI_NODE_TOTAL}_report.json @@ -63,7 +66,10 @@ - '[[ -f $FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${FLAKY_RSPEC_REPORT_PATH}' - '[[ -f $NEW_FLAKY_RSPEC_REPORT_PATH ]] || echo "{}" > ${NEW_FLAKY_RSPEC_REPORT_PATH}' - scripts/gitaly-test-spawn - - knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml" + - date + - 'export KNAPSACK_TEST_FILE_PATTERN=$(ruby -r./lib/quality/test_level.rb -e "puts Quality::TestLevel.new.pattern(:${TEST_LEVEL})")' + - knapsack rspec "--color --format documentation --format RspecJunitFormatter --out junit_rspec.xml --tag level:${TEST_LEVEL} --tag ~geo" + - date artifacts: expire_in: 31d when: always @@ -140,19 +146,47 @@ setup-test-env: except: - /(^docs[\/-].*|.*-docs$)/ -rspec-pg: +rspec unit pg: <<: *rspec-metadata-pg - parallel: 50 + parallel: 20 -rspec-pg-10: +rspec integration pg: + <<: *rspec-metadata-pg + parallel: 6 + +rspec system pg: + <<: *rspec-metadata-pg + parallel: 24 + +rspec unit pg-10: <<: *rspec-metadata-pg-10 <<: *only-schedules-master - parallel: 50 + parallel: 20 + +rspec integration pg-10: + <<: *rspec-metadata-pg-10 + <<: *only-schedules-master + parallel: 6 + +rspec system pg-10: + <<: *rspec-metadata-pg-10 + <<: *only-schedules-master + parallel: 24 + +rspec unit mysql: + <<: *rspec-metadata-mysql + <<: *only-schedules-master + parallel: 20 + +rspec integration mysql: + <<: *rspec-metadata-mysql + <<: *only-schedules-master + parallel: 6 -rspec-mysql: +rspec system mysql: <<: *rspec-metadata-mysql <<: *only-schedules-master - parallel: 50 + parallel: 24 rspec-fast-spec-helper: <<: *rspec-metadata-pg @@ -164,7 +198,7 @@ rspec-fast-spec-helper: script: - export CACHE_CLASSES=true - scripts/gitaly-test-spawn - - bin/rspec --color --format documentation --tag quarantine spec/ + - bin/rspec --color --format documentation --tag quarantine -- spec/ rspec-pg-quarantine: <<: *rspec-metadata-pg diff --git a/.gitlab/ci/test-metadata.gitlab-ci.yml b/.gitlab/ci/test-metadata.gitlab-ci.yml index 4b595083ec6..c51f825f831 100644 --- a/.gitlab/ci/test-metadata.gitlab-ci.yml +++ b/.gitlab/ci/test-metadata.gitlab-ci.yml @@ -40,12 +40,12 @@ update-tests-metadata: policy: push script: - retry gem install fog-aws mime-types activesupport rspec_profiling postgres-copy --no-document - - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec-pg_node_*.json + - scripts/merge-reports ${KNAPSACK_RSPEC_SUITE_REPORT_PATH} knapsack/${CI_PROJECT_NAME}/rspec_*_pg_node_*.json + - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH' + - rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json - scripts/merge-reports ${FLAKY_RSPEC_SUITE_REPORT_PATH} rspec_flaky/all_*_*.json - FLAKY_RSPEC_GENERATE_REPORT=1 scripts/prune-old-flaky-specs ${FLAKY_RSPEC_SUITE_REPORT_PATH} - - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $KNAPSACK_RSPEC_SUITE_REPORT_PATH' - '[[ -z ${TESTS_METADATA_S3_BUCKET} ]] || scripts/sync-reports put $TESTS_METADATA_S3_BUCKET $FLAKY_RSPEC_SUITE_REPORT_PATH' - - rm -f knapsack/${CI_PROJECT_NAME}/*_node_*.json - rm -f rspec_flaky/all_*.json rspec_flaky/new_*.json - scripts/insert-rspec-profiling-data only: diff --git a/Gemfile b/Gemfile index e2842ca27db..df2d2616ad3 100644 --- a/Gemfile +++ b/Gemfile @@ -361,7 +361,7 @@ group :development, :test do gem 'scss_lint', '~> 0.56.0', require: false gem 'haml_lint', '~> 0.31.0', require: false - gem 'simplecov', '~> 0.14.0', require: false + gem 'simplecov', '~> 0.16.1', require: false gem 'bundler-audit', '~> 0.5.0', require: false gem 'benchmark-ips', '~> 2.3.0', require: false diff --git a/Gemfile.lock b/Gemfile.lock index a63b7cc84aa..064413168f9 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -174,7 +174,7 @@ GEM diffy (3.1.0) discordrb-webhooks-blackst0ne (3.3.0) rest-client (~> 2.0) - docile (1.1.5) + docile (1.3.1) domain_name (0.5.20180417) unf (>= 0.0.5, < 1.0.0) doorkeeper (4.3.2) @@ -873,11 +873,11 @@ GEM jwt (>= 1.5, < 3.0) multi_json (~> 1.10) simple_po_parser (1.1.2) - simplecov (0.14.1) - docile (~> 1.1.0) + simplecov (0.16.1) + docile (~> 1.1) json (>= 1.8, < 3) simplecov-html (~> 0.10.0) - simplecov-html (0.10.0) + simplecov-html (0.10.2) slack-notifier (1.5.1) spring (2.0.2) activesupport (>= 4.2) @@ -1203,7 +1203,7 @@ DEPENDENCIES sidekiq (~> 5.2.7) sidekiq-cron (~> 1.0) simple_po_parser (~> 1.1.2) - simplecov (~> 0.14.0) + simplecov (~> 0.16.1) slack-notifier (~> 1.5.1) spring (~> 2.0.0) spring-commands-rspec (~> 1.0.4) diff --git a/doc/development/rake_tasks.md b/doc/development/rake_tasks.md index 27fc3231218..f06ffab03c6 100644 --- a/doc/development/rake_tasks.md +++ b/doc/development/rake_tasks.md @@ -108,11 +108,13 @@ To make sure that indices still fit. You could find great details in: In order to run the test you can use the following commands: -- `rake spec` to run the rspec suite -- `rake karma` to run the karma test suite -- `rake gitlab:test` to run all the tests +- `bin/rake spec` to run the rspec suite +- `bin/rake spec:unit` to run the only the unit tests +- `bin/rake spec:integration` to run the only the integration tests +- `bin/rake spec:system` to run the only the system tests +- `bin/rake karma` to run the karma test suite -Note: `rake spec` takes significant time to pass. +Note: `bin/rake spec` takes significant time to pass. Instead of running full test suite locally you can save a lot of time by running a single test or directory related to your changes. After you submit merge request CI will run full test suite for you. Green CI status in the merge request means @@ -121,6 +123,9 @@ full test suite is passed. Note: You can't run `rspec .` since this will try to run all the `_spec.rb` files it can find, also the ones in `/tmp` +Note: You can pass RSpec command line options to the `spec:unit`, +`spec:integration`, and `spec:system` tasks, e.g. `bin/rake "spec:unit[--tag ~geo --dry-run]"`. + To run a single test file you can use: - `bin/rspec spec/controllers/commit_controller_spec.rb` for a rspec test diff --git a/doc/development/testing_guide/testing_levels.md b/doc/development/testing_guide/testing_levels.md index 1fa6e38ea5a..b5155b6b7fa 100644 --- a/doc/development/testing_guide/testing_levels.md +++ b/doc/development/testing_guide/testing_levels.md @@ -4,12 +4,14 @@ _This diagram demonstrates the relative priority of each test type we use. `e2e` stands for end-to-end._ -As of 2019-04-16, we have the following distribution of tests per level: +As of 2019-05-01, we have the following distribution of tests per level: -- 67 black-box tests at the system level (aka end-to-end or QA tests) in CE, 98 in EE. This represents 0.3% of all the CE tests (0.3% in EE). -- 5,457 white-box tests at the system level (aka system or feature tests) in CE, 6,585 in EE. This represents 24.6% of all the CE tests (20.3% in EE). -- 8,298 integration tests in CE, 10,633 in EE: 0.3% of all the CE tests (0.3% in EE). This represents 37.2% of all the CE tests (32.8% in EE). -- 8,403 unit tests in CE, 15,090 in EE: 0.3% of all the CE tests (0.3% in EE). This represents 37.8% of all the CE tests (46.6% in EE). +| Test level | Community Edition | Enterprise Edition | Community + Enterprise Edition | +| --------- | ---------- | -------------- | ----- | +| Black-box tests at the system level (aka end-to-end or QA tests) | 68 (0.14%) | 31 (0.2%) | 99 (0.17%) | +| White-box tests at the system level (aka system or feature tests) | 5,471 (11.9%) | 969 (7.4%) | 6440 (10.9%) | +| Integration tests | 8,333 (18.2%) | 2,244 (17.2%) | 10,577 (17.9%) | +| Unit tests | 32,031 (69.7%) | 9,778 (75.1%) | 41,809 (71%) | ## Unit tests diff --git a/lib/quality/test_level.rb b/lib/quality/test_level.rb new file mode 100644 index 00000000000..24d8eac200c --- /dev/null +++ b/lib/quality/test_level.rb @@ -0,0 +1,75 @@ +# frozen_string_literal: true + +module Quality + class TestLevel + UnknownTestLevelError = Class.new(StandardError) + + TEST_LEVEL_FOLDERS = { + unit: %w[ + bin + config + db + dependencies + factories + finders + frontend + graphql + helpers + initializers + javascripts + lib + migrations + models + policies + presenters + rack_servers + routing + rubocop + serializers + services + sidekiq + tasks + uploaders + validators + views + workers + elastic_integration + ], + integration: %w[ + controllers + mailers + requests + ], + system: ['features'] + }.freeze + + attr_reader :prefix + + def initialize(prefix = nil) + @prefix = prefix + @patterns = {} + @regexps = {} + end + + def pattern(level) + @patterns[level] ||= "#{prefix}spec/{#{TEST_LEVEL_FOLDERS.fetch(level).join(',')}}{,/**/}*_spec.rb".freeze + end + + def regexp(level) + @regexps[level] ||= Regexp.new("#{prefix}spec/(#{TEST_LEVEL_FOLDERS.fetch(level).join('|')})").freeze + end + + def level_for(file_path) + case file_path + when regexp(:unit) + :unit + when regexp(:integration) + :integration + when regexp(:system) + :system + else + raise UnknownTestLevelError, "Test level for #{file_path} couldn't be set. Please rename the file properly or change the test level detection regexes in #{__FILE__}." + end + end + end +end diff --git a/lib/tasks/spec.rake b/lib/tasks/spec.rake index 2eddcb3c777..c881ad4cf12 100644 --- a/lib/tasks/spec.rake +++ b/lib/tasks/spec.rake @@ -1,7 +1,32 @@ +# frozen_string_literal: true + +return if Rails.env.production? + Rake::Task["spec"].clear if Rake::Task.task_defined?('spec') namespace :spec do - desc 'GitLab | Rspec | Run request specs' + desc 'GitLab | RSpec | Run unit tests' + RSpec::Core::RakeTask.new(:unit, :rspec_opts) do |t, args| + require_dependency 'quality/test_level' + t.pattern = Quality::TestLevel.new.pattern(:unit) + t.rspec_opts = args[:rspec_opts] + end + + desc 'GitLab | RSpec | Run integration tests' + RSpec::Core::RakeTask.new(:integration, :rspec_opts) do |t, args| + require_dependency 'quality/test_level' + t.pattern = Quality::TestLevel.new.pattern(:integration) + t.rspec_opts = args[:rspec_opts] + end + + desc 'GitLab | RSpec | Run system tests' + RSpec::Core::RakeTask.new(:system, :rspec_opts) do |t, args| + require_dependency 'quality/test_level' + t.pattern = Quality::TestLevel.new.pattern(:system) + t.rspec_opts = args[:rspec_opts] + end + + desc '[Deprecated] Use the "bin/rspec --tag api" instead' task :api do cmds = [ %w(rake gitlab:setup), @@ -10,7 +35,7 @@ namespace :spec do run_commands(cmds) end - desc 'GitLab | Rspec | Run feature specs' + desc '[Deprecated] Use the "spec:system" task instead' task :feature do cmds = [ %w(rake gitlab:setup), @@ -19,7 +44,7 @@ namespace :spec do run_commands(cmds) end - desc 'GitLab | Rspec | Run model specs' + desc '[Deprecated] Use "bin/rspec spec/models" instead' task :models do cmds = [ %w(rake gitlab:setup), @@ -28,7 +53,7 @@ namespace :spec do run_commands(cmds) end - desc 'GitLab | Rspec | Run service specs' + desc '[Deprecated] Use "bin/rspec spec/services" instead' task :services do cmds = [ %w(rake gitlab:setup), @@ -37,7 +62,7 @@ namespace :spec do run_commands(cmds) end - desc 'GitLab | Rspec | Run lib specs' + desc '[Deprecated] Use "bin/rspec spec/lib" instead' task :lib do cmds = [ %w(rake gitlab:setup), @@ -45,15 +70,6 @@ namespace :spec do ] run_commands(cmds) end - - desc 'GitLab | Rspec | Run other specs' - task :other do - cmds = [ - %w(rake gitlab:setup), - %w(rspec spec --tag ~@api --tag ~@feature --tag ~@models --tag ~@lib --tag ~@services) - ] - run_commands(cmds) - end end desc "GitLab | Run specs" diff --git a/scripts/prepare_build.sh b/scripts/prepare_build.sh index 58b74f2f07d..5fca95f1f40 100644 --- a/scripts/prepare_build.sh +++ b/scripts/prepare_build.sh @@ -5,6 +5,7 @@ export USE_BUNDLE_INSTALL=${USE_BUNDLE_INSTALL:-true} export BUNDLE_INSTALL_FLAGS="--without=production --jobs=$(nproc) --path=vendor --retry=3 --quiet" if [ "$USE_BUNDLE_INSTALL" != "false" ]; then + bundle --version bundle install --clean $BUNDLE_INSTALL_FLAGS && bundle check fi diff --git a/spec/lib/quality/test_level_spec.rb b/spec/lib/quality/test_level_spec.rb new file mode 100644 index 00000000000..3465c3a050b --- /dev/null +++ b/spec/lib/quality/test_level_spec.rb @@ -0,0 +1,105 @@ +# frozen_string_literal: true + +require 'fast_spec_helper' + +RSpec.describe Quality::TestLevel do + describe '#pattern' do + context 'when level is unit' do + it 'returns a pattern' do + expect(subject.pattern(:unit)) + .to eq("spec/{bin,config,db,dependencies,factories,finders,frontend,graphql,helpers,initializers,javascripts,lib,migrations,models,policies,presenters,rack_servers,routing,rubocop,serializers,services,sidekiq,tasks,uploaders,validators,views,workers,elastic_integration}{,/**/}*_spec.rb") + end + end + + context 'when level is integration' do + it 'returns a pattern' do + expect(subject.pattern(:integration)) + .to eq("spec/{controllers,mailers,requests}{,/**/}*_spec.rb") + end + end + + context 'when level is system' do + it 'returns a pattern' do + expect(subject.pattern(:system)) + .to eq("spec/{features}{,/**/}*_spec.rb") + end + end + + context 'with a prefix' do + it 'returns a pattern' do + expect(described_class.new('ee/').pattern(:system)) + .to eq("ee/spec/{features}{,/**/}*_spec.rb") + end + end + + describe 'performance' do + it 'memoizes the pattern for a given level' do + expect(subject.pattern(:system).object_id).to eq(subject.pattern(:system).object_id) + end + + it 'freezes the pattern for a given level' do + expect(subject.pattern(:system)).to be_frozen + end + end + end + + describe '#regexp' do + context 'when level is unit' do + it 'returns a regexp' do + expect(subject.regexp(:unit)) + .to eq(%r{spec/(bin|config|db|dependencies|factories|finders|frontend|graphql|helpers|initializers|javascripts|lib|migrations|models|policies|presenters|rack_servers|routing|rubocop|serializers|services|sidekiq|tasks|uploaders|validators|views|workers|elastic_integration)}) + end + end + + context 'when level is integration' do + it 'returns a regexp' do + expect(subject.regexp(:integration)) + .to eq(%r{spec/(controllers|mailers|requests)}) + end + end + + context 'when level is system' do + it 'returns a regexp' do + expect(subject.regexp(:system)) + .to eq(%r{spec/(features)}) + end + end + + context 'with a prefix' do + it 'returns a regexp' do + expect(described_class.new('ee/').regexp(:system)) + .to eq(%r{ee/spec/(features)}) + end + end + + describe 'performance' do + it 'memoizes the regexp for a given level' do + expect(subject.regexp(:system).object_id).to eq(subject.regexp(:system).object_id) + end + + it 'freezes the regexp for a given level' do + expect(subject.regexp(:system)).to be_frozen + end + end + end + + describe '#level_for' do + it 'returns the correct level for a unit test' do + expect(subject.level_for('spec/models/abuse_report_spec.rb')).to eq(:unit) + end + + it 'returns the correct level for an integration test' do + expect(subject.level_for('spec/mailers/abuse_report_mailer_spec.rb')).to eq(:integration) + end + + it 'returns the correct level for a system test' do + expect(subject.level_for('spec/features/abuse_report_spec.rb')).to eq(:system) + end + + it 'raises an error for an unknown level' do + expect { subject.level_for('spec/unknown/foo_spec.rb') } + .to raise_error(described_class::UnknownTestLevelError, + %r{Test level for spec/unknown/foo_spec.rb couldn't be set. Please rename the file properly or change the test level detection regexes in .+/lib/quality/test_level.rb.}) + end + end +end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 69589c9aa33..390a869d93f 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -44,6 +44,8 @@ Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].each { |f| require f } Dir[Rails.root.join("spec/support/shared_examples/*.rb")].each { |f| require f } Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f } +quality_level = Quality::TestLevel.new + RSpec.configure do |config| config.use_transactional_fixtures = false config.use_instantiated_fixtures = false @@ -55,9 +57,10 @@ RSpec.configure do |config| config.infer_spec_type_from_file_location! config.full_backtrace = !!ENV['CI'] - config.define_derived_metadata(file_path: %r{/spec/}) do |metadata| + config.define_derived_metadata(file_path: %r{(ee)?/spec/.+_spec\.rb\z}) do |metadata| location = metadata[:location] + metadata[:level] = quality_level.level_for(location) metadata[:api] = true if location =~ %r{/spec/requests/api/} # do not overwrite type if it's already set diff --git a/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb new file mode 100644 index 00000000000..782a2d97746 --- /dev/null +++ b/spec/support/shared_examples/finders/assignees_filter_shared_examples.rb @@ -0,0 +1,49 @@ +# frozen_string_literal: true + +shared_examples 'assignee ID filter' do + it 'returns issuables assigned to that user' do + expect(issuables).to contain_exactly(*expected_issuables) + end +end + +shared_examples 'assignee username filter' do + it 'returns issuables assigned to those users' do + expect(issuables).to contain_exactly(*expected_issuables) + end +end + +shared_examples 'no assignee filter' do + let(:params) { { assignee_id: 'None' } } + + it 'returns issuables not assigned to any assignee' do + expect(issuables).to contain_exactly(*expected_issuables) + end + + it 'returns issuables not assigned to any assignee' do + params[:assignee_id] = 0 + + expect(issuables).to contain_exactly(*expected_issuables) + end + + it 'returns issuables not assigned to any assignee' do + params[:assignee_id] = 'none' + + expect(issuables).to contain_exactly(*expected_issuables) + end +end + +shared_examples 'any assignee filter' do + context '' do + let(:params) { { assignee_id: 'Any' } } + + it 'returns issuables assigned to any assignee' do + expect(issuables).to contain_exactly(*expected_issuables) + end + + it 'returns issuables assigned to any assignee' do + params[:assignee_id] = 'any' + + expect(issuables).to contain_exactly(*expected_issuables) + end + end +end diff --git a/spec/support/shared_examples/finders/assignees_filter_spec.rb b/spec/support/shared_examples/finders/assignees_filter_spec.rb deleted file mode 100644 index 782a2d97746..00000000000 --- a/spec/support/shared_examples/finders/assignees_filter_spec.rb +++ /dev/null @@ -1,49 +0,0 @@ -# frozen_string_literal: true - -shared_examples 'assignee ID filter' do - it 'returns issuables assigned to that user' do - expect(issuables).to contain_exactly(*expected_issuables) - end -end - -shared_examples 'assignee username filter' do - it 'returns issuables assigned to those users' do - expect(issuables).to contain_exactly(*expected_issuables) - end -end - -shared_examples 'no assignee filter' do - let(:params) { { assignee_id: 'None' } } - - it 'returns issuables not assigned to any assignee' do - expect(issuables).to contain_exactly(*expected_issuables) - end - - it 'returns issuables not assigned to any assignee' do - params[:assignee_id] = 0 - - expect(issuables).to contain_exactly(*expected_issuables) - end - - it 'returns issuables not assigned to any assignee' do - params[:assignee_id] = 'none' - - expect(issuables).to contain_exactly(*expected_issuables) - end -end - -shared_examples 'any assignee filter' do - context '' do - let(:params) { { assignee_id: 'Any' } } - - it 'returns issuables assigned to any assignee' do - expect(issuables).to contain_exactly(*expected_issuables) - end - - it 'returns issuables assigned to any assignee' do - params[:assignee_id] = 'any' - - expect(issuables).to contain_exactly(*expected_issuables) - end - end -end diff --git a/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb new file mode 100644 index 00000000000..a248f60d23e --- /dev/null +++ b/spec/support/shared_examples/models/atomic_internal_id_shared_examples.rb @@ -0,0 +1,113 @@ +require 'spec_helper' + +shared_examples_for 'AtomicInternalId' do |validate_presence: true| + describe '.has_internal_id' do + describe 'Module inclusion' do + subject { described_class } + + it { is_expected.to include_module(AtomicInternalId) } + end + + describe 'Validation' do + before do + allow_any_instance_of(described_class).to receive(:"ensure_#{scope}_#{internal_id_attribute}!") + + instance.valid? + end + + context 'when presence validation is required' do + before do + skip unless validate_presence + end + + it 'validates presence' do + expect(instance.errors[internal_id_attribute]).to include("can't be blank") + end + end + + context 'when presence validation is not required' do + before do + skip if validate_presence + end + + it 'does not validate presence' do + expect(instance.errors[internal_id_attribute]).to be_empty + end + end + end + + describe 'Creating an instance' do + subject { instance.save! } + + it 'saves a new instance properly' do + expect { subject }.not_to raise_error + end + end + + describe 'internal id generation' do + subject { instance.save! } + + it 'calls InternalId.generate_next and sets internal id attribute' do + iid = rand(1..1000) + + expect(InternalId).to receive(:generate_next).with(instance, scope_attrs, usage, any_args).and_return(iid) + subject + expect(read_internal_id).to eq(iid) + end + + it 'does not overwrite an existing internal id' do + write_internal_id(4711) + + expect { subject }.not_to change { read_internal_id } + end + + context 'when the instance has an internal ID set' do + let(:internal_id) { 9001 } + + it 'calls InternalId.update_last_value and sets the `last_value` to that of the instance' do + write_internal_id(internal_id) + + expect(InternalId) + .to receive(:track_greatest) + .with(instance, scope_attrs, usage, internal_id, any_args) + .and_return(internal_id) + subject + end + end + end + + describe "#reset_scope_internal_id_attribute" do + it 'rewinds the allocated IID' do + expect { ensure_scope_attribute! }.not_to raise_error + expect(read_internal_id).not_to be_nil + + expect(reset_scope_attribute).to be_nil + expect(read_internal_id).to be_nil + end + + it 'allocates the same IID' do + internal_id = ensure_scope_attribute! + reset_scope_attribute + expect(read_internal_id).to be_nil + + expect(ensure_scope_attribute!).to eq(internal_id) + end + end + + def ensure_scope_attribute! + instance.public_send(:"ensure_#{scope}_#{internal_id_attribute}!") + end + + def reset_scope_attribute + instance.public_send(:"reset_#{scope}_#{internal_id_attribute}") + end + + def read_internal_id + instance.public_send(internal_id_attribute) + end + + def write_internal_id(value) + instance.public_send(:"#{internal_id_attribute}=", value) + end + end +end diff --git a/spec/support/shared_examples/models/atomic_internal_id_spec.rb b/spec/support/shared_examples/models/atomic_internal_id_spec.rb deleted file mode 100644 index a248f60d23e..00000000000 --- a/spec/support/shared_examples/models/atomic_internal_id_spec.rb +++ /dev/null @@ -1,113 +0,0 @@ -require 'spec_helper' - -shared_examples_for 'AtomicInternalId' do |validate_presence: true| - describe '.has_internal_id' do - describe 'Module inclusion' do - subject { described_class } - - it { is_expected.to include_module(AtomicInternalId) } - end - - describe 'Validation' do - before do - allow_any_instance_of(described_class).to receive(:"ensure_#{scope}_#{internal_id_attribute}!") - - instance.valid? - end - - context 'when presence validation is required' do - before do - skip unless validate_presence - end - - it 'validates presence' do - expect(instance.errors[internal_id_attribute]).to include("can't be blank") - end - end - - context 'when presence validation is not required' do - before do - skip if validate_presence - end - - it 'does not validate presence' do - expect(instance.errors[internal_id_attribute]).to be_empty - end - end - end - - describe 'Creating an instance' do - subject { instance.save! } - - it 'saves a new instance properly' do - expect { subject }.not_to raise_error - end - end - - describe 'internal id generation' do - subject { instance.save! } - - it 'calls InternalId.generate_next and sets internal id attribute' do - iid = rand(1..1000) - - expect(InternalId).to receive(:generate_next).with(instance, scope_attrs, usage, any_args).and_return(iid) - subject - expect(read_internal_id).to eq(iid) - end - - it 'does not overwrite an existing internal id' do - write_internal_id(4711) - - expect { subject }.not_to change { read_internal_id } - end - - context 'when the instance has an internal ID set' do - let(:internal_id) { 9001 } - - it 'calls InternalId.update_last_value and sets the `last_value` to that of the instance' do - write_internal_id(internal_id) - - expect(InternalId) - .to receive(:track_greatest) - .with(instance, scope_attrs, usage, internal_id, any_args) - .and_return(internal_id) - subject - end - end - end - - describe "#reset_scope_internal_id_attribute" do - it 'rewinds the allocated IID' do - expect { ensure_scope_attribute! }.not_to raise_error - expect(read_internal_id).not_to be_nil - - expect(reset_scope_attribute).to be_nil - expect(read_internal_id).to be_nil - end - - it 'allocates the same IID' do - internal_id = ensure_scope_attribute! - reset_scope_attribute - expect(read_internal_id).to be_nil - - expect(ensure_scope_attribute!).to eq(internal_id) - end - end - - def ensure_scope_attribute! - instance.public_send(:"ensure_#{scope}_#{internal_id_attribute}!") - end - - def reset_scope_attribute - instance.public_send(:"reset_#{scope}_#{internal_id_attribute}") - end - - def read_internal_id - instance.public_send(internal_id_attribute) - end - - def write_internal_id(value) - instance.public_send(:"#{internal_id_attribute}=", value) - end - end -end diff --git a/spec/support/shared_examples/models/chat_service_shared_examples.rb b/spec/support/shared_examples/models/chat_service_shared_examples.rb new file mode 100644 index 00000000000..0a302e7d030 --- /dev/null +++ b/spec/support/shared_examples/models/chat_service_shared_examples.rb @@ -0,0 +1,248 @@ +require "spec_helper" + +shared_examples_for "chat service" do |service_name| + describe "Associations" do + it { is_expected.to belong_to :project } + it { is_expected.to have_one :service_hook } + end + + describe "Validations" do + context "when service is active" do + before do + subject.active = true + end + + it { is_expected.to validate_presence_of(:webhook) } + it_behaves_like "issue tracker service URL attribute", :webhook + end + + context "when service is inactive" do + before do + subject.active = false + end + + it { is_expected.not_to validate_presence_of(:webhook) } + end + end + + describe '.supported_events' do + it 'does not support deployment_events' do + expect(described_class.supported_events).not_to include('deployment') + end + end + + describe "#execute" do + let(:user) { create(:user) } + let(:project) { create(:project, :repository) } + let(:webhook_url) { "https://example.gitlab.com/" } + + before do + allow(subject).to receive_messages( + project: project, + project_id: project.id, + service_hook: true, + webhook: webhook_url + ) + + WebMock.stub_request(:post, webhook_url) + end + + shared_examples "#{service_name} service" do + it "calls #{service_name} API" do + subject.execute(sample_data) + + expect(WebMock).to have_requested(:post, webhook_url).with { |req| req.body =~ /\A{"#{content_key}":.+}\Z/ }.once + end + end + + context "with push events" do + let(:sample_data) do + Gitlab::DataBuilder::Push.build_sample(project, user) + end + + it_behaves_like "#{service_name} service" + + it "specifies the webhook when it is configured" do + expect(client).to receive(:new).with(client_arguments).and_return(double(:chat_service).as_null_object) + + subject.execute(sample_data) + end + + context "with not default branch" do + let(:sample_data) do + Gitlab::DataBuilder::Push.build(project: project, user: user, ref: "not-the-default-branch") + end + + context "when notify_only_default_branch enabled" do + before do + subject.notify_only_default_branch = true + end + + it "does not call the Discord Webhooks API" do + result = subject.execute(sample_data) + + expect(result).to be_falsy + end + end + + context "when notify_only_default_branch disabled" do + before do + subject.notify_only_default_branch = false + end + + it_behaves_like "#{service_name} service" + end + end + end + + context "with issue events" do + let(:opts) { { title: "Awesome issue", description: "please fix" } } + let(:sample_data) do + service = Issues::CreateService.new(project, user, opts) + issue = service.execute + service.hook_data(issue, "open") + end + + it_behaves_like "#{service_name} service" + end + + context "with merge events" do + let(:opts) do + { + title: "Awesome merge_request", + description: "please fix", + source_branch: "feature", + target_branch: "master" + } + end + + let(:sample_data) do + service = MergeRequests::CreateService.new(project, user, opts) + merge_request = service.execute + service.hook_data(merge_request, "open") + end + + before do + project.add_developer(user) + end + + it_behaves_like "#{service_name} service" + end + + context "with wiki page events" do + let(:opts) do + { + title: "Awesome wiki_page", + content: "Some text describing some thing or another", + format: "md", + message: "user created page: Awesome wiki_page" + } + end + let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) } + let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, "create") } + + it_behaves_like "#{service_name} service" + end + + context "with note events" do + let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) } + + context "with commit comment" do + let(:note) do + create(:note_on_commit, + author: user, + project: project, + commit_id: project.repository.commit.id, + note: "a comment on a commit") + end + + it_behaves_like "#{service_name} service" + end + + context "with merge request comment" do + let(:note) do + create(:note_on_merge_request, project: project, note: "merge request note") + end + + it_behaves_like "#{service_name} service" + end + + context "with issue comment" do + let(:note) do + create(:note_on_issue, project: project, note: "issue note") + end + + it_behaves_like "#{service_name} service" + end + + context "with snippet comment" do + let(:note) do + create(:note_on_project_snippet, project: project, note: "snippet note") + end + + it_behaves_like "#{service_name} service" + end + end + + context "with pipeline events" do + let(:pipeline) do + create(:ci_pipeline, + project: project, status: status, + sha: project.commit.sha, ref: project.default_branch) + end + let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) } + + context "with failed pipeline" do + let(:status) { "failed" } + + it_behaves_like "#{service_name} service" + end + + context "with succeeded pipeline" do + let(:status) { "success" } + + context "with default notify_only_broken_pipelines" do + it "does not call Discord Webhooks API" do + result = subject.execute(sample_data) + + expect(result).to be_falsy + end + end + + context "when notify_only_broken_pipelines is false" do + before do + subject.notify_only_broken_pipelines = false + end + + it_behaves_like "#{service_name} service" + end + end + + context "with not default branch" do + let(:pipeline) do + create(:ci_pipeline, project: project, status: "failed", ref: "not-the-default-branch") + end + + context "when notify_only_default_branch enabled" do + before do + subject.notify_only_default_branch = true + end + + it "does not call the Discord Webhooks API" do + result = subject.execute(sample_data) + + expect(result).to be_falsy + end + end + + context "when notify_only_default_branch disabled" do + before do + subject.notify_only_default_branch = false + end + + it_behaves_like "#{service_name} service" + end + end + end + end +end diff --git a/spec/support/shared_examples/models/chat_service_spec.rb b/spec/support/shared_examples/models/chat_service_spec.rb deleted file mode 100644 index 0a302e7d030..00000000000 --- a/spec/support/shared_examples/models/chat_service_spec.rb +++ /dev/null @@ -1,248 +0,0 @@ -require "spec_helper" - -shared_examples_for "chat service" do |service_name| - describe "Associations" do - it { is_expected.to belong_to :project } - it { is_expected.to have_one :service_hook } - end - - describe "Validations" do - context "when service is active" do - before do - subject.active = true - end - - it { is_expected.to validate_presence_of(:webhook) } - it_behaves_like "issue tracker service URL attribute", :webhook - end - - context "when service is inactive" do - before do - subject.active = false - end - - it { is_expected.not_to validate_presence_of(:webhook) } - end - end - - describe '.supported_events' do - it 'does not support deployment_events' do - expect(described_class.supported_events).not_to include('deployment') - end - end - - describe "#execute" do - let(:user) { create(:user) } - let(:project) { create(:project, :repository) } - let(:webhook_url) { "https://example.gitlab.com/" } - - before do - allow(subject).to receive_messages( - project: project, - project_id: project.id, - service_hook: true, - webhook: webhook_url - ) - - WebMock.stub_request(:post, webhook_url) - end - - shared_examples "#{service_name} service" do - it "calls #{service_name} API" do - subject.execute(sample_data) - - expect(WebMock).to have_requested(:post, webhook_url).with { |req| req.body =~ /\A{"#{content_key}":.+}\Z/ }.once - end - end - - context "with push events" do - let(:sample_data) do - Gitlab::DataBuilder::Push.build_sample(project, user) - end - - it_behaves_like "#{service_name} service" - - it "specifies the webhook when it is configured" do - expect(client).to receive(:new).with(client_arguments).and_return(double(:chat_service).as_null_object) - - subject.execute(sample_data) - end - - context "with not default branch" do - let(:sample_data) do - Gitlab::DataBuilder::Push.build(project: project, user: user, ref: "not-the-default-branch") - end - - context "when notify_only_default_branch enabled" do - before do - subject.notify_only_default_branch = true - end - - it "does not call the Discord Webhooks API" do - result = subject.execute(sample_data) - - expect(result).to be_falsy - end - end - - context "when notify_only_default_branch disabled" do - before do - subject.notify_only_default_branch = false - end - - it_behaves_like "#{service_name} service" - end - end - end - - context "with issue events" do - let(:opts) { { title: "Awesome issue", description: "please fix" } } - let(:sample_data) do - service = Issues::CreateService.new(project, user, opts) - issue = service.execute - service.hook_data(issue, "open") - end - - it_behaves_like "#{service_name} service" - end - - context "with merge events" do - let(:opts) do - { - title: "Awesome merge_request", - description: "please fix", - source_branch: "feature", - target_branch: "master" - } - end - - let(:sample_data) do - service = MergeRequests::CreateService.new(project, user, opts) - merge_request = service.execute - service.hook_data(merge_request, "open") - end - - before do - project.add_developer(user) - end - - it_behaves_like "#{service_name} service" - end - - context "with wiki page events" do - let(:opts) do - { - title: "Awesome wiki_page", - content: "Some text describing some thing or another", - format: "md", - message: "user created page: Awesome wiki_page" - } - end - let(:wiki_page) { create(:wiki_page, wiki: project.wiki, attrs: opts) } - let(:sample_data) { Gitlab::DataBuilder::WikiPage.build(wiki_page, user, "create") } - - it_behaves_like "#{service_name} service" - end - - context "with note events" do - let(:sample_data) { Gitlab::DataBuilder::Note.build(note, user) } - - context "with commit comment" do - let(:note) do - create(:note_on_commit, - author: user, - project: project, - commit_id: project.repository.commit.id, - note: "a comment on a commit") - end - - it_behaves_like "#{service_name} service" - end - - context "with merge request comment" do - let(:note) do - create(:note_on_merge_request, project: project, note: "merge request note") - end - - it_behaves_like "#{service_name} service" - end - - context "with issue comment" do - let(:note) do - create(:note_on_issue, project: project, note: "issue note") - end - - it_behaves_like "#{service_name} service" - end - - context "with snippet comment" do - let(:note) do - create(:note_on_project_snippet, project: project, note: "snippet note") - end - - it_behaves_like "#{service_name} service" - end - end - - context "with pipeline events" do - let(:pipeline) do - create(:ci_pipeline, - project: project, status: status, - sha: project.commit.sha, ref: project.default_branch) - end - let(:sample_data) { Gitlab::DataBuilder::Pipeline.build(pipeline) } - - context "with failed pipeline" do - let(:status) { "failed" } - - it_behaves_like "#{service_name} service" - end - - context "with succeeded pipeline" do - let(:status) { "success" } - - context "with default notify_only_broken_pipelines" do - it "does not call Discord Webhooks API" do - result = subject.execute(sample_data) - - expect(result).to be_falsy - end - end - - context "when notify_only_broken_pipelines is false" do - before do - subject.notify_only_broken_pipelines = false - end - - it_behaves_like "#{service_name} service" - end - end - - context "with not default branch" do - let(:pipeline) do - create(:ci_pipeline, project: project, status: "failed", ref: "not-the-default-branch") - end - - context "when notify_only_default_branch enabled" do - before do - subject.notify_only_default_branch = true - end - - it "does not call the Discord Webhooks API" do - result = subject.execute(sample_data) - - expect(result).to be_falsy - end - end - - context "when notify_only_default_branch disabled" do - before do - subject.notify_only_default_branch = false - end - - it_behaves_like "#{service_name} service" - end - end - end - end -end diff --git a/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb new file mode 100644 index 00000000000..7a04e940ee5 --- /dev/null +++ b/spec/support/shared_examples/models/update_project_statistics_shared_examples.rb @@ -0,0 +1,76 @@ +# frozen_string_literal: true + +require 'spec_helper' + +shared_examples_for 'UpdateProjectStatistics' do + let(:project) { subject.project } + let(:stat) { described_class.statistic_name } + let(:attribute) { described_class.statistic_attribute } + + def reload_stat + project.statistics.reload.send(stat).to_i + end + + def read_attribute + subject.read_attribute(attribute).to_i + end + + it { is_expected.to be_new_record } + + context 'when creating' do + it 'updates the project statistics' do + delta = read_attribute + + expect { subject.save! } + .to change { reload_stat } + .by(delta) + end + end + + context 'when updating' do + before do + subject.save! + end + + it 'updates project statistics' do + delta = 42 + + expect(ProjectStatistics) + .to receive(:increment_statistic) + .and_call_original + + subject.write_attribute(attribute, read_attribute + delta) + expect { subject.save! } + .to change { reload_stat } + .by(delta) + end + end + + context 'when destroying' do + before do + subject.save! + end + + it 'updates the project statistics' do + delta = -read_attribute + + expect(ProjectStatistics) + .to receive(:increment_statistic) + .and_call_original + + expect { subject.destroy } + .to change { reload_stat } + .by(delta) + end + + context 'when it is destroyed from the project level' do + it 'does not update the project statistics' do + expect(ProjectStatistics) + .not_to receive(:increment_statistic) + + project.update(pending_delete: true) + project.destroy! + end + end + end +end diff --git a/spec/support/shared_examples/models/update_project_statistics_spec.rb b/spec/support/shared_examples/models/update_project_statistics_spec.rb deleted file mode 100644 index 7a04e940ee5..00000000000 --- a/spec/support/shared_examples/models/update_project_statistics_spec.rb +++ /dev/null @@ -1,76 +0,0 @@ -# frozen_string_literal: true - -require 'spec_helper' - -shared_examples_for 'UpdateProjectStatistics' do - let(:project) { subject.project } - let(:stat) { described_class.statistic_name } - let(:attribute) { described_class.statistic_attribute } - - def reload_stat - project.statistics.reload.send(stat).to_i - end - - def read_attribute - subject.read_attribute(attribute).to_i - end - - it { is_expected.to be_new_record } - - context 'when creating' do - it 'updates the project statistics' do - delta = read_attribute - - expect { subject.save! } - .to change { reload_stat } - .by(delta) - end - end - - context 'when updating' do - before do - subject.save! - end - - it 'updates project statistics' do - delta = 42 - - expect(ProjectStatistics) - .to receive(:increment_statistic) - .and_call_original - - subject.write_attribute(attribute, read_attribute + delta) - expect { subject.save! } - .to change { reload_stat } - .by(delta) - end - end - - context 'when destroying' do - before do - subject.save! - end - - it 'updates the project statistics' do - delta = -read_attribute - - expect(ProjectStatistics) - .to receive(:increment_statistic) - .and_call_original - - expect { subject.destroy } - .to change { reload_stat } - .by(delta) - end - - context 'when it is destroyed from the project level' do - it 'does not update the project statistics' do - expect(ProjectStatistics) - .not_to receive(:increment_statistic) - - project.update(pending_delete: true) - project.destroy! - end - end - end -end diff --git a/spec/support/shared_examples/requests/api/issues_shared_example_spec.rb b/spec/support/shared_examples/requests/api/issues_shared_example_spec.rb deleted file mode 100644 index 1133e95e44e..00000000000 --- a/spec/support/shared_examples/requests/api/issues_shared_example_spec.rb +++ /dev/null @@ -1,44 +0,0 @@ -# frozen_string_literal: true - -shared_examples 'labeled issues with labels and label_name params' do - shared_examples 'returns label names' do - it 'returns label names' do - expect_paginated_array_response(issue.id) - expect(json_response.first['labels']).to eq([label_c.title, label_b.title, label.title]) - end - end - - shared_examples 'returns basic label entity' do - it 'returns basic label entity' do - expect_paginated_array_response(issue.id) - expect(json_response.first['labels'].pluck('name')).to eq([label_c.title, label_b.title, label.title]) - expect(json_response.first['labels'].first).to match_schema('/public_api/v4/label_basic') - end - end - - context 'array of labeled issues when all labels match' do - let(:params) { { labels: "#{label.title},#{label_b.title},#{label_c.title}" } } - - it_behaves_like 'returns label names' - end - - context 'array of labeled issues when all labels match with labels param as array' do - let(:params) { { labels: [label.title, label_b.title, label_c.title] } } - - it_behaves_like 'returns label names' - end - - context 'when with_labels_details provided' do - context 'array of labeled issues when all labels match' do - let(:params) { { labels: "#{label.title},#{label_b.title},#{label_c.title}", with_labels_details: true } } - - it_behaves_like 'returns basic label entity' - end - - context 'array of labeled issues when all labels match with labels param as array' do - let(:params) { { labels: [label.title, label_b.title, label_c.title], with_labels_details: true } } - - it_behaves_like 'returns basic label entity' - end - end -end diff --git a/spec/support/shared_examples/requests/api/issues_shared_examples.rb b/spec/support/shared_examples/requests/api/issues_shared_examples.rb new file mode 100644 index 00000000000..1133e95e44e --- /dev/null +++ b/spec/support/shared_examples/requests/api/issues_shared_examples.rb @@ -0,0 +1,44 @@ +# frozen_string_literal: true + +shared_examples 'labeled issues with labels and label_name params' do + shared_examples 'returns label names' do + it 'returns label names' do + expect_paginated_array_response(issue.id) + expect(json_response.first['labels']).to eq([label_c.title, label_b.title, label.title]) + end + end + + shared_examples 'returns basic label entity' do + it 'returns basic label entity' do + expect_paginated_array_response(issue.id) + expect(json_response.first['labels'].pluck('name')).to eq([label_c.title, label_b.title, label.title]) + expect(json_response.first['labels'].first).to match_schema('/public_api/v4/label_basic') + end + end + + context 'array of labeled issues when all labels match' do + let(:params) { { labels: "#{label.title},#{label_b.title},#{label_c.title}" } } + + it_behaves_like 'returns label names' + end + + context 'array of labeled issues when all labels match with labels param as array' do + let(:params) { { labels: [label.title, label_b.title, label_c.title] } } + + it_behaves_like 'returns label names' + end + + context 'when with_labels_details provided' do + context 'array of labeled issues when all labels match' do + let(:params) { { labels: "#{label.title},#{label_b.title},#{label_c.title}", with_labels_details: true } } + + it_behaves_like 'returns basic label entity' + end + + context 'array of labeled issues when all labels match with labels param as array' do + let(:params) { { labels: [label.title, label_b.title, label_c.title], with_labels_details: true } } + + it_behaves_like 'returns basic label entity' + end + end +end -- cgit v1.2.1