1# frozen_string_literal: true 2 3# $" is $LOADED_FEATURES, but RuboCop didn't like it 4if $".include?(File.expand_path('fast_spec_helper.rb', __dir__)) 5 warn 'Detected fast_spec_helper is loaded first than spec_helper.' 6 warn 'If running test files using both spec_helper and fast_spec_helper,' 7 warn 'make sure test file with spec_helper is loaded first.' 8 abort 'Aborting...' 9end 10 11# Enable deprecation warnings by default and make them more visible 12# to developers to ease upgrading to newer Ruby versions. 13Warning[:deprecated] = true unless ENV.key?('SILENCE_DEPRECATIONS') 14 15require './spec/deprecation_toolkit_env' 16DeprecationToolkitEnv.configure! 17 18require './spec/knapsack_env' 19KnapsackEnv.configure! 20 21require './spec/simplecov_env' 22SimpleCovEnv.start! 23 24require './spec/crystalball_env' 25CrystalballEnv.start! 26 27ENV["RAILS_ENV"] = 'test' 28ENV["IN_MEMORY_APPLICATION_SETTINGS"] = 'true' 29ENV["RSPEC_ALLOW_INVALID_URLS"] = 'true' 30 31require_relative '../config/environment' 32 33require 'rspec/mocks' 34require 'rspec/rails' 35require 'rspec/retry' 36require 'rspec-parameterized' 37require 'shoulda/matchers' 38require 'test_prof/recipes/rspec/let_it_be' 39require 'test_prof/factory_default' 40require 'parslet/rig/rspec' 41 42rspec_profiling_is_configured = 43 ENV['RSPEC_PROFILING_POSTGRES_URL'].present? || 44 ENV['RSPEC_PROFILING'] 45branch_can_be_profiled = 46 (ENV['CI_COMMIT_REF_NAME'] == 'master' || 47 ENV['CI_COMMIT_REF_NAME'] =~ /rspec-profile/) 48 49if rspec_profiling_is_configured && (!ENV.key?('CI') || branch_can_be_profiled) 50 require 'rspec_profiling/rspec' 51end 52 53# require rainbow gem String monkeypatch, so we can test SystemChecks 54require 'rainbow/ext/string' 55Rainbow.enabled = false 56 57require_relative('../ee/spec/spec_helper') if Gitlab.ee? 58require_relative('../jh/spec/spec_helper') if Gitlab.jh? 59 60# Requires supporting ruby files with custom matchers and macros, etc, 61# in spec/support/ and its subdirectories. 62# Requires helpers, and shared contexts/examples first since they're used in other support files 63 64# Load these first since they may be required by other helpers 65require Rails.root.join("spec/support/helpers/git_helpers.rb") 66require Rails.root.join("spec/support/helpers/stub_requests.rb") 67 68# Then the rest 69Dir[Rails.root.join("spec/support/helpers/*.rb")].sort.each { |f| require f } 70Dir[Rails.root.join("spec/support/shared_contexts/*.rb")].sort.each { |f| require f } 71Dir[Rails.root.join("spec/support/shared_examples/*.rb")].sort.each { |f| require f } 72Dir[Rails.root.join("spec/support/**/*.rb")].sort.each { |f| require f } 73 74require_relative '../tooling/quality/test_level' 75 76quality_level = Quality::TestLevel.new 77 78RSpec.configure do |config| 79 config.use_transactional_fixtures = true 80 config.use_instantiated_fixtures = false 81 config.fixture_path = Rails.root 82 83 config.verbose_retry = true 84 config.display_try_failure_messages = true 85 86 config.infer_spec_type_from_file_location! 87 88 # Add :full_backtrace tag to an example if full_backtrace output is desired 89 config.before(:each, full_backtrace: true) do |example| 90 config.full_backtrace = true 91 end 92 93 # Attempt to troubleshoot https://gitlab.com/gitlab-org/gitlab/-/issues/297359 94 if ENV['CI'] 95 config.after do |example| 96 if example.exception.is_a?(GRPC::Unavailable) 97 warn "=== gRPC unavailable detected, process list:" 98 processes = `ps -ef | grep toml` 99 warn processes 100 warn "=== free memory" 101 warn `free -m` 102 warn "=== uptime" 103 warn `uptime` 104 warn "=== Prometheus metrics:" 105 warn `curl -s -o log/gitaly-metrics.log http://localhost:9236/metrics` 106 warn "=== Taking goroutine dump in log/goroutines.log..." 107 warn `curl -s -o log/goroutines.log http://localhost:9236/debug/pprof/goroutine?debug=2` 108 end 109 end 110 else 111 # Allow running `:focus` examples locally, 112 # falling back to all tests when there is no `:focus` example. 113 config.filter_run focus: true 114 config.run_all_when_everything_filtered = true 115 116 # Re-run failures locally with `--only-failures` 117 config.example_status_persistence_file_path = './spec/examples.txt' 118 end 119 120 config.define_derived_metadata(file_path: %r{(ee)?/spec/.+_spec\.rb\z}) do |metadata| 121 location = metadata[:location] 122 123 metadata[:level] = quality_level.level_for(location) 124 metadata[:api] = true if location =~ %r{/spec/requests/api/} 125 126 # Do not overwrite migration if it's already set 127 unless metadata.key?(:migration) 128 metadata[:migration] = true if metadata[:level] == :migration 129 end 130 131 # Do not overwrite schema if it's already set 132 unless metadata.key?(:schema) 133 metadata[:schema] = :latest if quality_level.background_migration?(location) 134 end 135 136 # Do not overwrite type if it's already set 137 unless metadata.key?(:type) 138 match = location.match(%r{/spec/([^/]+)/}) 139 metadata[:type] = match[1].singularize.to_sym if match 140 end 141 142 # Admin controller specs get auto admin mode enabled since they are 143 # protected by the 'EnforcesAdminAuthentication' concern 144 metadata[:enable_admin_mode] = true if location =~ %r{(ee)?/spec/controllers/admin/} 145 end 146 147 config.define_derived_metadata(file_path: %r{(ee)?/spec/.+_docs\.rb\z}) do |metadata| 148 metadata[:type] = :feature 149 end 150 151 config.include LicenseHelpers 152 config.include ActiveJob::TestHelper 153 config.include ActiveSupport::Testing::TimeHelpers 154 config.include CycleAnalyticsHelpers 155 config.include FactoryBot::Syntax::Methods 156 config.include FixtureHelpers 157 config.include NonExistingRecordsHelpers 158 config.include GitlabRoutingHelper 159 config.include StubExperiments 160 config.include StubGitlabCalls 161 config.include NextFoundInstanceOf 162 config.include NextInstanceOf 163 config.include TestEnv 164 config.include FileReadHelpers 165 config.include Database::MultipleDatabases 166 config.include Devise::Test::ControllerHelpers, type: :controller 167 config.include Devise::Test::ControllerHelpers, type: :view 168 config.include Devise::Test::IntegrationHelpers, type: :feature 169 config.include Devise::Test::IntegrationHelpers, type: :request 170 config.include LoginHelpers, type: :feature 171 config.include SearchHelpers, type: :feature 172 config.include WaitHelpers, type: :feature 173 config.include WaitForRequests, type: :feature 174 config.include EmailHelpers, :mailer, type: :mailer 175 config.include Warden::Test::Helpers, type: :request 176 config.include Gitlab::Routing, type: :routing 177 config.include ApiHelpers, :api 178 config.include CookieHelper, :js 179 config.include InputHelper, :js 180 config.include SelectionHelper, :js 181 config.include InspectRequests, :js 182 config.include LiveDebugger, :js 183 config.include MigrationsHelpers, :migration 184 config.include RedisHelpers 185 config.include Rails.application.routes.url_helpers, type: :routing 186 config.include PolicyHelpers, type: :policy 187 config.include MemoryUsageHelper 188 config.include ExpectRequestWithStatus, type: :request 189 config.include IdempotentWorkerHelper, type: :worker 190 config.include RailsHelpers 191 config.include SidekiqMiddleware 192 config.include StubActionCableConnection, type: :channel 193 config.include StubSpamServices 194 195 include StubFeatureFlags 196 197 if ENV['CI'] || ENV['RETRIES'] 198 # This includes the first try, i.e. tests will be run 4 times before failing. 199 config.default_retry_count = ENV.fetch('RETRIES', 3).to_i + 1 200 201 # Do not retry controller tests because rspec-retry cannot properly 202 # reset the controller which may contain data from last attempt. See 203 # https://gitlab.com/gitlab-org/gitlab/-/merge_requests/73360 204 config.prepend_before(:each, type: :controller) do |example| 205 example.metadata[:retry] = 1 206 end 207 208 config.exceptions_to_hard_fail = [DeprecationToolkitEnv::DeprecationBehaviors::SelectiveRaise::RaiseDisallowedDeprecation] 209 end 210 211 if ENV['FLAKY_RSPEC_GENERATE_REPORT'] 212 require_relative '../tooling/rspec_flaky/listener' 213 214 config.reporter.register_listener( 215 RspecFlaky::Listener.new, 216 :example_passed, 217 :dump_summary) 218 end 219 220 config.before(:suite) do 221 Timecop.safe_mode = true 222 TestEnv.init 223 224 # Reload all feature flags definitions 225 Feature.register_definitions 226 227 # Enable all features by default for testing 228 # Reset any changes in after hook. 229 stub_all_feature_flags 230 231 TestEnv.seed_db 232 end 233 234 config.after(:all) do 235 TestEnv.clean_test_path 236 end 237 238 # We can't use an `around` hook here because the wrapping transaction 239 # is not yet opened at the time that is triggered 240 config.prepend_before do 241 ApplicationRecord.set_open_transactions_baseline 242 ::Ci::ApplicationRecord.set_open_transactions_baseline 243 end 244 245 config.append_before do 246 Thread.current[:current_example_group] = ::RSpec.current_example.metadata[:example_group] 247 end 248 249 config.append_after do 250 ApplicationRecord.reset_open_transactions_baseline 251 ::Ci::ApplicationRecord.reset_open_transactions_baseline 252 end 253 254 config.before do |example| 255 if example.metadata.fetch(:stub_feature_flags, true) 256 # The following can be removed when we remove the staged rollout strategy 257 # and we can just enable it using instance wide settings 258 # (ie. ApplicationSetting#auto_devops_enabled) 259 stub_feature_flags(force_autodevops_on_by_default: false) 260 261 # Merge request widget GraphQL requests are disabled in the tests 262 # for now whilst we migrate as much as we can over the GraphQL 263 # stub_feature_flags(merge_request_widget_graphql: false) 264 265 # Using FortiAuthenticator as OTP provider is disabled by default in 266 # tests, until we introduce it in user settings 267 stub_feature_flags(forti_authenticator: false) 268 269 # Using FortiToken Cloud as OTP provider is disabled by default in 270 # tests, until we introduce it in user settings 271 stub_feature_flags(forti_token_cloud: false) 272 273 # Disable for now whilst we add more states 274 stub_feature_flags(restructured_mr_widget: false) 275 276 # These feature flag are by default disabled and used in disaster recovery mode 277 stub_feature_flags(ci_queueing_disaster_recovery_disable_fair_scheduling: false) 278 stub_feature_flags(ci_queueing_disaster_recovery_disable_quota: false) 279 280 enable_rugged = example.metadata[:enable_rugged].present? 281 282 # Disable Rugged features by default 283 Gitlab::Git::RuggedImpl::Repository::FEATURE_FLAGS.each do |flag| 284 stub_feature_flags(flag => enable_rugged) 285 end 286 287 # Disable the usage of file_identifier_hash by default until it is ready 288 # See https://gitlab.com/gitlab-org/gitlab/-/issues/33867 289 stub_feature_flags(file_identifier_hash: false) 290 291 stub_feature_flags(diffs_virtual_scrolling: false) 292 293 # The following `vue_issues_list`/`vue_issuables_list` stubs can be removed 294 # once the Vue issues page has feature parity with the current Haml page 295 stub_feature_flags(vue_issues_list: false) 296 stub_feature_flags(vue_issuables_list: false) 297 298 # Disable `refactor_blob_viewer` as we refactor 299 # the blob viewer. See the follwing epic for more: 300 # https://gitlab.com/groups/gitlab-org/-/epics/5531 301 stub_feature_flags(refactor_blob_viewer: false) 302 303 # Disable `main_branch_over_master` as we migrate 304 # from `master` to `main` accross our codebase. 305 # It's done in order to preserve the concistency in tests 306 # As we're ready to change `master` usages to `main`, let's enable it 307 stub_feature_flags(main_branch_over_master: false) 308 309 stub_feature_flags(issue_boards_filtered_search: false) 310 311 # Disable issue respositioning to avoid heavy load on database when importing big projects. 312 # This is only turned on when app is handling heavy project imports. 313 # Can be removed when we find a better way to deal with the problem. 314 # For more information check https://gitlab.com/gitlab-com/gl-infra/production/-/issues/4321 315 stub_feature_flags(block_issue_repositioning: false) 316 317 # These are ops feature flags that are disabled by default 318 stub_feature_flags(disable_anonymous_search: false) 319 stub_feature_flags(disable_anonymous_project_search: false) 320 321 # Disable the refactored top nav search until there is functionality 322 # Can be removed once all existing functionality has been replicated 323 # For more information check https://gitlab.com/gitlab-org/gitlab/-/issues/339348 324 stub_feature_flags(new_header_search: false) 325 326 allow(Gitlab::GitalyClient).to receive(:can_use_disk?).and_return(enable_rugged) 327 else 328 unstub_all_feature_flags 329 end 330 331 # Stub these calls due to being expensive operations 332 # It can be reenabled for specific tests via: 333 # 334 # expect(Gitlab::Git::KeepAround).to receive(:execute).and_call_original 335 allow(Gitlab::Git::KeepAround).to receive(:execute) 336 337 # Stub these calls due to being expensive operations 338 # It can be reenabled for specific tests via: 339 # 340 # expect(Gitlab::JobWaiter).to receive(:wait).and_call_original 341 allow_any_instance_of(Gitlab::JobWaiter).to receive(:wait) 342 343 Gitlab::ProcessMemoryCache.cache_backend.clear 344 345 Sidekiq::Worker.clear_all 346 347 # Administrators have to re-authenticate in order to access administrative 348 # functionality when application setting admin_mode is active. Any spec 349 # that requires administrative access can use the tag :enable_admin_mode 350 # to avoid the second auth step (provided the user is already an admin): 351 # 352 # context 'some test that requires admin mode', :enable_admin_mode do ... end 353 # 354 # Some specs do get admin mode enabled automatically (e.g. `spec/controllers/admin`). 355 # In this case, specs that need to test both admin mode states can use the 356 # :do_not_mock_admin_mode tag to disable auto admin mode. 357 # 358 # See also spec/support/helpers/admin_mode_helpers.rb 359 if example.metadata[:enable_admin_mode] && !example.metadata[:do_not_mock_admin_mode] 360 allow_any_instance_of(Gitlab::Auth::CurrentUserMode).to receive(:admin_mode?) do |current_user_mode| 361 current_user_mode.send(:user)&.admin? 362 end 363 end 364 365 # Make sure specs test by default admin mode setting on, unless forced to the opposite 366 stub_application_setting(admin_mode: true) unless example.metadata[:do_not_mock_admin_mode_setting] 367 368 allow(Gitlab::CurrentSettings).to receive(:current_application_settings?).and_return(false) 369 end 370 371 config.around(:example, :quarantine) do |example| 372 # Skip tests in quarantine unless we explicitly focus on them. 373 example.run if config.inclusion_filter[:quarantine] 374 end 375 376 config.around(:example, :request_store) do |example| 377 Gitlab::WithRequestStore.with_request_store { example.run } 378 end 379 380 # previous test runs may have left some resources throttled 381 config.before do 382 ::Gitlab::ExclusiveLease.reset_all!("el:throttle:*") 383 end 384 385 config.before(:example, :assume_throttled) do |example| 386 allow(::Gitlab::ExclusiveLease).to receive(:throttle).and_return(nil) 387 end 388 389 config.before(:example, :request_store) do 390 # Clear request store before actually starting the spec (the 391 # `around` above will have the request store enabled for all 392 # `before` blocks) 393 RequestStore.clear! 394 end 395 396 config.around do |example| 397 # Wrap each example in it's own context to make sure the contexts don't 398 # leak 399 Gitlab::ApplicationContext.with_raw_context { example.run } 400 end 401 402 config.around do |example| 403 with_sidekiq_server_middleware do |chain| 404 Gitlab::SidekiqMiddleware.server_configurator( 405 metrics: false, # The metrics don't go anywhere in tests 406 arguments_logger: false, # We're not logging the regular messages for inline jobs 407 memory_killer: false # This is not a thing we want to do inline in tests 408 ).call(chain) 409 chain.add DisableQueryLimit 410 chain.insert_after ::Gitlab::SidekiqMiddleware::RequestStoreMiddleware, IsolatedRequestStore 411 412 example.run 413 end 414 end 415 416 config.after do 417 Fog.unmock! if Fog.mock? 418 Gitlab::CurrentSettings.clear_in_memory_application_settings! 419 420 # Reset all feature flag stubs to default for testing 421 stub_all_feature_flags 422 423 # Re-enable query limiting in case it was disabled 424 Gitlab::QueryLimiting.enable! 425 end 426 427 config.before(:example, :mailer) do 428 reset_delivered_emails! 429 end 430 431 config.before(:example, :prometheus) do 432 matching_files = File.join(::Prometheus::Client.configuration.multiprocess_files_dir, "**/*.db") 433 Dir[matching_files].map { |filename| File.delete(filename) if File.file?(filename) } 434 435 Gitlab::Metrics.reset_registry! 436 end 437 438 config.before(:example, :eager_load) do 439 Rails.application.eager_load! 440 end 441 442 # This makes sure the `ApplicationController#can?` method is stubbed with the 443 # original implementation for all view specs. 444 config.before(:each, type: :view) do 445 allow(view).to receive(:can?) do |*args| 446 Ability.allowed?(*args) 447 end 448 end 449 450 # Allows stdout to be redirected to reduce noise 451 config.before(:each, :silence_stdout) do 452 $stdout = StringIO.new 453 end 454 455 # Makes diffs show entire non-truncated values. 456 config.before(:each, unlimited_max_formatted_output_length: true) do |_example| 457 config.expect_with :rspec do |c| 458 c.max_formatted_output_length = nil 459 end 460 end 461 462 config.after(:each, :silence_stdout) do 463 $stdout = STDOUT 464 end 465 466 config.disable_monkey_patching! 467end 468 469ActiveRecord::Migration.maintain_test_schema! 470 471Shoulda::Matchers.configure do |config| 472 config.integrate do |with| 473 with.test_framework :rspec 474 with.library :rails 475 end 476end 477 478# Prevent Rugged from picking up local developer gitconfig. 479Rugged::Settings['search_path_global'] = Rails.root.join('tmp/tests').to_s 480 481# Initialize FactoryDefault to use create_default helper 482TestProf::FactoryDefault.init 483 484# Exclude the Geo proxy API request from getting on_next_request Warden handlers, 485# necessary to prevent race conditions with feature tests not getting authenticated. 486::Warden.asset_paths << %r{^/api/v4/geo/proxy$} 487 488module TouchRackUploadedFile 489 def initialize_from_file_path(path) 490 super 491 492 # This is a no-op workaround for https://github.com/docker/for-linux/issues/1015 493 File.utime @tempfile.atime, @tempfile.mtime, @tempfile.path # rubocop:disable Gitlab/ModuleWithInstanceVariables 494 end 495end 496 497Rack::Test::UploadedFile.prepend(TouchRackUploadedFile) 498