spec/elastic_apm/config_spec.rb (246 lines of code) (raw):

# Licensed to Elasticsearch B.V. under one or more contributor # license agreements. See the NOTICE file distributed with # this work for additional information regarding copyright # ownership. Elasticsearch B.V. licenses this file to you under # the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, # software distributed under the License is distributed on an # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY # KIND, either express or implied. See the License for the # specific language governing permissions and limitations # under the License. # frozen_string_literal: true require 'spec_helper' module ElasticAPM RSpec.describe Config do it 'has defaults' do config = Config.new expect(config.server_url).to eq 'http://localhost:8200' end it 'overwrites defaults' do config = Config.new(server_url: 'somewhere-else.com') expect(config.server_url).to eq 'somewhere-else.com' end it 'loads from config file' do config = Config.new(config_file: 'spec/fixtures/elastic_apm.yml') expect(config.server_url).to eq 'somewhere-config.com' end it 'loads from erb-styled config file' do config = Config.new(config_file: 'spec/fixtures/elastic_apm_erb.yml') expect(config.server_url).to eq 'somewhere-config.com' end it 'takes options from ENV' do with_env('ELASTIC_APM_SERVER_URL' => 'by-env!') do expect(Config.new.server_url).to eq 'by-env!' end end it 'converts certain env values to Ruby types' do [ # [ 'NAME', 'VALUE', 'EXPECTED' ] ['ELASTIC_APM_SOURCE_LINES_ERROR_APP_FRAMES', '666', 666], ['ELASTIC_APM_SOURCE_LINES_SPAN_APP_FRAMES', '666', 666], ['ELASTIC_APM_SOURCE_LINES_ERROR_LIBRARY_FRAMES', '666', 666], ['ELASTIC_APM_SOURCE_LINES_SPAN_LIBRARY_FRAMES', '666', 666], ['ELASTIC_APM_TRANSACTION_SAMPLE_RATE', '0.5', 0.5], ['ELASTIC_APM_VERIFY_SERVER_CERT', '1', true], ['ELASTIC_APM_VERIFY_SERVER_CERT', 'true', true], ['ELASTIC_APM_VERIFY_SERVER_CERT', '0', false], ['ELASTIC_APM_VERIFY_SERVER_CERT', 'false', false], ['ELASTIC_APM_DISABLE_INSTRUMENTATIONS', 'json,http', %w[json http]], [ 'ELASTIC_APM_DEFAULT_LABELS', 'wave=something erlking&brother=ok', { 'wave' => 'something erlking', 'brother' => 'ok' } ], [ 'ELASTIC_APM_GLOBAL_LABELS', 'why=hello goodbye,apples=oranges', { 'why' => 'hello goodbye', 'apples' => 'oranges' } ] ].each do |(key, val, expected)| with_env(key => val) do setting = key.gsub('ELASTIC_APM_', '').downcase expect(Config.new.send(setting.to_sym)).to eq expected end end end it 'can get config_file specified by env' do with_env('ELASTIC_APM_CONFIG_FILE' => 'spec/fixtures/elastic_apm.yml') do config = Config.new expect(config.config_file).to eq('spec/fixtures/elastic_apm.yml') expect(config.server_url).to eq('somewhere-config.com') end end context 'server_url' do context 'as a String with a trailing slash' do subject { Config.new(server_url: 'http://localhost:8200/') } it 'strips the trailing slash' do expect(subject.server_url).to eq('http://localhost:8200') end end context 'as a URI with a trailing slash' do subject { Config.new(server_url: URI('http://localhost:8200/')) } it 'strips the trailing slash' do expect(subject.server_url).to eq('http://localhost:8200') end end end context 'duration units' do subject do Config.new( api_request_time: '1m', span_frames_min_duration: '10ms' ) end its(:api_request_time) { should eq 60 } its(:span_frames_min_duration) { should eq 0.010 } context 'when no unit' do it 'uses default unit' do expect(Config.new(api_request_time: '4').api_request_time).to eq 4 expect( Config.new(span_frames_min_duration: '5').span_frames_min_duration ).to eq 0.005 end end end describe 'byte units' do context 'with a unit' do subject { Config.new(api_request_size: '500kb') } its(:api_request_size) { should eq 500 * 1024 } end context 'without a unit' do subject { Config.new(api_request_size: '1') } its(:api_request_size) { should eq 1024 } end end describe 'sample rate precision' do context 'sets a minimum' do subject { Config.new(transaction_sample_rate: '0.00001') } its(:transaction_sample_rate) { should eq 0.0001 } end context 'ensures max 4 digits of precision' do subject { Config.new(transaction_sample_rate: '0.55554') } its(:transaction_sample_rate) { should eq 0.5555 } end end it 'yields itself to a given block' do config = Config.new { |c| c.server_url = 'somewhere-else.com' } expect(config.server_url).to eq 'somewhere-else.com' end it 'has spies and may disable them' do expect(Config.new.available_instrumentations).to_not be_empty config = Config.new disable_instrumentations: ['json'] expect(config.enabled_instrumentations).to_not include('json') end context 'logging' do it "writes to stdout with '-'" do config = Config.new log_path: '-' expect($stdout).to receive(:write).with(/This test ran$/) config.logger.info 'This test ran' end it 'can write to a file' do Tempfile.open 'elastic-apm' do |file| config = Config.new log_path: file.path config.logger.info 'This test ran' expect(file.read).to match(/This test ran$/) end end it 'can be given a logger' do logger = double(Logger) config = Config.new logger: logger expect(logger).to receive(:info).with('MockLog') config.logger.info 'MockLog' end it 'overrides the logger if given a log_path' do Tempfile.open 'elastic-apm' do |file| logger = double(Logger) config = Config.new logger: logger, log_path: file.path config.logger.info 'This test ran' expect(file.read).to match(/This test ran$/) end end describe 'log level' do it 'can accept integers' do config = Config.new log_level: Logger::FATAL expect(config.log_level).to eq(Logger::FATAL) end it 'can accept symbols' do config = Config.new log_level: :fatal expect(config.log_level).to eq(Logger::FATAL) end it 'can accept symbols not mapping to native Ruby logger levels' do config = Config.new log_level: :critical expect(config.log_level).to eq(Logger::FATAL) end end describe 'ecs logging' do context "when old config option is used, with 'override'" do it 'builds an EcsLogging::Logger' do config = Config.new log_ecs_formatting: 'override' expect(config.logger).to be_a(::EcsLogging::Logger) end end context "when log_ecs_reformatting is 'override'" do it 'builds an EcsLogging::Logger' do config = Config.new log_ecs_reformatting: 'override' expect(config.logger).to be_a(::EcsLogging::Logger) end end context "when the log_ecs_reformatting value is not valid" do it 'builds a ::Logger' do config = Config.new log_ecs_reformatting: 'invalid_option' # Using be_a(::Logger) would be true even if the logger were # a EcsLogging::Logger because the class inherits from ::Logger. # So we test the negative: expect(config.logger).not_to be_a(::EcsLogging::Logger) end end end end describe 'unknown options' do it 'warns' do expect(subject).to receive(:warn).with(/Unknown option/) subject.unknown_option = 'whatever' end context 'from args' do it 'warns' do expect_any_instance_of(Config) .to receive(:warn).with(/Unknown option/) Config.new(unknown_key: true) end end context 'from config_file' do it 'warns' do expect_any_instance_of(Config) .to receive(:warn).with(/Unknown option/) Config.new(config_file: 'spec/fixtures/unknown_option.yml') end end end context 'boolean values' do subject { Config.new } it 'allows false to be set' do subject.capture_headers = false expect(subject.capture_headers).to eq(false) expect(subject.capture_headers?).to eq(false) end end describe '#replace_options' do subject { Config.new(server_url: 'somewhere-else.com') } it 'replaces the option values' do subject.replace_options(api_request_time: '1s', api_buffer_size: 100) expect(subject.api_request_time).to eq(1) expect(subject.api_buffer_size).to eq(100) end it 'leaves existing config values unchanged' do subject.replace_options(api_request_time: '1s') expect(subject.server_url).to eq('somewhere-else.com') end it 'replaces the options object' do original_options = subject.options subject.replace_options(api_request_time: '1s') expect(subject.options).not_to be(original_options) end it 'does not update the log level on the existing logger' do subject.replace_options(log_level: Logger::DEBUG) expect(subject.logger.level).to eq(Logger::INFO) end end describe "#version" do it "has version 0 if the server does not respond" do WebMock.stub_request(:get, "http://localhost:8200/") .to_return(status: 404, body: "") expect(Config.new.version).to eq '0' end it "returns the version from the server" do WebMock.stub_request(:get, "http://localhost:8200/") .to_return(status: 200, body: '{"version": 8.5}') expect(Config.new.version).to eq '8.5' end end end end