Skip to content
This repository has been archived by the owner on Feb 16, 2024. It is now read-only.

Commit

Permalink
[ch69519] Implement tests.
Browse files Browse the repository at this point in the history
  • Loading branch information
recurser committed Aug 31, 2020
1 parent 8e57868 commit 877214f
Show file tree
Hide file tree
Showing 11 changed files with 397 additions and 18 deletions.
1 change: 0 additions & 1 deletion lib/trailer/recorder.rb
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ def start
def write(data)
raise Trailer::Error, 'start() must be called before write()' if @trace_id.nil?
raise Trailer::Error, 'data must be an instance of Hash' unless data.is_a?(Hash)
raise Trailer::Error, 'could not convert data to JSON' unless data.respond_to?(:to_json)

# Include some standard tags.
data[:environment] ||= Trailer.config.environment
Expand Down
20 changes: 10 additions & 10 deletions lib/trailer/storage/cloud_watch.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,16 +16,6 @@ def initialize
ensure_log_stream
end

# Queues the given hash for writing to CloudWatch.
#
# @param data [Hash] A key-value hash of trace data to write to storage.
def write(data)
messages << {
timestamp: (Time.now.utc.to_f.round(3) * 1000).to_i,
message: data&.to_json,
}.compact
end

# Sends all of the queued messages to CloudWatch, and resets the messages queue.
#
# See https://stackoverflow.com/a/36901509
Expand All @@ -49,6 +39,16 @@ def flush
retry
end

# Queues the given hash for writing to CloudWatch.
#
# @param data [Hash] A key-value hash of trace data to write to storage.
def write(data)
messages << {
timestamp: (Time.now.utc.to_f.round(3) * 1000).to_i,
message: data&.to_json,
}.compact
end

private

attr_accessor :client, :messages, :sequence_token
Expand Down
17 changes: 17 additions & 0 deletions lib/trailer/storage/null.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# frozen_string_literal: true

module Trailer
module Storage
class Null
include Concurrent::Async

# Pretends to queue the given hash for writing.
#
# @param data [Hash] A key-value hash of trace data to write to storage.
def write(_data); end

# Pretends to flush the queued messages to the storage provider.
def flush; end
end
end
end
10 changes: 10 additions & 0 deletions spec/spec_helper.rb
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
# frozen_string_literal: true

require 'bundler/setup'
require 'request_store'
require 'trailer'
require 'trailer/storage/null'

RSpec.configure do |config|
# Enable flags like --only-failures and --next-failure
Expand All @@ -13,4 +15,12 @@
config.expect_with :rspec do |cnf|
cnf.syntax = :expect
end

config.before do
Trailer.configure do |cnf|
cnf.enabled = true
cnf.storage = Trailer::Storage::Null
end
RequestStore.store[:trailer] = nil
end
end
8 changes: 7 additions & 1 deletion spec/trailer/configuration_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# frozen_string_literal: true

RSpec.describe Trailer::Configuration do
it 'has tests'
describe 'initialize' do
subject { described_class.new }

it 'sets some default values' do
expect(subject.aws_region).to eq 'us-east-1'
end
end
end
47 changes: 46 additions & 1 deletion spec/trailer/middleware/rack_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,50 @@
# frozen_string_literal: true

require 'rack'
require 'trailer/middleware/rack'

RSpec.describe Trailer::Middleware::Rack do
it 'has tests'
let(:app) { ->(_env) { [200, { 'Content-Type' => 'text/plain' }, ['OK']] } }
let(:subject) { described_class.new(app) }
let(:request) { ::Rack::MockRequest.new(subject) }

describe '#call' do
context 'trailer enabled' do
it 'instantiates a trailer instance' do
expect(RequestStore.store[:trailer]).to be_nil
request.get('/')
expect(RequestStore.store[:trailer]).to be_a(Trailer::Recorder)
end

it 'starts and finishes a trace' do
trailer = RequestStore.store[:trailer] = Trailer.new
allow(trailer).to receive(:start)
allow(trailer).to receive(:finish)
request.get('/')
expect(trailer).to have_received(:start).ordered
expect(trailer).to have_received(:finish).ordered
end

it 'records exceptions' do
err = StandardError.new('something went wrong')
trailer = RequestStore.store[:trailer] = Trailer.new
allow(trailer).to receive(:start).and_raise(err)
allow(trailer).to receive(:add_exception)
expect do
request.get('/')
end.to raise_exception(err)
expect(trailer).to have_received(:add_exception).with(err)
end
end

context 'trailer disabled' do
it 'does not instantiate a trailer instance' do
Trailer.configure { |config| config.enabled = false }
allow(Trailer).to receive(:new)
request.get('/')
expect(RequestStore.store[:trailer]).to be_nil
expect(Trailer).not_to have_received(:new)
end
end
end
end
46 changes: 45 additions & 1 deletion spec/trailer/middleware/sidekiq_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,49 @@
# frozen_string_literal: true

RSpec.describe Trailer::Middleware::Sidekiq do
it 'has tests'
subject { described_class.new }

def call!
subject.call(double, double, double) { true }
end

describe '#call' do
context 'trailer enabled' do
it 'instantiates a trailer instance' do
expect(RequestStore.store[:trailer]).to be_nil
call!
expect(RequestStore.store[:trailer]).to be_a(Trailer::Recorder)
end

it 'starts and finishes a trace' do
trailer = RequestStore.store[:trailer] = Trailer.new
allow(trailer).to receive(:start)
allow(trailer).to receive(:finish)
call!
expect(trailer).to have_received(:start).ordered
expect(trailer).to have_received(:finish).ordered
end

it 'records exceptions' do
err = StandardError.new('something went wrong')
trailer = RequestStore.store[:trailer] = Trailer.new
allow(trailer).to receive(:start).and_raise(err)
allow(trailer).to receive(:add_exception)
expect do
call!
end.to raise_exception(err)
expect(trailer).to have_received(:add_exception).with(err)
end
end

context 'trailer disabled' do
it 'does not instantiate a trailer instance' do
Trailer.configure { |config| config.enabled = false }
allow(Trailer).to receive(:new)
call!
expect(RequestStore.store[:trailer]).to be_nil
expect(Trailer).not_to have_received(:new)
end
end
end
end
91 changes: 90 additions & 1 deletion spec/trailer/recorder_spec.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,94 @@
# frozen_string_literal: true

RSpec.describe Trailer::Recorder do
it 'has tests'
subject { described_class.new(storage) }

let(:storage) { instance_double(Trailer::Storage::Null) }

before do
Trailer.configure do |config|
config.environment = 'test'
config.host_name = 'web.1'
config.service_name = 'studio'
end
allow(Trailer.config).to receive(:storage).and_return(storage)
allow(storage).to receive(:async).and_return(storage)
subject.start
end

def trace_id
subject.send(:trace_id)
end

describe '#add_exception' do
it 'writes the exception to storage' do
err = StandardError.new('something went wrong')
backtrace = ['line one']
allow(err).to receive(:backtrace).and_return(backtrace)
allow(storage).to receive(:write)
subject.add_exception(err)
expected = { environment: 'test',
exception: 'StandardError',
host_name: 'web.1',
service_name: 'studio',
message: 'something went wrong',
trace: backtrace,
trace_id: trace_id }
expect(storage).to have_received(:write).with(expected)
end
end

describe '#finish' do
it 'flushes data to storage' do
allow(storage).to receive(:flush)
expect(subject.send(:trace_id)).not_to be_nil
subject.finish
expect(storage).to have_received(:flush)
expect(trace_id).to be_nil
end
end

describe '#start' do
it 'raises an error if the previous trace has not finished' do
expect do
subject.start
end.to raise_exception(Trailer::Error, 'finish() must be called before a new trace can be started')
end

it 'starts a new trace' do
subject.instance_variable_set(:@trace_id, nil)
subject.start
expect(trace_id).not_to be_nil
end
end

describe '#write' do
let(:data) { { some: :data } }

it 'raises an error if the trace has not started' do
subject.instance_variable_set(:@trace_id, nil)
expect do
subject.write(data)
end.to raise_exception(Trailer::Error, 'start() must be called before write()')
end

it 'raises an error if invalid data is passed' do
expect do
subject.write('invalid')
end.to raise_exception(Trailer::Error, 'data must be an instance of Hash')
end

it 'writes the data to storage' do
allow(storage).to receive(:write)
subject.write(data)
expected = {
environment: 'test',
host_name: 'web.1',
service_name: 'studio',
some: :data,
trace_id: trace_id,
}
expect(storage).to have_received(:write).with(expected)
end
end
end
Loading

0 comments on commit 877214f

Please sign in to comment.