test/unit/client_test.rb in raygun4ruby-3.2.3 vs test/unit/client_test.rb in raygun4ruby-3.2.4

- old
+ new

@@ -1,792 +1,792 @@ -# -*- coding: utf-8 -*- -require_relative "../test_helper.rb" -require 'stringio' - -class ClientTest < Raygun::UnitTest - - class TestException < StandardError - def initialize(message = nil) - super(message || "A test message") - end - end - - class NilMessageError < StandardError - def message - nil - end - end - - class FakeActionDispatcherIp - attr_reader :ip - def initialize remote_ip - @ip = remote_ip - end - def to_s - return ip - end - end - - def setup - super - @client = Raygun::Client.new - Raygun.configuration.record_raw_data = true - fake_successful_entry - - # Force NZ time zone for utcOffset tests - ENV['TZ'] = 'UTC-13' - end - - def test_record_breadcrumb_does_not_crash_without_initialized_store - Raygun.record_breadcrumb( - message: 'aliens', - category: 'exceptions', - level: :info - ) - end - - def test_api_key_required_message - Raygun.configuration.api_key = nil - - $stderr.expects(:puts).with(Raygun::Client::NO_API_KEY_MESSAGE).once - second_client = Raygun::Client.new - end - - def test_track_exception - response = Raygun.track_exceptions do - raise TestException.new - end - - assert response.success? - end - - def test_error_details - assert_equal exception_hash, @client.send(:error_details, test_exception) - end - - def test_error_details_with_nil_message - e = NilMessageError.new - expected_message = "" - assert_equal expected_message, @client.send(:error_details, e)[:message] - end - - def test_utc_offset - expected = 13 - - assert_equal expected, @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details][:environment][:utcOffset] - end - - def test_inner_error_details - oe = TestException.new("A test message") - oe.set_backtrace(["/some/folder/some_file.rb:123:in `some_method_name'"]) - - ie = TestException.new("Inner test message") - ie.set_backtrace(["/another/path/foo.rb:1234:in `block (3 levels) run'"]) - - e = nest_exceptions(oe, ie) - - expected_hash = { - className: "ClientTest::TestException", - message: oe.message, - stackTrace: [ - { lineNumber: "123", fileName: "/some/folder/some_file.rb", methodName: "some_method_name" } - ] - } - - # test inner error according with its availability (ruby >= 2.1) - if e.respond_to? :cause - expected_hash[:innerError] = { - className: "ClientTest::TestException", - message: ie.message, - stackTrace: [ - { lineNumber: "1234", fileName: "/another/path/foo.rb", methodName: "block (3 levels) run"} - ] - } - end - - assert_equal expected_hash, @client.send(:error_details, e) - end - - def test_client_details - expected_hash = { - name: Raygun::CLIENT_NAME, - version: Raygun::VERSION, - clientUrl: Raygun::CLIENT_URL - } - - assert_equal expected_hash, @client.send(:client_details) - end - - - def test_version - Raygun.setup do |config| - config.version = 123 - end - - assert_equal 123, @client.send(:version) - end - - def test_affected_user - test_env = { "raygun.affected_user" => { :identifier => "somepooruser@yourapp.com" } } - expected_hash = test_env["raygun.affected_user"] - - assert_equal expected_hash, @client.send(:build_payload_hash, test_exception, test_env)[:details][:user] - end - - def test_tags - configuration_tags = %w{alpha beta gaga} - explicit_env_tags = %w{one two three four} - rack_env_tag = %w{test} - - Raygun.setup do |config| - config.tags = configuration_tags - end - - test_env = { tags: explicit_env_tags } - expected_tags = configuration_tags + explicit_env_tags + rack_env_tag - - assert_equal expected_tags, @client.send(:build_payload_hash, test_exception, test_env)[:details][:tags] - end - - def test_tags_with_proc - configuration_tags = %w{bar} - explicit_env_tags = %w{one two three four} - rack_env_tag = %w{test} - - Raygun.setup do |config| - config.tags = ->(exception, env) { - [env[:foo]] - } - end - - test_env = { tags: explicit_env_tags, foo: 'bar' } - expected_tags = configuration_tags + explicit_env_tags + rack_env_tag - - assert_equal expected_tags, @client.send(:build_payload_hash, test_exception, test_env)[:details][:tags] - end - - def test_hostname - assert_equal Socket.gethostname, @client.send(:hostname) - end - - def test_unicode - e = TestException.new('日本語のメッセージ です') - - assert_silent { @client.track_exception(e) } - end - - def test_bad_encoding - bad_message = (100..1000).to_a.pack('c*').force_encoding('utf-8') - bad_exception = TestException.new(bad_message) - - assert !bad_message.valid_encoding? - assert_silent { @client.track_exception(bad_exception) } - end - - def test_backtrace_without_method_name - - expected = { - lineNumber: "123", - fileName: "/some/folder/some_file.rb", - methodName: "(none)" - } - - # note lack of "in method name" in this stack trace line - assert_equal expected, @client.send(:stack_trace_for, "/some/folder/some_file.rb:123") - end - - def test_full_payload_hash - Timecop.freeze do - Raygun.configuration.version = 123 - grouping_key = "my custom group" - correlation_id = "my correlation id" - - expected_hash = { - occurredOn: Time.now.utc.iso8601, - details: { - machineName: Socket.gethostname, - version: 123, - client: { - name: Raygun::CLIENT_NAME, - version: Raygun::VERSION, - clientUrl: Raygun::CLIENT_URL - }, - error: exception_hash, - userCustomData: {}, - tags: ["test"], - request: {}, - groupingKey: grouping_key, - correlationId: correlation_id, - environment: { - utcOffset: 13 - } - } - } - - assert_equal expected_hash, @client.send(:build_payload_hash, test_exception, { grouping_key: grouping_key, correlation_id: correlation_id }) - end - end - - def test_getting_request_information - env_hash = sample_env_hash.merge({ - "QUERY_STRING"=>"a=b&c=4945438", - "REQUEST_URI"=>"/?a=b&c=4945438", - }) - - expected_hash = { - hostName: "localhost", - url: "/", - httpMethod: "GET", - iPAddress: "127.0.0.1", - queryString: { "a" => "b", "c" => "4945438" }, - headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, - form: {}, - rawData: nil - } - - assert_equal expected_hash, @client.send(:request_information, env_hash) - end - - def test_getting_request_information_with_nil_env - assert_equal({}, @client.send(:request_information, nil)) - end - - def test_raw_post_body - env_hash = sample_env_hash.merge({ - "CONTENT_TYPE" => "application/json", - "REQUEST_METHOD" => "POST", - "rack.input" => StringIO.new('{"foo": "bar"}') - }) - - assert_equal '{"foo": "bar"}', @client.send(:request_information, env_hash)[:rawData] - end - - def test_raw_post_body_with_more_than_4096_chars - input = "0" * 5000; - env_hash = sample_env_hash.merge({ - "CONTENT_TYPE" => "application/json", - "REQUEST_METHOD" => "POST", - "rack.input" => StringIO.new(input) - }) - - assert_equal input.slice(0, 4096), @client.send(:request_information, env_hash)[:rawData] - end - - def test_raw_post_body_with_config_disabled - Raygun.configuration.record_raw_data = false - env_hash = sample_env_hash.merge({ - "CONTENT_TYPE" => "application/json", - "REQUEST_METHOD" => "POST", - "rack.input" => StringIO.new('{"foo": "bar"}') - }) - - assert_equal(nil, @client.send(:request_information, env_hash)[:rawData]) - end - - def test_error_raygun_custom_data - custom_data = { "kappa" => "keepo" } - e = Raygun::Error.new("A test message", custom_data) - test_env = {} - expected_form_hash = test_env.merge(custom_data) - - assert_equal expected_form_hash, @client.send(:build_payload_hash, e, test_env)[:details][:userCustomData] - end - - def test_custom_data_configuration_with_hash - custom_data = {foo: '123'} - Raygun.configuration.custom_data = custom_data - - assert_equal custom_data, @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details][:userCustomData] - end - - def test_custom_data_configuration_with_proc - Raygun.configuration.custom_data do |exception, env| - {exception_message: exception.message, server_name: env["SERVER_NAME"]} - end - expected = { - exception_message: "A test message", - server_name: "localhost" - } - - assert_equal expected, @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details][:userCustomData] - end - - def test_filtering_parameters - post_body_env_hash = sample_env_hash.merge( - "REQUEST_METHOD" => "POST", - "rack.input"=>StringIO.new("a=b&c=4945438&password=swordfish") - ) - - expected_form_hash = { "a" => "b", "c" => "4945438", "password" => "[FILTERED]" } - - assert_equal expected_form_hash, @client.send(:request_information, post_body_env_hash)[:form] - end - - def test_filtering_nested_params - post_body_env_hash = sample_env_hash.merge( - "REQUEST_METHOD" => "POST", - "rack.input" => StringIO.new("a=b&bank%5Bcredit_card%5D%5Bcard_number%5D=my_secret_bank_number&bank%5Bname%5D=something&c=123456&user%5Bpassword%5D=my_fancy_password") - ) - - expected_form_hash = { "a" => "b", "bank" => { "credit_card" => { "card_number" => "[FILTERED]" }, "name" => "something" }, "c" => "123456", "user" => { "password" => "[FILTERED]" } } - - assert_equal expected_form_hash, @client.send(:request_information, post_body_env_hash)[:form] - end - - def test_filter_parameters_using_proc - # filter any parameters that start with "nsa_only" - Raygun.configuration.filter_parameters do |hash| - hash.inject({}) do |sanitized_hash, (k, v)| - sanitized_hash[k] = if k.start_with? "nsa_only" - "[OUREYESONLY]" - else - v - end - sanitized_hash - end - end - - post_body_env_hash = sample_env_hash.merge( - "REQUEST_METHOD" => "POST", - "rack.input" => StringIO.new("nsa_only_info=123&nsa_only_metadata=seekrit&something_normal=hello") - ) - - expected_form_hash = { "nsa_only_info" => "[OUREYESONLY]", "nsa_only_metadata" => "[OUREYESONLY]", "something_normal" => "hello" } - - assert_equal expected_form_hash, @client.send(:request_information, post_body_env_hash)[:form] - ensure - Raygun.configuration.filter_parameters = nil - end - - def test_filter_parameters_using_array - filter_params_as_from_rails = [:password] - Raygun.configuration.filter_parameters = filter_params_as_from_rails - - parameters = { - "something_normal" => "hello", - "password" => "wouldntyouliketoknow", - "password_confirmation" => "wouldntyouliketoknow", - "PasswORD_weird_case" => "anythingatall" - } - - expected_form_hash = { - "something_normal" => "hello", - "password" => "[FILTERED]", - "password_confirmation" => "[FILTERED]", - "PasswORD_weird_case" => "[FILTERED]" - } - - post_body_env_hash = sample_env_hash.merge( - "REQUEST_METHOD" => "POST", - "rack.input" => StringIO.new(URI.encode_www_form(parameters)) - ) - - assert_equal expected_form_hash, @client.send(:request_information, post_body_env_hash)[:form] - ensure - Raygun.configuration.filter_parameters = nil - end - - def test_ip_address_from_action_dispatch - env_hash = sample_env_hash.merge({ - "action_dispatch.remote_ip"=> "123.456.789.012" - }) - - assert_equal "123.456.789.012", @client.send(:ip_address_from, env_hash) - assert_equal "123.456.789.012", @client.send(:request_information, env_hash)[:iPAddress] - end - - def test_ip_address_from_old_action_dispatch - old_action_dispatch_ip = FakeActionDispatcherIp.new("123.456.789.012") - env_hash = sample_env_hash.merge({ - "action_dispatch.remote_ip"=> old_action_dispatch_ip - }) - - assert_equal old_action_dispatch_ip, @client.send(:ip_address_from, env_hash) - assert_equal "123.456.789.012", @client.send(:request_information, env_hash)[:iPAddress] - end - - def test_ip_address_from_raygun_specific_key - env_hash = sample_env_hash.merge({ - "raygun.remote_ip"=>"123.456.789.012" - }) - - assert_equal "123.456.789.012", @client.send(:ip_address_from, env_hash) - assert_equal "123.456.789.012", @client.send(:request_information, env_hash)[:iPAddress] - end - - def test_ip_address_returns_not_available_if_not_set - env_hash = sample_env_hash.dup - env_hash.delete("REMOTE_ADDR") - - assert_equal "(Not Available)", @client.send(:ip_address_from, env_hash) - end - - def test_setting_up_http_proxy - begin - Raygun.configuration.proxy_settings[:address] = "http://proxy.com" - Raygun::Client.expects(:http_proxy).with("http://proxy.com", "80", nil, nil) - - Raygun.track_exceptions do - raise TestException.new - end - ensure - Raygun.configuration.proxy_settings = {} - end - end - - def test_filter_payload_with_whitelist_never_filters_toplevel - Timecop.freeze do - Raygun.configuration.filter_payload_with_whitelist = true - Raygun.configuration.whitelist_payload_shape = {} - - e = test_exception - - assert_equal Time.now.utc.iso8601, @client.send(:build_payload_hash, e)[:occurredOn] - assert_equal Hash, @client.send(:build_payload_hash, e)[:details].class - end - end - - def test_filter_payload_with_whitelist_never_filters_client - Raygun.configuration.filter_payload_with_whitelist = true - Raygun.configuration.whitelist_payload_shape = {} - - client_details = @client.send(:client_details) - - assert_equal client_details, @client.send(:build_payload_hash, test_exception)[:details][:client] - end - - def test_filter_payload_with_whitelist_default_error - Raygun.configuration.filter_payload_with_whitelist = true - - details = @client.send(:build_payload_hash, test_exception)[:details] - - assert_equal exception_hash, details[:error] - end - - def test_filter_payload_with_whitelist_exclude_error_keys - Raygun.configuration.filter_payload_with_whitelist = true - Raygun.configuration.whitelist_payload_shape = { - error: { - className: true, - message: true, - stackTrace: true - } - } - - details = @client.send(:build_payload_hash, test_exception)[:details] - - assert_equal exception_hash, details[:error] - end - - def test_filter_payload_with_whitelist_exclude_error_except_stacktrace - Raygun.configuration.filter_payload_with_whitelist = true - Raygun.configuration.whitelist_payload_shape = { - error: { - className: true, - message: true, - } - } - - details = @client.send(:build_payload_hash, test_exception)[:details] - - expected_hash = exception_hash.merge({ - stackTrace: "[FILTERED]" - }) - - assert_equal expected_hash, details[:error] - end - - def test_filter_payload_with_whitelist_proc - Raygun.configuration.filter_payload_with_whitelist = true - Raygun.configuration.whitelist_payload_shape do |payload| - payload - end - - details = @client.send(:build_payload_hash, test_exception)[:details] - - assert_equal exception_hash, details[:error] - end - - def test_filter_payload_with_whitelist_default_request_post - Raygun.configuration.filter_payload_with_whitelist = true - - post_body_env_hash = sample_env_hash.merge( - "CONTENT_TYPE" => 'application/x-www-form-urlencoded', - "REQUEST_METHOD" => "POST", - "rack.input"=>StringIO.new("a=b&c=4945438&password=swordfish") - ) - - details = @client.send(:build_payload_hash, test_exception, post_body_env_hash)[:details] - - expected_hash = { - hostName: "localhost", - url: "/", - httpMethod: "POST", - iPAddress: "127.0.0.1", - queryString: { }, - headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, - form: { "a" => "[FILTERED]", "c" => "[FILTERED]", "password" => "[FILTERED]" }, - rawData: {} - } - - assert_equal expected_hash, details[:request] - end - - def test_filter_payload_with_whitelist_request_post_except_formkey - Raygun.configuration.filter_payload_with_whitelist = true - shape = Raygun.configuration.whitelist_payload_shape.dup - shape[:request] = Raygun.configuration.whitelist_payload_shape[:request].merge( - form: { - username: true - } - ) - Raygun.configuration.whitelist_payload_shape = shape - - post_body_env_hash = sample_env_hash.merge( - "REQUEST_METHOD" => "POST", - "rack.input"=>StringIO.new("username=foo&password=swordfish") - ) - - details = @client.send(:build_payload_hash, test_exception, post_body_env_hash)[:details] - - expected_hash = { - hostName: "localhost", - url: "/", - httpMethod: "POST", - iPAddress: "127.0.0.1", - queryString: { }, - headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, - form: { "username" => "foo", "password" => "[FILTERED]" }, - rawData: {} - } - - assert_equal expected_hash, details[:request] - end - - def test_filter_payload_with_whitelist_default_request_get - Raygun.configuration.filter_payload_with_whitelist = true - - env_hash = sample_env_hash.merge({ - "QUERY_STRING"=>"a=b&c=4945438", - "REQUEST_URI"=>"/?a=b&c=4945438", - }) - expected_hash = { - hostName: "localhost", - url: "/", - httpMethod: "GET", - iPAddress: "127.0.0.1", - queryString: { "a" => "b", "c" => "4945438" }, - headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, - form: {}, - rawData: nil - } - - details = @client.send(:build_payload_hash, test_exception, env_hash)[:details] - - assert_equal expected_hash, details[:request] - end - - def test_filter_payload_with_whitelist_default_request_get_except_querystring - Raygun.configuration.filter_payload_with_whitelist = true - shape = Raygun.configuration.whitelist_payload_shape.dup - shape[:request] = Raygun::Configuration::DEFAULT_WHITELIST_PAYLOAD_SHAPE_REQUEST.dup.tap do |h| - h.delete(:queryString) - end - Raygun.configuration.whitelist_payload_shape = shape - - expected_hash = { - hostName: "localhost", - url: "/", - httpMethod: "GET", - iPAddress: "127.0.0.1", - queryString: "[FILTERED]", - headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, - form: {}, - rawData: nil - } - - details = @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details] - - assert_equal expected_hash, details[:request] - end - - def test_filter_payload_with_whitelist_being_false_does_not_filter_query_string - Raygun.configuration.filter_payload_with_whitelist = false - - env_hash = sample_env_hash.merge({ - "QUERY_STRING"=>"a=b&c=4945438", - "REQUEST_URI"=>"/?a=b&c=4945438", - }) - expected_hash = { - hostName: "localhost", - url: "/", - httpMethod: "GET", - iPAddress: "127.0.0.1", - queryString: { "a" => "b", "c" => "4945438" }, - headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, - form: {}, - rawData: nil - } - - details = @client.send(:build_payload_hash, test_exception, env_hash)[:details] - - assert_equal expected_hash, details[:request] - end - - def test_filter_payload_with_whitelist_request_specific_keys - Raygun.configuration.filter_payload_with_whitelist = true - Raygun.configuration.whitelist_payload_shape = { - request: { - url: true, - httpMethod: true, - hostName: true - } - } - - details = @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details] - - expected_hash = { - hostName: "localhost", - url: "/", - httpMethod: "GET", - iPAddress: "[FILTERED]", - queryString: "[FILTERED]", - headers: "[FILTERED]", - form: "[FILTERED]", - rawData: "[FILTERED]" - } - - assert_equal expected_hash, details[:request] - end - - - def test_filter_payload_with_whitelist_and_filter_parameters_applies_both - Raygun.configuration.filter_parameters = [:password] - Raygun.configuration.filter_payload_with_whitelist = true - Raygun.configuration.whitelist_payload_shape = proc do |payload| - payload[:request][:headers]["Cookie"] = "[FILTERED]" - payload - end - - parameters = { - "something_normal" => "hello", - "password" => "wouldntyouliketoknow" - } - - post_body_env_hash = sample_env_hash.merge( - "REQUEST_METHOD" => "POST", - "rack.input" => StringIO.new(URI.encode_www_form(parameters)) - ) - - payload = @client.send(:build_payload_hash, test_exception, post_body_env_hash) - request_payload = payload[:details][:request] - - expected_form = { - "something_normal" => "hello", - "password" => "[FILTERED]" - } - - assert_equal expected_form, request_payload[:form] - - expected_headers = { - "Version" => "HTTP/1.1", - "Host" => "localhost:3000", - "Cookie" => "[FILTERED]" - } - - assert_equal expected_headers, request_payload[:headers] - end - - def test_build_payload_hash_adds_affected_user_details_when_supplied_with_user - user = OpenStruct.new(id: '123', email: 'test@email.com', first_name: 'Taylor') - expected_details = { - :isAnonymous => false, - :identifier => '123', - :email => 'test@email.com', - :firstName => 'Taylor', - } - - user_details = @client.send(:build_payload_hash, test_exception, sample_env_hash, user)[:details][:user] - - assert_equal expected_details, user_details - end - - def test_build_payload_includes_breadcrumbs - ::Raygun::Breadcrumbs::Store.initialize - ::Raygun::Breadcrumbs::Store.record(message: "foo") - - breadcrumbs = @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details][:breadcrumbs] - ::Raygun::Breadcrumbs::Store.clear - - assert_equal breadcrumbs.length, 1 - assert breadcrumbs[0].is_a? Hash - end - - def test_raw_data_does_not_crash_on_buffer_without_pos - buffer = StringIO.new('123456789') - rack_env = { - REQUEST_METHOD: 'POST', - 'rack.input' => buffer - } - - buffer.instance_eval('undef :pos') - - raw_data = @client.send(:raw_data, rack_env) - - assert_equal('123456789', raw_data) - end - - private - - def test_exception - e = TestException.new - e.set_backtrace(["/some/folder/some_file.rb:123:in `some_method_name'", - "/another/path/foo.rb:1234:in `block (3 levels) run'"]) - - e - end - - def nest_exceptions(outer_exception, inner_exception) - begin - begin - raise inner_exception.class, inner_exception.message - rescue => e - e.set_backtrace inner_exception.backtrace - raise outer_exception.class, outer_exception.message - end - rescue => nested_exception - nested_exception.set_backtrace outer_exception.backtrace - end - - nested_exception - end - - def exception_hash - { - className: "ClientTest::TestException", - message: "A test message", - stackTrace: [ - { lineNumber: "123", fileName: "/some/folder/some_file.rb", methodName: "some_method_name" }, - { lineNumber: "1234", fileName: "/another/path/foo.rb", methodName: "block (3 levels) run"} - ] - } - end - - def sample_env_hash - { - "SERVER_NAME"=>"localhost", - "REQUEST_METHOD"=>"GET", - "REQUEST_PATH"=>"/", - "PATH_INFO"=>"/", - "QUERY_STRING"=>"", - "REQUEST_URI"=>"/", - "HTTP_VERSION"=>"HTTP/1.1", - "HTTP_HOST"=>"localhost:3000", - "HTTP_COOKIE"=>"cookieval", - "GATEWAY_INTERFACE"=>"CGI/1.2", - "SERVER_PORT"=>"3000", - "SERVER_PROTOCOL"=>"HTTP/1.1", - "SCRIPT_NAME"=>"", - "REMOTE_ADDR"=>"127.0.0.1" - } - end -end +# -*- coding: utf-8 -*- +require_relative "../test_helper.rb" +require 'stringio' + +class ClientTest < Raygun::UnitTest + + class TestException < StandardError + def initialize(message = nil) + super(message || "A test message") + end + end + + class NilMessageError < StandardError + def message + nil + end + end + + class FakeActionDispatcherIp + attr_reader :ip + def initialize remote_ip + @ip = remote_ip + end + def to_s + return ip + end + end + + def setup + super + @client = Raygun::Client.new + Raygun.configuration.record_raw_data = true + fake_successful_entry + + # Force NZ time zone for utcOffset tests + ENV['TZ'] = 'UTC-13' + end + + def test_record_breadcrumb_does_not_crash_without_initialized_store + Raygun.record_breadcrumb( + message: 'aliens', + category: 'exceptions', + level: :info + ) + end + + def test_api_key_required_message + Raygun.configuration.api_key = nil + + $stderr.expects(:puts).with(Raygun::Client::NO_API_KEY_MESSAGE).once + second_client = Raygun::Client.new + end + + def test_track_exception + response = Raygun.track_exceptions do + raise TestException.new + end + + assert response.success? + end + + def test_error_details + assert_equal exception_hash, @client.send(:error_details, test_exception) + end + + def test_error_details_with_nil_message + e = NilMessageError.new + expected_message = "" + assert_equal expected_message, @client.send(:error_details, e)[:message] + end + + def test_utc_offset + expected = 13 + + assert_equal expected, @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details][:environment][:utcOffset] + end + + def test_inner_error_details + oe = TestException.new("A test message") + oe.set_backtrace(["/some/folder/some_file.rb:123:in `some_method_name'"]) + + ie = TestException.new("Inner test message") + ie.set_backtrace(["/another/path/foo.rb:1234:in `block (3 levels) run'"]) + + e = nest_exceptions(oe, ie) + + expected_hash = { + className: "ClientTest::TestException", + message: oe.message, + stackTrace: [ + { lineNumber: "123", fileName: "/some/folder/some_file.rb", methodName: "some_method_name" } + ] + } + + # test inner error according with its availability (ruby >= 2.1) + if e.respond_to? :cause + expected_hash[:innerError] = { + className: "ClientTest::TestException", + message: ie.message, + stackTrace: [ + { lineNumber: "1234", fileName: "/another/path/foo.rb", methodName: "block (3 levels) run"} + ] + } + end + + assert_equal expected_hash, @client.send(:error_details, e) + end + + def test_client_details + expected_hash = { + name: Raygun::CLIENT_NAME, + version: Raygun::VERSION, + clientUrl: Raygun::CLIENT_URL + } + + assert_equal expected_hash, @client.send(:client_details) + end + + + def test_version + Raygun.setup do |config| + config.version = 123 + end + + assert_equal 123, @client.send(:version) + end + + def test_affected_user + test_env = { "raygun.affected_user" => { :identifier => "somepooruser@yourapp.com" } } + expected_hash = test_env["raygun.affected_user"] + + assert_equal expected_hash, @client.send(:build_payload_hash, test_exception, test_env)[:details][:user] + end + + def test_tags + configuration_tags = %w{alpha beta gaga} + explicit_env_tags = %w{one two three four} + rack_env_tag = %w{test} + + Raygun.setup do |config| + config.tags = configuration_tags + end + + test_env = { tags: explicit_env_tags } + expected_tags = configuration_tags + explicit_env_tags + rack_env_tag + + assert_equal expected_tags, @client.send(:build_payload_hash, test_exception, test_env)[:details][:tags] + end + + def test_tags_with_proc + configuration_tags = %w{bar} + explicit_env_tags = %w{one two three four} + rack_env_tag = %w{test} + + Raygun.setup do |config| + config.tags = ->(exception, env) { + [env[:foo]] + } + end + + test_env = { tags: explicit_env_tags, foo: 'bar' } + expected_tags = configuration_tags + explicit_env_tags + rack_env_tag + + assert_equal expected_tags, @client.send(:build_payload_hash, test_exception, test_env)[:details][:tags] + end + + def test_hostname + assert_equal Socket.gethostname, @client.send(:hostname) + end + + def test_unicode + e = TestException.new('日本語のメッセージ です') + + assert_silent { @client.track_exception(e) } + end + + def test_bad_encoding + bad_message = (100..1000).to_a.pack('c*').force_encoding('utf-8') + bad_exception = TestException.new(bad_message) + + assert !bad_message.valid_encoding? + assert_silent { @client.track_exception(bad_exception) } + end + + def test_backtrace_without_method_name + + expected = { + lineNumber: "123", + fileName: "/some/folder/some_file.rb", + methodName: "(none)" + } + + # note lack of "in method name" in this stack trace line + assert_equal expected, @client.send(:stack_trace_for, "/some/folder/some_file.rb:123") + end + + def test_full_payload_hash + Timecop.freeze do + Raygun.configuration.version = 123 + grouping_key = "my custom group" + correlation_id = "my correlation id" + + expected_hash = { + occurredOn: Time.now.utc.iso8601, + details: { + machineName: Socket.gethostname, + version: 123, + client: { + name: Raygun::CLIENT_NAME, + version: Raygun::VERSION, + clientUrl: Raygun::CLIENT_URL + }, + error: exception_hash, + userCustomData: {}, + tags: ["test"], + request: {}, + groupingKey: grouping_key, + correlationId: correlation_id, + environment: { + utcOffset: 13 + } + } + } + + assert_equal expected_hash, @client.send(:build_payload_hash, test_exception, { grouping_key: grouping_key, correlation_id: correlation_id }) + end + end + + def test_getting_request_information + env_hash = sample_env_hash.merge({ + "QUERY_STRING"=>"a=b&c=4945438", + "REQUEST_URI"=>"/?a=b&c=4945438", + }) + + expected_hash = { + hostName: "localhost", + url: "/", + httpMethod: "GET", + iPAddress: "127.0.0.1", + queryString: { "a" => "b", "c" => "4945438" }, + headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, + form: {}, + rawData: nil + } + + assert_equal expected_hash, @client.send(:request_information, env_hash) + end + + def test_getting_request_information_with_nil_env + assert_equal({}, @client.send(:request_information, nil)) + end + + def test_raw_post_body + env_hash = sample_env_hash.merge({ + "CONTENT_TYPE" => "application/json", + "REQUEST_METHOD" => "POST", + "rack.input" => StringIO.new('{"foo": "bar"}') + }) + + assert_equal '{"foo": "bar"}', @client.send(:request_information, env_hash)[:rawData] + end + + def test_raw_post_body_with_more_than_4096_chars + input = "0" * 5000; + env_hash = sample_env_hash.merge({ + "CONTENT_TYPE" => "application/json", + "REQUEST_METHOD" => "POST", + "rack.input" => StringIO.new(input) + }) + + assert_equal input.slice(0, 4096), @client.send(:request_information, env_hash)[:rawData] + end + + def test_raw_post_body_with_config_disabled + Raygun.configuration.record_raw_data = false + env_hash = sample_env_hash.merge({ + "CONTENT_TYPE" => "application/json", + "REQUEST_METHOD" => "POST", + "rack.input" => StringIO.new('{"foo": "bar"}') + }) + + assert_equal(nil, @client.send(:request_information, env_hash)[:rawData]) + end + + def test_error_raygun_custom_data + custom_data = { "kappa" => "keepo" } + e = Raygun::Error.new("A test message", custom_data) + test_env = {} + expected_form_hash = test_env.merge(custom_data) + + assert_equal expected_form_hash, @client.send(:build_payload_hash, e, test_env)[:details][:userCustomData] + end + + def test_custom_data_configuration_with_hash + custom_data = {foo: '123'} + Raygun.configuration.custom_data = custom_data + + assert_equal custom_data, @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details][:userCustomData] + end + + def test_custom_data_configuration_with_proc + Raygun.configuration.custom_data do |exception, env| + {exception_message: exception.message, server_name: env["SERVER_NAME"]} + end + expected = { + exception_message: "A test message", + server_name: "localhost" + } + + assert_equal expected, @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details][:userCustomData] + end + + def test_filtering_parameters + post_body_env_hash = sample_env_hash.merge( + "REQUEST_METHOD" => "POST", + "rack.input"=>StringIO.new("a=b&c=4945438&password=swordfish") + ) + + expected_form_hash = { "a" => "b", "c" => "4945438", "password" => "[FILTERED]" } + + assert_equal expected_form_hash, @client.send(:request_information, post_body_env_hash)[:form] + end + + def test_filtering_nested_params + post_body_env_hash = sample_env_hash.merge( + "REQUEST_METHOD" => "POST", + "rack.input" => StringIO.new("a=b&bank%5Bcredit_card%5D%5Bcard_number%5D=my_secret_bank_number&bank%5Bname%5D=something&c=123456&user%5Bpassword%5D=my_fancy_password") + ) + + expected_form_hash = { "a" => "b", "bank" => { "credit_card" => { "card_number" => "[FILTERED]" }, "name" => "something" }, "c" => "123456", "user" => { "password" => "[FILTERED]" } } + + assert_equal expected_form_hash, @client.send(:request_information, post_body_env_hash)[:form] + end + + def test_filter_parameters_using_proc + # filter any parameters that start with "nsa_only" + Raygun.configuration.filter_parameters do |hash| + hash.inject({}) do |sanitized_hash, (k, v)| + sanitized_hash[k] = if k.start_with? "nsa_only" + "[OUREYESONLY]" + else + v + end + sanitized_hash + end + end + + post_body_env_hash = sample_env_hash.merge( + "REQUEST_METHOD" => "POST", + "rack.input" => StringIO.new("nsa_only_info=123&nsa_only_metadata=seekrit&something_normal=hello") + ) + + expected_form_hash = { "nsa_only_info" => "[OUREYESONLY]", "nsa_only_metadata" => "[OUREYESONLY]", "something_normal" => "hello" } + + assert_equal expected_form_hash, @client.send(:request_information, post_body_env_hash)[:form] + ensure + Raygun.configuration.filter_parameters = nil + end + + def test_filter_parameters_using_array + filter_params_as_from_rails = [:password] + Raygun.configuration.filter_parameters = filter_params_as_from_rails + + parameters = { + "something_normal" => "hello", + "password" => "wouldntyouliketoknow", + "password_confirmation" => "wouldntyouliketoknow", + "PasswORD_weird_case" => "anythingatall" + } + + expected_form_hash = { + "something_normal" => "hello", + "password" => "[FILTERED]", + "password_confirmation" => "[FILTERED]", + "PasswORD_weird_case" => "[FILTERED]" + } + + post_body_env_hash = sample_env_hash.merge( + "REQUEST_METHOD" => "POST", + "rack.input" => StringIO.new(URI.encode_www_form(parameters)) + ) + + assert_equal expected_form_hash, @client.send(:request_information, post_body_env_hash)[:form] + ensure + Raygun.configuration.filter_parameters = nil + end + + def test_ip_address_from_action_dispatch + env_hash = sample_env_hash.merge({ + "action_dispatch.remote_ip"=> "123.456.789.012" + }) + + assert_equal "123.456.789.012", @client.send(:ip_address_from, env_hash) + assert_equal "123.456.789.012", @client.send(:request_information, env_hash)[:iPAddress] + end + + def test_ip_address_from_old_action_dispatch + old_action_dispatch_ip = FakeActionDispatcherIp.new("123.456.789.012") + env_hash = sample_env_hash.merge({ + "action_dispatch.remote_ip"=> old_action_dispatch_ip + }) + + assert_equal old_action_dispatch_ip, @client.send(:ip_address_from, env_hash) + assert_equal "123.456.789.012", @client.send(:request_information, env_hash)[:iPAddress] + end + + def test_ip_address_from_raygun_specific_key + env_hash = sample_env_hash.merge({ + "raygun.remote_ip"=>"123.456.789.012" + }) + + assert_equal "123.456.789.012", @client.send(:ip_address_from, env_hash) + assert_equal "123.456.789.012", @client.send(:request_information, env_hash)[:iPAddress] + end + + def test_ip_address_returns_not_available_if_not_set + env_hash = sample_env_hash.dup + env_hash.delete("REMOTE_ADDR") + + assert_equal "(Not Available)", @client.send(:ip_address_from, env_hash) + end + + def test_setting_up_http_proxy + begin + Raygun.configuration.proxy_settings[:address] = "http://proxy.com" + Raygun::Client.expects(:http_proxy).with("http://proxy.com", "80", nil, nil) + + Raygun.track_exceptions do + raise TestException.new + end + ensure + Raygun.configuration.proxy_settings = {} + end + end + + def test_filter_payload_with_whitelist_never_filters_toplevel + Timecop.freeze do + Raygun.configuration.filter_payload_with_whitelist = true + Raygun.configuration.whitelist_payload_shape = {} + + e = test_exception + + assert_equal Time.now.utc.iso8601, @client.send(:build_payload_hash, e)[:occurredOn] + assert_equal Hash, @client.send(:build_payload_hash, e)[:details].class + end + end + + def test_filter_payload_with_whitelist_never_filters_client + Raygun.configuration.filter_payload_with_whitelist = true + Raygun.configuration.whitelist_payload_shape = {} + + client_details = @client.send(:client_details) + + assert_equal client_details, @client.send(:build_payload_hash, test_exception)[:details][:client] + end + + def test_filter_payload_with_whitelist_default_error + Raygun.configuration.filter_payload_with_whitelist = true + + details = @client.send(:build_payload_hash, test_exception)[:details] + + assert_equal exception_hash, details[:error] + end + + def test_filter_payload_with_whitelist_exclude_error_keys + Raygun.configuration.filter_payload_with_whitelist = true + Raygun.configuration.whitelist_payload_shape = { + error: { + className: true, + message: true, + stackTrace: true + } + } + + details = @client.send(:build_payload_hash, test_exception)[:details] + + assert_equal exception_hash, details[:error] + end + + def test_filter_payload_with_whitelist_exclude_error_except_stacktrace + Raygun.configuration.filter_payload_with_whitelist = true + Raygun.configuration.whitelist_payload_shape = { + error: { + className: true, + message: true, + } + } + + details = @client.send(:build_payload_hash, test_exception)[:details] + + expected_hash = exception_hash.merge({ + stackTrace: "[FILTERED]" + }) + + assert_equal expected_hash, details[:error] + end + + def test_filter_payload_with_whitelist_proc + Raygun.configuration.filter_payload_with_whitelist = true + Raygun.configuration.whitelist_payload_shape do |payload| + payload + end + + details = @client.send(:build_payload_hash, test_exception)[:details] + + assert_equal exception_hash, details[:error] + end + + def test_filter_payload_with_whitelist_default_request_post + Raygun.configuration.filter_payload_with_whitelist = true + + post_body_env_hash = sample_env_hash.merge( + "CONTENT_TYPE" => 'application/x-www-form-urlencoded', + "REQUEST_METHOD" => "POST", + "rack.input"=>StringIO.new("a=b&c=4945438&password=swordfish") + ) + + details = @client.send(:build_payload_hash, test_exception, post_body_env_hash)[:details] + + expected_hash = { + hostName: "localhost", + url: "/", + httpMethod: "POST", + iPAddress: "127.0.0.1", + queryString: { }, + headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, + form: { "a" => "[FILTERED]", "c" => "[FILTERED]", "password" => "[FILTERED]" }, + rawData: {} + } + + assert_equal expected_hash, details[:request] + end + + def test_filter_payload_with_whitelist_request_post_except_formkey + Raygun.configuration.filter_payload_with_whitelist = true + shape = Raygun.configuration.whitelist_payload_shape.dup + shape[:request] = Raygun.configuration.whitelist_payload_shape[:request].merge( + form: { + username: true + } + ) + Raygun.configuration.whitelist_payload_shape = shape + + post_body_env_hash = sample_env_hash.merge( + "REQUEST_METHOD" => "POST", + "rack.input"=>StringIO.new("username=foo&password=swordfish") + ) + + details = @client.send(:build_payload_hash, test_exception, post_body_env_hash)[:details] + + expected_hash = { + hostName: "localhost", + url: "/", + httpMethod: "POST", + iPAddress: "127.0.0.1", + queryString: { }, + headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, + form: { "username" => "foo", "password" => "[FILTERED]" }, + rawData: {} + } + + assert_equal expected_hash, details[:request] + end + + def test_filter_payload_with_whitelist_default_request_get + Raygun.configuration.filter_payload_with_whitelist = true + + env_hash = sample_env_hash.merge({ + "QUERY_STRING"=>"a=b&c=4945438", + "REQUEST_URI"=>"/?a=b&c=4945438", + }) + expected_hash = { + hostName: "localhost", + url: "/", + httpMethod: "GET", + iPAddress: "127.0.0.1", + queryString: { "a" => "b", "c" => "4945438" }, + headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, + form: {}, + rawData: nil + } + + details = @client.send(:build_payload_hash, test_exception, env_hash)[:details] + + assert_equal expected_hash, details[:request] + end + + def test_filter_payload_with_whitelist_default_request_get_except_querystring + Raygun.configuration.filter_payload_with_whitelist = true + shape = Raygun.configuration.whitelist_payload_shape.dup + shape[:request] = Raygun::Configuration::DEFAULT_WHITELIST_PAYLOAD_SHAPE_REQUEST.dup.tap do |h| + h.delete(:queryString) + end + Raygun.configuration.whitelist_payload_shape = shape + + expected_hash = { + hostName: "localhost", + url: "/", + httpMethod: "GET", + iPAddress: "127.0.0.1", + queryString: "[FILTERED]", + headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, + form: {}, + rawData: nil + } + + details = @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details] + + assert_equal expected_hash, details[:request] + end + + def test_filter_payload_with_whitelist_being_false_does_not_filter_query_string + Raygun.configuration.filter_payload_with_whitelist = false + + env_hash = sample_env_hash.merge({ + "QUERY_STRING"=>"a=b&c=4945438", + "REQUEST_URI"=>"/?a=b&c=4945438", + }) + expected_hash = { + hostName: "localhost", + url: "/", + httpMethod: "GET", + iPAddress: "127.0.0.1", + queryString: { "a" => "b", "c" => "4945438" }, + headers: { "Version"=>"HTTP/1.1", "Host"=>"localhost:3000", "Cookie"=>"cookieval" }, + form: {}, + rawData: nil + } + + details = @client.send(:build_payload_hash, test_exception, env_hash)[:details] + + assert_equal expected_hash, details[:request] + end + + def test_filter_payload_with_whitelist_request_specific_keys + Raygun.configuration.filter_payload_with_whitelist = true + Raygun.configuration.whitelist_payload_shape = { + request: { + url: true, + httpMethod: true, + hostName: true + } + } + + details = @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details] + + expected_hash = { + hostName: "localhost", + url: "/", + httpMethod: "GET", + iPAddress: "[FILTERED]", + queryString: "[FILTERED]", + headers: "[FILTERED]", + form: "[FILTERED]", + rawData: "[FILTERED]" + } + + assert_equal expected_hash, details[:request] + end + + + def test_filter_payload_with_whitelist_and_filter_parameters_applies_both + Raygun.configuration.filter_parameters = [:password] + Raygun.configuration.filter_payload_with_whitelist = true + Raygun.configuration.whitelist_payload_shape = proc do |payload| + payload[:request][:headers]["Cookie"] = "[FILTERED]" + payload + end + + parameters = { + "something_normal" => "hello", + "password" => "wouldntyouliketoknow" + } + + post_body_env_hash = sample_env_hash.merge( + "REQUEST_METHOD" => "POST", + "rack.input" => StringIO.new(URI.encode_www_form(parameters)) + ) + + payload = @client.send(:build_payload_hash, test_exception, post_body_env_hash) + request_payload = payload[:details][:request] + + expected_form = { + "something_normal" => "hello", + "password" => "[FILTERED]" + } + + assert_equal expected_form, request_payload[:form] + + expected_headers = { + "Version" => "HTTP/1.1", + "Host" => "localhost:3000", + "Cookie" => "[FILTERED]" + } + + assert_equal expected_headers, request_payload[:headers] + end + + def test_build_payload_hash_adds_affected_user_details_when_supplied_with_user + user = OpenStruct.new(id: '123', email: 'test@email.com', first_name: 'Taylor') + expected_details = { + :isAnonymous => false, + :identifier => '123', + :email => 'test@email.com', + :firstName => 'Taylor', + } + + user_details = @client.send(:build_payload_hash, test_exception, sample_env_hash, user)[:details][:user] + + assert_equal expected_details, user_details + end + + def test_build_payload_includes_breadcrumbs + ::Raygun::Breadcrumbs::Store.initialize + ::Raygun::Breadcrumbs::Store.record(message: "foo") + + breadcrumbs = @client.send(:build_payload_hash, test_exception, sample_env_hash)[:details][:breadcrumbs] + ::Raygun::Breadcrumbs::Store.clear + + assert_equal breadcrumbs.length, 1 + assert breadcrumbs[0].is_a? Hash + end + + def test_raw_data_does_not_crash_on_buffer_without_pos + buffer = StringIO.new('123456789') + rack_env = { + REQUEST_METHOD: 'POST', + 'rack.input' => buffer + } + + buffer.instance_eval('undef :pos') + + raw_data = @client.send(:raw_data, rack_env) + + assert_equal('123456789', raw_data) + end + + private + + def test_exception + e = TestException.new + e.set_backtrace(["/some/folder/some_file.rb:123:in `some_method_name'", + "/another/path/foo.rb:1234:in `block (3 levels) run'"]) + + e + end + + def nest_exceptions(outer_exception, inner_exception) + begin + begin + raise inner_exception.class, inner_exception.message + rescue => e + e.set_backtrace inner_exception.backtrace + raise outer_exception.class, outer_exception.message + end + rescue => nested_exception + nested_exception.set_backtrace outer_exception.backtrace + end + + nested_exception + end + + def exception_hash + { + className: "ClientTest::TestException", + message: "A test message", + stackTrace: [ + { lineNumber: "123", fileName: "/some/folder/some_file.rb", methodName: "some_method_name" }, + { lineNumber: "1234", fileName: "/another/path/foo.rb", methodName: "block (3 levels) run"} + ] + } + end + + def sample_env_hash + { + "SERVER_NAME"=>"localhost", + "REQUEST_METHOD"=>"GET", + "REQUEST_PATH"=>"/", + "PATH_INFO"=>"/", + "QUERY_STRING"=>"", + "REQUEST_URI"=>"/", + "HTTP_VERSION"=>"HTTP/1.1", + "HTTP_HOST"=>"localhost:3000", + "HTTP_COOKIE"=>"cookieval", + "GATEWAY_INTERFACE"=>"CGI/1.2", + "SERVER_PORT"=>"3000", + "SERVER_PROTOCOL"=>"HTTP/1.1", + "SCRIPT_NAME"=>"", + "REMOTE_ADDR"=>"127.0.0.1" + } + end +end