gitlab-ce/spec/scripts/cells/application_settings_analys...

270 lines
9.1 KiB
Ruby

# frozen_string_literal: true
require 'fast_spec_helper'
require 'gitlab/rspec/all'
require_relative '../../../scripts/cells/application-settings-analysis'
RSpec.describe ApplicationSettingsAnalysis, feature_category: :tooling do
let(:stdout) { StringIO.new }
subject(:analyzer) { described_class.new(stdout: stdout) }
describe described_class::ApplicationSetting do
let(:attr) { 'default_branch_name' }
let(:definition_file_path) { File.expand_path("../../../config/application_setting_columns/#{attr}.yml", __dir__) }
let(:definition_file_exist) { true }
let(:definition) { { column: 'default_branch_name', db_type: 'fake type', clusterwide: true } }
subject(:application_setting) do
described_class.new(column: 'default_branch_name', db_type: 'text', clusterwide: true)
end
before do
allow(File).to receive(:exist?).and_call_original
allow(File).to receive(:exist?).with(definition_file_path).and_return(definition_file_exist)
allow(YAML).to receive(:safe_load_file).with(definition_file_path).and_return(definition)
end
describe '#initialize' do
it 'does not override codebase fields' do
expect(application_setting.column).to eq('default_branch_name')
expect(application_setting.db_type).to eq('text')
end
context 'when definition file does not' do
let(:definition_file_exist) { false }
it 'is not a problem' do
expect { application_setting }.not_to raise_error
end
end
end
describe '#definition_file_path' do
it 'returns the definition file path' do
expect(application_setting.definition_file_path)
.to eq(File.expand_path("../../../config/application_setting_columns/#{attr}.yml", __dir__))
end
end
describe '#definition_file_exist?' do
it 'returns true when the definition file exists' do
expect(File).to receive(:exist?).with(definition_file_path).and_return(definition_file_exist)
expect(application_setting.definition_file_exist?).to eq(definition_file_exist)
end
end
end
describe '#attributes' do
subject(:attributes) { analyzer.attributes }
describe 'return value' do
it 'returns an array of described_class::ApplicationSetting' do
expect(attributes).to all(be_a(described_class::ApplicationSetting))
end
end
describe 'non-encrypted attribute' do
it 'returns non-encrypted attributes from db/structure.sql' do
setting = attributes.find { |attr| attr.column == 'default_branch_name' }
expect(setting).to be_present
end
end
describe 'DB type' do
it 'stores the column type from db/structure.sql' do
setting = attributes.find { |attr| attr.column == 'snippet_size_limit' }
expect(setting.db_type).to eq('bigint')
end
end
describe 'API type' do
it 'fetches the API type from doc/api/settings.md' do
setting = attributes.find { |attr| attr.column == 'snippet_size_limit' }
expect(setting.api_type).to eq('integer')
end
end
describe 'encrypts columns' do
let(:attribute) { 'default_branch_protection_defaults' } # use this field since it's an actual JSONB field
let(:ee_attribute) { 'future_subscriptions' } # use this field since it's an actual JSONB field
let(:fake_application_setting_file) do
<<~CLASS
class ApplicationSetting < ApplicationRecord
encrypts :#{attribute}
end
CLASS
end
let(:fake_ee_application_setting_file) do
<<~MODULE
module EE
module ApplicationSetting
extend ActiveSupport::Concern
extend ::Gitlab::Utils::Override
prepended do
encrypts :#{ee_attribute}
end
end
end
MODULE
end
before do
stub_const(
"#{described_class}::ApplicationSetting::AS_MODEL",
fake_application_setting_file + fake_ee_application_setting_file
)
end
it 'returns encrypted attribute columns from the model' do
setting = attributes.find { |attr| attr.column == attribute }
ee_setting = attributes.find { |attr| attr.column == ee_attribute }
expect(setting.attr).to eq(attribute)
expect(setting.encrypted).to be(true)
expect(ee_setting.attr).to eq(ee_attribute)
expect(ee_setting.encrypted).to be(true)
end
end
describe 'attr_encrypted columns' do
it 'returns encrypted attribute columns from db/structure.sql' do
setting = attributes.find { |attr| attr.column == 'encrypted_external_auth_client_key' }
encryption_iv_column = attributes
.find { |attr| attr.column == 'encrypted_external_auth_client_key_iv' }
expect(setting.attr).to eq('external_auth_client_key') # `encrypted_` prefix is removed
expect(setting.encrypted).to be(true)
expect(encryption_iv_column).to be_nil # `*_iv` column aren't listed as it's an implementation detail
end
end
describe 'TokenAuthenticatable columns' do
it 'returns encrypted attribute columns from db/structure.sql' do
setting = attributes.find { |attr| attr.column == 'runners_registration_token_encrypted' }
expect(setting.attr).to eq('runners_registration_token') # `_encrypted` suffix is removed
expect(setting.encrypted).to be(true)
end
end
describe 'column `not null`' do
it 'stores the column `not null` from db/structure.sql' do
setting = attributes.find { |attr| attr.column == 'snippet_size_limit' }
expect(setting.not_null).to be(true)
end
end
describe 'column default' do
it 'stores the column default from db/structure.sql' do
setting = attributes.find { |attr| attr.column == 'snippet_size_limit' }
expect(setting.default).to eq('52428800')
end
end
describe 'attributes different than default on GitLab.com' do
it 'marks settings that have a different value than default set on GitLab.com' do
setting = attributes.find { |attr| attr.column == 'zoekt_settings' }
expect(setting.gitlab_com_different_than_default).to be(true)
end
end
describe 'attribute description' do
it 'fetches attribute description from doc/api/settings.md' do
setting = attributes.find { |attr| attr.column == 'commit_email_hostname' }
expect(setting.description).to eq('Custom hostname (for private commit emails).')
end
end
describe 'JiHu-specific columns' do
it 'fetches JiHu-specific columns from db/structure.sql' do
setting = attributes.find { |attr| attr.column == 'content_validation_endpoint_url' }
expect(setting.jihu).to be(true)
end
end
describe 'HTML caching column' do
it 'does not return _html-suffixed columns from db/structure.sql' do
setting = attributes.find { |attr| attr.column.end_with?('_html') }
expect(setting).to be_nil
end
end
describe 'definition file' do
it 'returns true when an attribute has an existing definition file' do
setting = attributes.find { |attr| attr.column == 'commit_email_hostname' }
expect(setting.definition_file_exist?).to be(true)
end
end
end
describe '#execute' do
before do
allow(File).to receive(:write)
allow(stdout).to receive(:puts)
end
it 'works without issues' do
analyzer.execute
end
context 'when API type is not compatible with DB type' do
let(:documentation_api_settings) do
[described_class::ApplicationSettingApiDoc.new(attr: 'default_branch_name', api_type: 'integer')]
end
before do
allow(analyzer).to receive(:documentation_api_settings).and_return(documentation_api_settings)
end
it 'raises an error' do
expect { analyzer.execute }.to raise_error("`default_branch_name`: Documented type `integer` " \
"isn't compatible with actual DB type `text`!")
end
end
context 'when a definition file exists for an attribute that does not exist anymore' do
let(:fake_attribute_definition_file_path) do
File.expand_path("../../../config/application_setting_columns/fake_attribute.yml", __dir__)
end
before do
allow(File).to receive(:unlink).and_call_original
end
it 'deletes the definition file' do
# #definition_files is memoized so the stub it directly
allow(described_class).to receive(:definition_files).and_return([fake_attribute_definition_file_path])
expect(stdout).to receive(:puts).with(
"Deleting #{fake_attribute_definition_file_path} since the fake_attribute attribute doesn't exist anymore."
)
expect(File).to receive(:unlink).with(fake_attribute_definition_file_path).and_return(1)
analyzer.execute
end
end
end
describe '.definition_files' do
it 'returns all definition files' do
expect(described_class.definition_files).to eq(
Dir.glob(File.expand_path("../../../config/application_setting_columns/*.yml", __dir__))
)
end
end
end