Commit c9a8a2f0 authored by Jan Provaznik's avatar Jan Provaznik

Merge branch 'fix_file_read_stub' into 'master'

Stub file reads only for the files we are interested in

See merge request gitlab-org/gitlab!48014
parents a4bbb806 56b6f8a6
...@@ -603,6 +603,32 @@ it "really connects to Prometheus", :permit_dns do ...@@ -603,6 +603,32 @@ it "really connects to Prometheus", :permit_dns do
And if you need more specific control, the DNS blocking is implemented in And if you need more specific control, the DNS blocking is implemented in
`spec/support/helpers/dns_helpers.rb` and these methods can be called elsewhere. `spec/support/helpers/dns_helpers.rb` and these methods can be called elsewhere.
#### Stubbing File methods
In the situations where you need to
[stub](https://relishapp.com/rspec/rspec-mocks/v/3-9/docs/basics/allowing-messages)
methods such as `File.read`, make sure to:
1. Stub `File.read` for only the filepath you are interested in.
1. Call the original implementation for other filepaths.
Otherwise `File.read` calls from other parts of the codebase get
stubbed incorrectly. You should use the `stub_file_read`, and
`expect_file_read` helper methods which does the stubbing for
`File.read` correctly.
```ruby
# bad, all Files will read and return nothing
allow(File).to receive(:read)
# good
stub_file_read(my_filepath)
# also OK
allow(File).to receive(:read).and_call_original
allow(File).to receive(:read).with(my_filepath)
```
#### Filesystem #### Filesystem
Filesystem data can be roughly split into "repositories", and "everything else". Filesystem data can be roughly split into "repositories", and "everything else".
......
...@@ -11,7 +11,7 @@ RSpec.describe Gitlab::Sitemaps::SitemapFile do ...@@ -11,7 +11,7 @@ RSpec.describe Gitlab::Sitemaps::SitemapFile do
describe '#render' do describe '#render' do
it 'returns if no elements has been provided' do it 'returns if no elements has been provided' do
expect(File).not_to receive(:read) expect_file_not_to_read(described_class::SITEMAP_FILE_PATH)
described_class.new.save # rubocop: disable Rails/SaveBang described_class.new.save # rubocop: disable Rails/SaveBang
end end
......
...@@ -36,7 +36,7 @@ RSpec.describe 'gitlab:license namespace rake tasks' do ...@@ -36,7 +36,7 @@ RSpec.describe 'gitlab:license namespace rake tasks' do
let(:license_file_contents) { 'valid contents' } let(:license_file_contents) { 'valid contents' }
it 'succeeds in adding the license' do it 'succeeds in adding the license' do
expect(File).to receive(:read).with(license_path).and_return(license_file_contents) expect_file_read(license_path, content: license_file_contents)
expect(License).to receive(:create).with(data: license_file_contents).and_return(true) expect(License).to receive(:create).with(data: license_file_contents).and_return(true)
expect { subject }.not_to raise_error expect { subject }.not_to raise_error
...@@ -47,7 +47,7 @@ RSpec.describe 'gitlab:license namespace rake tasks' do ...@@ -47,7 +47,7 @@ RSpec.describe 'gitlab:license namespace rake tasks' do
let(:license_file_contents) { 'invalid contents' } let(:license_file_contents) { 'invalid contents' }
it 'fails to add the license' do it 'fails to add the license' do
expect(File).to receive(:read).with(license_path).and_return(license_file_contents) expect_file_read(license_path, content: license_file_contents)
expect(License).to receive(:create).with(data: license_file_contents).and_return(false) expect(License).to receive(:create).with(data: license_file_contents).and_return(false)
expect { subject }.to raise_error(RuntimeError, "License Invalid") expect { subject }.to raise_error(RuntimeError, "License Invalid")
......
...@@ -101,7 +101,8 @@ RSpec.describe HelpController do ...@@ -101,7 +101,8 @@ RSpec.describe HelpController do
context 'for Markdown formats' do context 'for Markdown formats' do
context 'when requested file exists' do context 'when requested file exists' do
before do before do
expect(File).to receive(:read).and_return(fixture_file('blockquote_fence_after.md')) expect_file_read(File.join(Rails.root, 'doc/ssh/README.md'), content: fixture_file('blockquote_fence_after.md'))
get :show, params: { path: 'ssh/README' }, format: :md get :show, params: { path: 'ssh/README' }, format: :md
end end
...@@ -213,6 +214,6 @@ RSpec.describe HelpController do ...@@ -213,6 +214,6 @@ RSpec.describe HelpController do
end end
def stub_readme(content) def stub_readme(content)
expect(File).to receive(:read).and_return(content) expect_file_read(Rails.root.join('doc', 'README.md'), content: content)
end end
end end
...@@ -86,7 +86,7 @@ RSpec.describe IconsHelper do ...@@ -86,7 +86,7 @@ RSpec.describe IconsHelper do
it 'does not raise in production mode' do it 'does not raise in production mode' do
stub_rails_env('production') stub_rails_env('production')
expect(File).not_to receive(:read) expect_file_not_to_read(Rails.root.join('node_modules/@gitlab/svgs/dist/icons.json'))
expect { sprite_icon(non_existing) }.not_to raise_error expect { sprite_icon(non_existing) }.not_to raise_error
end end
......
...@@ -11,10 +11,13 @@ RSpec.describe 'create_tokens' do ...@@ -11,10 +11,13 @@ RSpec.describe 'create_tokens' do
let(:rsa_key) { /\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m.freeze } let(:rsa_key) { /\A-----BEGIN RSA PRIVATE KEY-----\n.+\n-----END RSA PRIVATE KEY-----\n\Z/m.freeze }
before do before do
allow(File).to receive(:write)
allow(File).to receive(:delete)
allow(Rails).to receive_message_chain(:application, :secrets).and_return(secrets) allow(Rails).to receive_message_chain(:application, :secrets).and_return(secrets)
allow(Rails).to receive_message_chain(:root, :join) { |string| string } allow(Rails).to receive_message_chain(:root, :join) { |string| string }
allow(File).to receive(:write).and_call_original
allow(File).to receive(:write).with(Rails.root.join('config/secrets.yml'))
allow(File).to receive(:delete).and_call_original
allow(File).to receive(:delete).with(Rails.root.join('.secret'))
allow(self).to receive(:warn) allow(self).to receive(:warn)
allow(self).to receive(:exit) allow(self).to receive(:exit)
end end
...@@ -105,7 +108,7 @@ RSpec.describe 'create_tokens' do ...@@ -105,7 +108,7 @@ RSpec.describe 'create_tokens' do
secrets.openid_connect_signing_key = 'openid_connect_signing_key' secrets.openid_connect_signing_key = 'openid_connect_signing_key'
allow(File).to receive(:exist?).with('.secret').and_return(true) allow(File).to receive(:exist?).with('.secret').and_return(true)
allow(File).to receive(:read).with('.secret').and_return('file_key') stub_file_read('.secret', content: 'file_key')
end end
context 'when secret_key_base exists in the environment and secrets.yml' do context 'when secret_key_base exists in the environment and secrets.yml' do
......
...@@ -75,7 +75,7 @@ RSpec.describe Feature::Definition do ...@@ -75,7 +75,7 @@ RSpec.describe Feature::Definition do
describe '.load_from_file' do describe '.load_from_file' do
it 'properly loads a definition from file' do it 'properly loads a definition from file' do
expect(File).to receive(:read).with(path) { yaml_content } expect_file_read(path, content: yaml_content)
expect(described_class.send(:load_from_file, path).attributes) expect(described_class.send(:load_from_file, path).attributes)
.to eq(definition.attributes) .to eq(definition.attributes)
...@@ -93,7 +93,7 @@ RSpec.describe Feature::Definition do ...@@ -93,7 +93,7 @@ RSpec.describe Feature::Definition do
context 'for invalid definition' do context 'for invalid definition' do
it 'raises exception' do it 'raises exception' do
expect(File).to receive(:read).with(path) { '{}' } expect_file_read(path, content: '{}')
expect do expect do
described_class.send(:load_from_file, path) described_class.send(:load_from_file, path)
......
...@@ -69,8 +69,8 @@ RSpec.describe Gitlab::Email::Smime::Certificate do ...@@ -69,8 +69,8 @@ RSpec.describe Gitlab::Email::Smime::Certificate do
describe '.from_files' do describe '.from_files' do
it 'parses correctly a certificate and key' do it 'parses correctly a certificate and key' do
allow(File).to receive(:read).with('a_key').and_return(@cert[:key].to_s) stub_file_read('a_key', content: @cert[:key].to_s)
allow(File).to receive(:read).with('a_cert').and_return(@cert[:cert].to_pem) stub_file_read('a_cert', content: @cert[:cert].to_pem)
parsed_cert = described_class.from_files('a_key', 'a_cert') parsed_cert = described_class.from_files('a_key', 'a_cert')
...@@ -79,9 +79,9 @@ RSpec.describe Gitlab::Email::Smime::Certificate do ...@@ -79,9 +79,9 @@ RSpec.describe Gitlab::Email::Smime::Certificate do
context 'with optional ca_certs' do context 'with optional ca_certs' do
it 'parses correctly certificate, key and ca_certs' do it 'parses correctly certificate, key and ca_certs' do
allow(File).to receive(:read).with('a_key').and_return(@cert[:key].to_s) stub_file_read('a_key', content: @cert[:key].to_s)
allow(File).to receive(:read).with('a_cert').and_return(@cert[:cert].to_pem) stub_file_read('a_cert', content: @cert[:cert].to_pem)
allow(File).to receive(:read).with('a_ca_cert').and_return(@intermediate_ca[:cert].to_pem) stub_file_read('a_ca_cert', content: @intermediate_ca[:cert].to_pem)
parsed_cert = described_class.from_files('a_key', 'a_cert', 'a_ca_cert') parsed_cert = described_class.from_files('a_key', 'a_cert', 'a_ca_cert')
...@@ -94,8 +94,8 @@ RSpec.describe Gitlab::Email::Smime::Certificate do ...@@ -94,8 +94,8 @@ RSpec.describe Gitlab::Email::Smime::Certificate do
it 'parses correctly a certificate and key' do it 'parses correctly a certificate and key' do
cert = generate_cert(signer_ca: @root_ca) cert = generate_cert(signer_ca: @root_ca)
allow(File).to receive(:read).with('a_key').and_return(cert[:key].to_s) stub_file_read('a_key', content: cert[:key].to_s)
allow(File).to receive(:read).with('a_cert').and_return(cert[:cert].to_pem) stub_file_read('a_cert', content: cert[:cert].to_pem)
parsed_cert = described_class.from_files('a_key', 'a_cert') parsed_cert = described_class.from_files('a_key', 'a_cert')
......
...@@ -53,7 +53,7 @@ RSpec.describe Gitlab::GitalyClient do ...@@ -53,7 +53,7 @@ RSpec.describe Gitlab::GitalyClient do
describe '.filesystem_id_from_disk' do describe '.filesystem_id_from_disk' do
it 'catches errors' do it 'catches errors' do
[Errno::ENOENT, Errno::EACCES, JSON::ParserError].each do |error| [Errno::ENOENT, Errno::EACCES, JSON::ParserError].each do |error|
allow(File).to receive(:read).with(described_class.storage_metadata_file_path('default')).and_raise(error) stub_file_read(described_class.storage_metadata_file_path('default'), error: error)
expect(described_class.filesystem_id_from_disk('default')).to be_nil expect(described_class.filesystem_id_from_disk('default')).to be_nil
end end
......
...@@ -97,7 +97,7 @@ RSpec.describe Gitlab::Webpack::Manifest do ...@@ -97,7 +97,7 @@ RSpec.describe Gitlab::Webpack::Manifest do
context "with dev server disabled" do context "with dev server disabled" do
before do before do
allow(Gitlab.config.webpack.dev_server).to receive(:enabled).and_return(false) allow(Gitlab.config.webpack.dev_server).to receive(:enabled).and_return(false)
allow(File).to receive(:read).with(::Rails.root.join("manifest_output/my_manifest.json")).and_return(manifest) stub_file_read(::Rails.root.join("manifest_output/my_manifest.json"), content: manifest)
end end
describe ".asset_paths" do describe ".asset_paths" do
...@@ -105,7 +105,7 @@ RSpec.describe Gitlab::Webpack::Manifest do ...@@ -105,7 +105,7 @@ RSpec.describe Gitlab::Webpack::Manifest do
it "errors if we can't find the manifest" do it "errors if we can't find the manifest" do
allow(Gitlab.config.webpack).to receive(:manifest_filename).and_return('broken.json') allow(Gitlab.config.webpack).to receive(:manifest_filename).and_return('broken.json')
allow(File).to receive(:read).with(::Rails.root.join("manifest_output/broken.json")).and_raise(Errno::ENOENT) stub_file_read(::Rails.root.join("manifest_output/broken.json"), error: Errno::ENOENT)
expect { Gitlab::Webpack::Manifest.asset_paths("entry1") }.to raise_error(Gitlab::Webpack::Manifest::ManifestLoadError) expect { Gitlab::Webpack::Manifest.asset_paths("entry1") }.to raise_error(Gitlab::Webpack::Manifest::ManifestLoadError)
end end
end end
......
...@@ -26,18 +26,17 @@ RSpec.describe Gitlab do ...@@ -26,18 +26,17 @@ RSpec.describe Gitlab do
end end
it 'returns the actual Git revision' do it 'returns the actual Git revision' do
expect(File).to receive(:read) expect_file_read(described_class.root.join('REVISION'), content: "abc123\n")
.with(described_class.root.join('REVISION'))
.and_return("abc123\n")
expect(described_class.revision).to eq('abc123') expect(described_class.revision).to eq('abc123')
end end
it 'memoizes the revision' do it 'memoizes the revision' do
stub_file_read(described_class.root.join('REVISION'), content: "abc123\n")
expect(File).to receive(:read) expect(File).to receive(:read)
.once .once
.with(described_class.root.join('REVISION')) .with(described_class.root.join('REVISION'))
.and_return("abc123\n")
2.times { described_class.revision } 2.times { described_class.revision }
end end
......
...@@ -10,31 +10,35 @@ RSpec.describe InstanceConfiguration do ...@@ -10,31 +10,35 @@ RSpec.describe InstanceConfiguration do
let(:sha256) { 'SHA256:2KJDT7xf2i68mBgJ3TVsjISntg4droLbXYLfQj0VvSY' } let(:sha256) { 'SHA256:2KJDT7xf2i68mBgJ3TVsjISntg4droLbXYLfQj0VvSY' }
it 'does not return anything if file does not exist' do it 'does not return anything if file does not exist' do
stub_pub_file(exist: false) stub_pub_file(pub_file(exist: false))
expect(subject.settings[:ssh_algorithms_hashes]).to be_empty expect(subject.settings[:ssh_algorithms_hashes]).to be_empty
end end
it 'does not return anything if file is empty' do it 'does not return anything if file is empty' do
stub_pub_file stub_pub_file(pub_file)
allow(File).to receive(:read).and_return('') stub_file_read(pub_file, content: '')
expect(subject.settings[:ssh_algorithms_hashes]).to be_empty expect(subject.settings[:ssh_algorithms_hashes]).to be_empty
end end
it 'returns the md5 and sha256 if file valid and exists' do it 'returns the md5 and sha256 if file valid and exists' do
stub_pub_file stub_pub_file(pub_file)
result = subject.settings[:ssh_algorithms_hashes].select { |o| o[:md5] == md5 && o[:sha256] == sha256 } result = subject.settings[:ssh_algorithms_hashes].select { |o| o[:md5] == md5 && o[:sha256] == sha256 }
expect(result.size).to eq(InstanceConfiguration::SSH_ALGORITHMS.size) expect(result.size).to eq(InstanceConfiguration::SSH_ALGORITHMS.size)
end end
def stub_pub_file(exist: true) def pub_file(exist: true)
path = exist ? 'spec/fixtures/ssh_host_example_key.pub' : 'spec/fixtures/ssh_host_example_key.pub.random' path = exist ? 'spec/fixtures/ssh_host_example_key.pub' : 'spec/fixtures/ssh_host_example_key.pub.random'
allow(subject).to receive(:ssh_algorithm_file).and_return(Rails.root.join(path)) Rails.root.join(path)
end
def stub_pub_file(path)
allow(subject).to receive(:ssh_algorithm_file).and_return(path)
end end
end end
......
...@@ -60,9 +60,7 @@ RSpec.describe Clusters::Aws::FetchCredentialsService do ...@@ -60,9 +60,7 @@ RSpec.describe Clusters::Aws::FetchCredentialsService do
subject { described_class.new(provision_role, provider: provider).execute } subject { described_class.new(provision_role, provider: provider).execute }
before do before do
allow(File).to receive(:read) stub_file_read(Rails.root.join('vendor', 'aws', 'iam', 'eks_cluster_read_only_policy.json'), content: session_policy)
.with(Rails.root.join('vendor', 'aws', 'iam', 'eks_cluster_read_only_policy.json'))
.and_return(session_policy)
end end
it { is_expected.to eq assumed_role_credentials } it { is_expected.to eq assumed_role_credentials }
......
...@@ -42,9 +42,7 @@ RSpec.describe Clusters::Aws::ProvisionService do ...@@ -42,9 +42,7 @@ RSpec.describe Clusters::Aws::ProvisionService do
allow(provider).to receive(:api_client) allow(provider).to receive(:api_client)
.and_return(client) .and_return(client)
allow(File).to receive(:read) stub_file_read(Rails.root.join('vendor', 'aws', 'cloudformation', 'eks_cluster.yaml'), content: cloudformation_template)
.with(Rails.root.join('vendor', 'aws', 'cloudformation', 'eks_cluster.yaml'))
.and_return(cloudformation_template)
end end
it 'updates the provider status to :creating and configures the provider with credentials' do it 'updates the provider status to :creating and configures the provider with credentials' do
......
...@@ -146,7 +146,7 @@ RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memor ...@@ -146,7 +146,7 @@ RSpec.describe Metrics::Dashboard::CloneDashboardService, :use_clean_rails_memor
it 'extends dashboard template path to absolute url' do it 'extends dashboard template path to absolute url' do
allow(::Files::CreateService).to receive(:new).and_return(double(execute: { status: :success })) allow(::Files::CreateService).to receive(:new).and_return(double(execute: { status: :success }))
expect(File).to receive(:read).with(Rails.root.join('config/prometheus/common_metrics.yml')).and_return('') expect_file_read(Rails.root.join('config/prometheus/common_metrics.yml'), content: '')
service_call service_call
end end
......
...@@ -134,6 +134,7 @@ RSpec.configure do |config| ...@@ -134,6 +134,7 @@ RSpec.configure do |config|
config.include NextFoundInstanceOf config.include NextFoundInstanceOf
config.include NextInstanceOf config.include NextInstanceOf
config.include TestEnv config.include TestEnv
config.include FileReadHelpers
config.include Devise::Test::ControllerHelpers, type: :controller config.include Devise::Test::ControllerHelpers, type: :controller
config.include Devise::Test::IntegrationHelpers, type: :feature config.include Devise::Test::IntegrationHelpers, type: :feature
config.include LoginHelpers, type: :feature config.include LoginHelpers, type: :feature
......
# frozen_string_literal: true
module FileReadHelpers
def stub_file_read(file, content: nil, error: nil)
allow_original_file_read
expectation = allow(File).to receive(:read).with(file)
if error
expectation.and_raise(error)
elsif content
expectation.and_return(content)
else
expectation
end
end
def expect_file_read(file, content: nil, error: nil)
allow_original_file_read
expectation = expect(File).to receive(:read).with(file)
if error
expectation.and_raise(error)
elsif content
expectation.and_return(content)
else
expectation
end
end
def expect_file_not_to_read(file)
allow_original_file_read
expect(File).not_to receive(:read).with(file)
end
private
def allow_original_file_read
# Don't set this mock twice, otherwise subsequent calls will clobber
# previous mocks
return if @allow_original_file_read
@allow_original_file_read = true
allow(File).to receive(:read).and_call_original
end
end
...@@ -46,7 +46,7 @@ RSpec.shared_examples 'refreshes cache when dashboard_version is changed' do ...@@ -46,7 +46,7 @@ RSpec.shared_examples 'refreshes cache when dashboard_version is changed' do
allow(service).to receive(:dashboard_version).and_return('1', '2') allow(service).to receive(:dashboard_version).and_return('1', '2')
end end
expect(File).to receive(:read).twice.and_call_original expect_file_read(Rails.root.join(described_class::DASHBOARD_PATH)).twice.and_call_original
service = described_class.new(*service_params) service = described_class.new(*service_params)
......
...@@ -129,7 +129,7 @@ RSpec.describe 'gitlab:db namespace rake task' do ...@@ -129,7 +129,7 @@ RSpec.describe 'gitlab:db namespace rake task' do
let(:output) { StringIO.new } let(:output) { StringIO.new }
before do before do
allow(File).to receive(:read).with(structure_file).and_return(input) stub_file_read(structure_file, content: input)
allow(File).to receive(:open).with(structure_file, any_args).and_yield(output) allow(File).to receive(:open).with(structure_file, any_args).and_yield(output)
end end
......
# frozen_string_literal: true # frozen_string_literal: true
require_relative '../../../../tooling/lib/tooling/test_map_generator' require_relative '../../../../tooling/lib/tooling/test_map_generator'
require_relative '../../../support/helpers/file_read_helpers'
RSpec.describe Tooling::TestMapGenerator do RSpec.describe Tooling::TestMapGenerator do
include FileReadHelpers
subject { described_class.new } subject { described_class.new }
describe '#parse' do describe '#parse' do
...@@ -39,8 +42,8 @@ RSpec.describe Tooling::TestMapGenerator do ...@@ -39,8 +42,8 @@ RSpec.describe Tooling::TestMapGenerator do
let(:pathname) { instance_double(Pathname) } let(:pathname) { instance_double(Pathname) }
before do before do
allow(File).to receive(:read).with('yaml1.yml').and_return(yaml1) stub_file_read('yaml1.yml', content: yaml1)
allow(File).to receive(:read).with('yaml2.yml').and_return(yaml2) stub_file_read('yaml2.yml', content: yaml2)
end end
context 'with single yaml' do context 'with single yaml' do
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment