Skip to content

Commit

Permalink
Merge pull request #689 from bugsnag/hackathon-test-harness
Browse files Browse the repository at this point in the history
Hackathon test harness
  • Loading branch information
sazap10 authored Oct 18, 2024
2 parents 3cdeab2 + 7554071 commit cb45852
Show file tree
Hide file tree
Showing 11 changed files with 172 additions and 23 deletions.
35 changes: 24 additions & 11 deletions lib/features/steps/pipeline_event_steps.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
# @!group Pipeline_event steps

# Checks to see if an event is available in the pipeline
Then('the event is available via the data access api') do
# Get event_id from... somewhere
Then('the last event is available via the data access api') do
event_id = Maze::Server.list_for('errors').current[:event_id]

raise Test::Unit::AssertionFailedError.new('Event id could not be found from last event') if event_id.nil?

# Check if the event exists in the pipeline_events list already
return if Maze::Server.pipeline_events.any? { |event| event[:event_id].eql?(event_id) }
return if Maze::Server.pipeline_events.all.any? { |event| event[:event_id].eql?(event_id) }

# if not, attempt to get the event via the data access api
event = get_event_from_api(event_id)
Expand All @@ -16,8 +19,12 @@
end
end

Then('the event is not available via the data access api') do
if Maze::Server.pipeline_events.any? { |event| event[:event_id].eql?(event_id) }
Then('the last event is not available via the data access api') do
event_id = Maze::Server.list_for('errors').current[:event_id]

raise Test::Unit::AssertionFailedError.new('Event id could not be found from last event') if event_id.nil?

if Maze::Server.pipeline_events.all.any? { |event| event[:event_id].eql?(event_id) }
raise Test::Unit::AssertionFailedError.new <<-MESSAGE
Event with id #{event_id} already exists in the events pulled via data access api
MESSAGE
Expand All @@ -36,17 +43,23 @@
def get_event_from_api(event_id)
wait = Maze::Wait.new(interval: 3, timeout: 15)
received_event = wait.until do
event = data_access_api.get_event(event_id)
# Probably needs some error handling somewhere
if event.has_key?('errors')
begin
event = data_access_api.get_event(event_id)
# Probably needs some error handling somewhere
if event.has_key?('errors')
false
else
event
end
rescue => e
pp "Pipeline-event GET Debug: #{e.class} #{e.message}"
false
else
event
end
end
if received_event
# This sucks
Maze::Server.pipeline_events.add({
body: JSON.parse(received_event),
body: JSON.parse(received_event.to_h.to_json),
request: received_event,
event_id: event_id
})
Expand Down
12 changes: 12 additions & 0 deletions lib/features/steps/setup_steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# @!group Setup steps

# Creates a new project in bugsnag
Given('I create a new project {string} with type {string}') do |name, type|
org_id = data_access_api.get_first_org_id
project = data_access_api.create_project(org_id, name, type)
project_id = project['id']
data_access_api.set_project_id(project_id)
api_key = data_access_api.get_project_api_key(project_id)
Maze.config.bugsnag_repeater_api_key = api_key
Maze.config.bugsnag_data_access_project_id = project_id
end
49 changes: 38 additions & 11 deletions lib/maze/client/bugsnag/data_access_api.rb
Original file line number Diff line number Diff line change
Expand Up @@ -6,26 +6,53 @@ module Bugsnag
# An abstraction for the underlying Bugsnag data access
class DataAccessApi
# @param api_key The Bugsnag API key
# @param endpoint The endpoint to use for the Bugsnag Data Access API#
# @param endpoint The endpoint to use for the Bugsnag Data Access API
# @param project_id The project ID to use for the Bugsnag Data Access API
def initialize
@api_key = Maze.config.bugsnag_data_access_api_key
@auth_token = Maze.config.bugsnag_data_access_api_key
@endpoint = Maze.config.bugsnag_data_access_api_endpoint
@project_id = Maze.config.bugsnag_data_access_project_id
opts = {
api_key: @api_key,
endpoint: @endpoint,
project_id: @project_id
}
if @endpoint.start_with?('http')
opts.connection_options = { ssl: { verify: false } }
end
@client = Bugsnag::Api::Client.new(opts)
@client = create_client(auth_token: @auth_token, endpoint: @endpoint, project_id: @project_id)
end

def set_project_id(project_id)
@project_id = project_id
@client = create_client(auth_token: @auth_token, endpoint: @endpoint, project_id: @project_id)
end

def get_event(event_id)
@client.event(@project_id, event_id)
end

def create_project(org_id, name, type)
@client.create_project(org_id, name, type)
end

def get_project_api_key(project_id)
project = @client.project(project_id)
project['api_key']
end

def get_first_org_id
orgs = @client.organizations
orgs.first['id']
end

private

def create_client(auth_token:, endpoint:, project_id:, opts: {})
opts[:auth_token] = auth_token
opts[:endpoint] = endpoint
opts[:project_id] = project_id
if endpoint.start_with?('http')
opts[:connection_options] = {
ssl: {
verify: false
}
}
end
::Bugsnag::Api::Client.new(opts)
end
end
end
end
Expand Down
2 changes: 1 addition & 1 deletion lib/maze/maze_output.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def write_requests
path = output_folder
FileUtils.makedirs(path)

request_types = %w[errors sessions builds uploads logs sourcemaps traces invalid reflections pipeline_events]
request_types = %w[errors sessions builds uploads logs sourcemaps traces invalid reflections]
request_types << 'sampling requests'

request_types.each do |request_type|
Expand Down
4 changes: 4 additions & 0 deletions lib/maze/option/processor.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def populate(config, options)
config.aspecto_repeater_api_key = options[Maze::Option::ASPECTO_REPEATER_API_KEY]
config.bugsnag_repeater_api_key = options[Maze::Option::BUGSNAG_REPEATER_API_KEY]

config.bugsnag_data_access_api_endpoint = options[Maze::Option::BUGSNAG_DATA_ACCESS_API_ENDPOINT]
config.bugsnag_data_access_api_key = options[Maze::Option::BUGSNAG_DATA_ACCESS_API_KEY]
config.bugsnag_data_access_project_id = options[Maze::Option::BUGSNAG_DATA_ACCESS_PROJECT_ID]

# Document server options
config.document_server_root = options[Maze::Option::DS_ROOT]
config.document_server_bind_address = options[Maze::Option::DS_BIND_ADDRESS]
Expand Down
6 changes: 6 additions & 0 deletions test/fixtures/pipeline-events/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
ARG BRANCH_NAME
ARG RUBY_VERSION
FROM 855461928731.dkr.ecr.us-west-1.amazonaws.com/maze-runner:${BRANCH_NAME}-ci-ruby-${RUBY_VERSION}

COPY . /app
WORKDIR /app
4 changes: 4 additions & 0 deletions test/fixtures/pipeline-events/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
source "https://rubygems.org"

gem 'bugsnag-maze-runner', path: '../../..'
gem 'bugsnag', '>= 6.0.0'
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
Feature: We can create a project, send an event to the new project and verify
that the event is available via the data access api

Scenario: We can create a project, send an event to the new project and verify
Given I create a new project "My Project" with type "ruby"
When I send a request to the server
Then I wait to receive an error
And the last event is available via the data access api
And the pipeline event payload field "exceptions.0.message" equals "This is an error"
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Feature: We can pull processed requests from Bugsnag Development

Scenario: We can pull a processed event from the pipeline
When I send a request to the server
Then I wait to receive an error
And the last event is available via the data access api
And the pipeline event payload field "exceptions.0.message" equals "This is an error"
60 changes: 60 additions & 0 deletions test/fixtures/pipeline-events/features/steps/steps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
When('I send a request to the server') do
test_payload = JSON.generate({
apiKey: Maze.config.bugsnag_repeater_api_key,
notifier: {
name: 'Ruby Bugsnag Notifier',
version: '6.27.1',
url: 'https://www.bugsnag.com'
},
payloadVersion: '4.0',
events: [
{
app: { version: nil, releaseStage: nil, type: nil },
breadcrumbs: [],
device: {
hostname: 'SBUK62MMD6T.local',
runtimeVersions: { ruby: '3.3.5' },
time: '2024-10-17T14:27:33.829Z'
},
exceptions: [
{
errorClass: 'RuntimeError',
message: 'This is an error',
stacktrace: [
{
lineNumber: 435,
file: 'gems/bugsnag-6.27.1/lib/bugsnag/report.rb',
method: 'block in generate_exception_list',
code: {
'432': ' {',
'433': ' errorClass: class_name,',
'434': ' message: error_message(exception, class_name),',
'435': ' stacktrace: Stacktrace.process(exception.backtrace, configuration)',
'436': ' }',
'437': ' end',
'438': ' end'
}
}
]
}
],
featureFlags: [],
metaData: {},
severity: 'warning',
severityReason: { type: 'handledException' },
unhandled: false,
user: {}
}
]
})

http = Net::HTTP.new('localhost', '9339')
request = Net::HTTP::Post.new('/notify')
request['content-type'] = 'application/json'
request['bugsnag-api-key'] = Maze.config.bugsnag_repeater_api_key
request['bugsnag-payload-version'] = '4.0'
request['bugsnag-sent-at'] = Time.now.utc.iso8601
request.body = test_payload

http.request(request)
end
7 changes: 7 additions & 0 deletions test/fixtures/pipeline-events/features/support/env.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Maze.config.enable_bugsnag = false

BeforeAll do
Maze.config.enforce_bugsnag_integrity = false
end


0 comments on commit cb45852

Please sign in to comment.