azure_application_insights 0.5.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/gem-push.yml +34 -0
  3. data/.gitignore +40 -0
  4. data/.travis.yml +22 -0
  5. data/CHANGELOG.md +17 -0
  6. data/CONTRIBUTING.md +68 -0
  7. data/Gemfile +4 -0
  8. data/LICENSE.txt +11 -0
  9. data/README.md +161 -0
  10. data/Rakefile +15 -0
  11. data/application_insights.gemspec +30 -0
  12. data/lib/application_insights/channel/asynchronous_queue.rb +58 -0
  13. data/lib/application_insights/channel/asynchronous_sender.rb +133 -0
  14. data/lib/application_insights/channel/contracts/application.rb +14 -0
  15. data/lib/application_insights/channel/contracts/cloud.rb +14 -0
  16. data/lib/application_insights/channel/contracts/data.rb +14 -0
  17. data/lib/application_insights/channel/contracts/data_point.rb +24 -0
  18. data/lib/application_insights/channel/contracts/data_point_type.rb +7 -0
  19. data/lib/application_insights/channel/contracts/dependency_kind.rb +9 -0
  20. data/lib/application_insights/channel/contracts/dependency_source_type.rb +9 -0
  21. data/lib/application_insights/channel/contracts/device.rb +28 -0
  22. data/lib/application_insights/channel/contracts/envelope.rb +40 -0
  23. data/lib/application_insights/channel/contracts/event_data.rb +28 -0
  24. data/lib/application_insights/channel/contracts/exception_data.rb +37 -0
  25. data/lib/application_insights/channel/contracts/exception_details.rb +28 -0
  26. data/lib/application_insights/channel/contracts/internal.rb +14 -0
  27. data/lib/application_insights/channel/contracts/json_serializable.rb +59 -0
  28. data/lib/application_insights/channel/contracts/location.rb +16 -0
  29. data/lib/application_insights/channel/contracts/message_data.rb +24 -0
  30. data/lib/application_insights/channel/contracts/metric_data.rb +27 -0
  31. data/lib/application_insights/channel/contracts/operation.rb +19 -0
  32. data/lib/application_insights/channel/contracts/page_view_data.rb +30 -0
  33. data/lib/application_insights/channel/contracts/remote_dependency_data.rb +56 -0
  34. data/lib/application_insights/channel/contracts/request_data.rb +36 -0
  35. data/lib/application_insights/channel/contracts/session.rb +15 -0
  36. data/lib/application_insights/channel/contracts/severity_level.rb +13 -0
  37. data/lib/application_insights/channel/contracts/stack_frame.rb +17 -0
  38. data/lib/application_insights/channel/contracts/user.rb +19 -0
  39. data/lib/application_insights/channel/event.rb +68 -0
  40. data/lib/application_insights/channel/queue_base.rb +73 -0
  41. data/lib/application_insights/channel/sender_base.rb +88 -0
  42. data/lib/application_insights/channel/synchronous_queue.rb +45 -0
  43. data/lib/application_insights/channel/synchronous_sender.rb +17 -0
  44. data/lib/application_insights/channel/telemetry_channel.rb +131 -0
  45. data/lib/application_insights/channel/telemetry_context.rb +85 -0
  46. data/lib/application_insights/rack/track_request.rb +158 -0
  47. data/lib/application_insights/telemetry_client.rb +229 -0
  48. data/lib/application_insights/unhandled_exception.rb +49 -0
  49. data/lib/application_insights/version.rb +3 -0
  50. data/lib/application_insights.rb +9 -0
  51. data/test/application_insights/channel/contracts/test_application.rb +44 -0
  52. data/test/application_insights/channel/contracts/test_cloud.rb +44 -0
  53. data/test/application_insights/channel/contracts/test_data.rb +44 -0
  54. data/test/application_insights/channel/contracts/test_data_point.rb +109 -0
  55. data/test/application_insights/channel/contracts/test_device.rb +200 -0
  56. data/test/application_insights/channel/contracts/test_envelope.rb +209 -0
  57. data/test/application_insights/channel/contracts/test_event_data.rb +62 -0
  58. data/test/application_insights/channel/contracts/test_exception_data.rb +111 -0
  59. data/test/application_insights/channel/contracts/test_exception_details.rb +106 -0
  60. data/test/application_insights/channel/contracts/test_internal.rb +44 -0
  61. data/test/application_insights/channel/contracts/test_location.rb +70 -0
  62. data/test/application_insights/channel/contracts/test_message_data.rb +66 -0
  63. data/test/application_insights/channel/contracts/test_metric_data.rb +50 -0
  64. data/test/application_insights/channel/contracts/test_operation.rb +109 -0
  65. data/test/application_insights/channel/contracts/test_page_view_data.rb +88 -0
  66. data/test/application_insights/channel/contracts/test_remote_dependency_data.rb +209 -0
  67. data/test/application_insights/channel/contracts/test_request_data.rb +153 -0
  68. data/test/application_insights/channel/contracts/test_session.rb +57 -0
  69. data/test/application_insights/channel/contracts/test_stack_frame.rb +83 -0
  70. data/test/application_insights/channel/contracts/test_user.rb +96 -0
  71. data/test/application_insights/channel/test_asynchronous_queue.rb +47 -0
  72. data/test/application_insights/channel/test_asynchronous_sender.rb +81 -0
  73. data/test/application_insights/channel/test_event.rb +53 -0
  74. data/test/application_insights/channel/test_queue_base.rb +89 -0
  75. data/test/application_insights/channel/test_sender_base.rb +94 -0
  76. data/test/application_insights/channel/test_synchronous_queue.rb +28 -0
  77. data/test/application_insights/channel/test_synchronous_sender.rb +11 -0
  78. data/test/application_insights/channel/test_telemetry_channel.rb +167 -0
  79. data/test/application_insights/channel/test_telemetry_context.rb +83 -0
  80. data/test/application_insights/mock_sender.rb +37 -0
  81. data/test/application_insights/rack/test_track_request.rb +182 -0
  82. data/test/application_insights/test_logger.rb +10 -0
  83. data/test/application_insights/test_telemetry_client.rb +138 -0
  84. data/test/application_insights/test_unhandled_exception.rb +23 -0
  85. data/test/application_insights.rb +8 -0
  86. metadata +247 -0
@@ -0,0 +1,94 @@
1
+ require_relative '../../../lib/application_insights/channel/queue_base'
2
+ require_relative '../../../lib/application_insights/channel/sender_base'
3
+ require_relative '../test_logger'
4
+ require 'socket'
5
+ require 'test/unit'
6
+ require 'thread'
7
+
8
+ include ApplicationInsights::Channel
9
+
10
+ class TestSenderBase < Test::Unit::TestCase
11
+ def test_initialize
12
+ sender = SenderBase.new 'http://tempuri.org'
13
+ assert_equal 'http://tempuri.org', sender.service_endpoint_uri
14
+ assert_nil sender.queue
15
+ assert_equal 100, sender.send_buffer_size
16
+ end
17
+
18
+ def test_service_endpoint_uri
19
+ sender = SenderBase.new 'http://tempuri.org'
20
+ assert_equal 'http://tempuri.org', sender.service_endpoint_uri
21
+ sender.service_endpoint_uri = 'http://live.com'
22
+ assert_equal 'http://live.com', sender.service_endpoint_uri
23
+ end
24
+
25
+ def test_sender_queue_attribute
26
+ sender = SenderBase.new 'http://tempuri.org'
27
+ assert_nil sender.queue
28
+ temp = Object.new
29
+ sender.queue = temp
30
+ assert_equal temp, sender.queue
31
+ end
32
+
33
+ def test_send_buffer_size_works_as_expected
34
+ sender = SenderBase.new 'http://tempuri.org'
35
+ assert_equal 100, sender.send_buffer_size
36
+ sender.send_buffer_size = 42
37
+ assert_equal 42, sender.send_buffer_size
38
+ end
39
+
40
+ def test_send_works_as_expected_with_400_code
41
+ thread, port = execute_server '400 BadRequest'
42
+ test_logger = TestLogger.new
43
+ sender = SenderBase.new 'http://localhost:' + port.to_s + '/track'
44
+ sender.logger = test_logger
45
+ sender.queue = []
46
+ sender.send([1, 2])
47
+ thread.join
48
+ assert_equal [], sender.queue
49
+ assert_true test_logger.messages.include?('BadRequest')
50
+ end
51
+
52
+ def test_send_works_as_expected_with_500_code
53
+ thread, port = execute_server '500 InternalServerError'
54
+ test_logger = TestLogger.new
55
+ sender = SenderBase.new 'http://localhost:' + port.to_s + '/track'
56
+ sender.logger = test_logger
57
+ sender.queue = []
58
+ sender.send([1, 2])
59
+ thread.join
60
+ assert_equal [], sender.queue
61
+ assert_true test_logger.messages.include?('InternalServerError')
62
+ end
63
+
64
+ def execute_server(code)
65
+ port = 50000 + Random.rand(10000)
66
+ thread = Thread.new {
67
+ server = TCPServer.new(port)
68
+ client = server.accept
69
+ request = ''
70
+ read_buffer_size = 64
71
+ while true
72
+ temp = client.recv(read_buffer_size)
73
+ request += temp
74
+ break if temp.length < read_buffer_size
75
+ end
76
+ request = request.split(/\n/)[-1]
77
+ response = request
78
+ headers = [
79
+ "HTTP/1.1 " + code,
80
+ "Content-Type: application/json",
81
+ "Content-Length: #{response.length}\r\n\r\n"
82
+ ].join("\r\n")
83
+ client.puts headers
84
+ client.puts response
85
+ client.close
86
+ }
87
+
88
+ while thread.status != 'sleep'
89
+ sleep 0.1
90
+ end
91
+
92
+ return thread, port
93
+ end
94
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../../../lib/application_insights/channel/sender_base'
2
+ require_relative '../../../lib/application_insights/channel/synchronous_queue'
3
+ require_relative '../mock_sender'
4
+ require 'test/unit'
5
+
6
+ include ApplicationInsights::Channel
7
+
8
+ class TestSynchronousQueue < Test::Unit::TestCase
9
+ def test_initialize
10
+ SynchronousQueue.new(MockSynchronousSender.new)
11
+ end
12
+
13
+ def test_flush_works_as_expected
14
+ sender = MockSynchronousSender.new
15
+ sender.send_buffer_size = 2
16
+ queue = SynchronousQueue.new sender
17
+ queue.max_queue_length = 3
18
+ (1..7).to_a.each do |i|
19
+ queue.push i
20
+ end
21
+ assert_equal [[1, 2], [3], [4, 5], [6]], sender.buffer
22
+ temp = []
23
+ queue.instance_variable_get('@queue').length.times do |i|
24
+ temp.push queue.instance_variable_get('@queue').pop
25
+ end
26
+ assert_equal [7], temp
27
+ end
28
+ end
@@ -0,0 +1,11 @@
1
+ require_relative '../../../lib/application_insights/channel/synchronous_sender'
2
+ require 'test/unit'
3
+
4
+ include ApplicationInsights::Channel
5
+
6
+ class TestSynchronousSender < Test::Unit::TestCase
7
+ def test_initialize
8
+ sender = SynchronousSender.new
9
+ assert_equal 'https://dc.services.visualstudio.com/v2/track', sender.service_endpoint_uri
10
+ end
11
+ end
@@ -0,0 +1,167 @@
1
+ require_relative '../../../lib/application_insights/channel/telemetry_channel'
2
+ require_relative '../../../lib/application_insights/channel/telemetry_context'
3
+ require_relative '../../../lib/application_insights/channel/synchronous_queue'
4
+ require_relative '../../../lib/application_insights/channel/synchronous_sender'
5
+ require 'test/unit'
6
+ require 'time'
7
+
8
+ include ApplicationInsights::Channel
9
+
10
+ class TestTelemetryChannel < Test::Unit::TestCase
11
+ def test_initialize
12
+ channel = TelemetryChannel.new
13
+ assert_not_nil channel.context
14
+ assert_not_nil channel.queue
15
+ assert_not_nil channel.sender
16
+ end
17
+
18
+ def test_context_works_as_expected
19
+ context = TelemetryContext.new
20
+ channel = TelemetryChannel.new
21
+ assert_not_nil channel.context
22
+ assert_not_same context, channel.context
23
+ channel = TelemetryChannel.new context
24
+ assert_same context, channel.context
25
+ end
26
+
27
+ def test_queue_works_as_expected
28
+ queue = SynchronousQueue.new SynchronousSender.new
29
+ channel = TelemetryChannel.new
30
+ assert_not_nil channel.queue
31
+ assert_not_same queue, channel.queue
32
+ channel = TelemetryChannel.new nil, queue
33
+ assert_same queue, channel.queue
34
+ end
35
+
36
+ def test_sender_works_as_expected
37
+ queue = SynchronousQueue.new SynchronousSender.new
38
+ channel = TelemetryChannel.new
39
+ assert_not_nil channel.sender
40
+ assert_not_same queue.sender, channel.sender
41
+ channel = TelemetryChannel.new nil, queue
42
+ assert_same queue.sender, channel.sender
43
+ end
44
+
45
+ def test_flush_works_as_expected
46
+ queue = MockTelemetryChannelQueue.new SynchronousSender.new
47
+ channel = TelemetryChannel.new nil, queue
48
+ assert_equal 0, queue.flush_count
49
+ channel.flush
50
+ assert_equal 1, queue.flush_count
51
+ end
52
+
53
+ def test_write_works_as_expected
54
+ queue = MockTelemetryChannelQueue.new SynchronousSender.new
55
+ context = TelemetryContext.new
56
+ context.instrumentation_key = 'instrumentation key'
57
+ channel = TelemetryChannel.new context, queue
58
+ expected = MockTelemetryItemData.new
59
+ channel.write expected
60
+ assert_equal 1, queue.queue.count
61
+ actual = queue.queue[0]
62
+ assert_not_nil actual
63
+ assert_equal 1, actual.ver
64
+ assert_equal 100, actual.sample_rate
65
+ assert_equal 'Microsoft.ApplicationInsights.MockTelemetryItem', actual.name
66
+ assert_not_nil actual.time
67
+ assert_equal 'instrumentation key', actual.i_key
68
+ assert_not_nil actual.tags
69
+ assert_equal 1, actual.tags.count
70
+ assert_equal 'rb:'+ ApplicationInsights::VERSION, actual.tags['ai.internal.sdkVersion']
71
+ assert_not_nil actual.data
72
+ assert_equal 'MockTelemetryItemData', actual.data.base_type
73
+ assert_same expected, actual.data.base_data
74
+ end
75
+
76
+ def test_write_custom_timestamp
77
+ queue = MockTelemetryChannelQueue.new SynchronousSender.new
78
+ context = TelemetryContext.new
79
+ context.instrumentation_key = 'instrumentation key'
80
+ channel = TelemetryChannel.new context, queue
81
+ data = MockTelemetryItemData.new
82
+ timestamp = (Time.now - 5).iso8601(7)
83
+
84
+ channel.write data
85
+ actual = queue.queue[0]
86
+ assert_not_equal timestamp, actual.time
87
+
88
+ channel.write data, nil, timestamp
89
+ actual = queue.queue[1]
90
+ assert_equal timestamp, actual.time
91
+ end
92
+
93
+ def test_write_custom_timestamp_accept_string_time_type
94
+ queue = MockTelemetryChannelQueue.new SynchronousSender.new
95
+ context = TelemetryContext.new
96
+ context.instrumentation_key = 'instrumentation key'
97
+ channel = TelemetryChannel.new context, queue
98
+ data = MockTelemetryItemData.new
99
+
100
+ timestamp_string = (Time.now - 5).iso8601(7)
101
+ channel.write data, nil, timestamp_string
102
+ actual = queue.queue[0]
103
+ assert_equal timestamp_string, actual.time
104
+
105
+ timestamp_time = Time.now - 5
106
+ channel.write data, nil, timestamp_time
107
+ actual = queue.queue[1]
108
+ assert_equal timestamp_time.iso8601(7), actual.time
109
+
110
+ timestamp_invalid = {:invalid => "invalid timestamp"}
111
+ channel.write data, nil, timestamp_invalid
112
+ actual = queue.queue[2]
113
+ assert_not_equal timestamp_invalid, actual.time
114
+ end
115
+
116
+ def test_get_tags_works_as_expected
117
+ queue = MockTelemetryChannelQueue.new SynchronousSender.new
118
+ context = TelemetryContext.new
119
+ context.application.ver = 'ver'
120
+ context.cloud.role_name = 'role name'
121
+ context.device.id = 'device id'
122
+ context.user.id = 'user id'
123
+ context.session.id = 'session id'
124
+ context.location.ip = 'ip'
125
+ context.operation.id = 'operation id'
126
+ channel = TelemetryChannel.new context, queue
127
+ expected = MockTelemetryItemData.new
128
+ channel.write expected
129
+
130
+ assert_equal 1, queue.queue.count
131
+ tags = queue.queue[0].tags
132
+ assert_equal 'rb:'+ ApplicationInsights::VERSION, tags['ai.internal.sdkVersion']
133
+ assert_equal 'ver', tags['ai.application.ver']
134
+ assert_equal 'role name', tags['ai.cloud.role']
135
+ assert_equal 'device id', tags['ai.device.id']
136
+ assert_equal 'user id', tags['ai.user.id']
137
+ assert_equal 'session id', tags['ai.session.id']
138
+ assert_equal 'ip', tags['ai.location.ip']
139
+ assert_equal 'operation id', tags['ai.operation.id']
140
+ end
141
+ end
142
+
143
+ class MockTelemetryItemData
144
+ def initialize
145
+ @properties = {}
146
+ end
147
+
148
+ attr_accessor :properties
149
+ end
150
+
151
+ class MockTelemetryChannelQueue < QueueBase
152
+ def initialize(sender)
153
+ super sender
154
+ @queue = []
155
+ @flush_count = 0
156
+ end
157
+
158
+ attr_accessor :flush_count, :queue
159
+
160
+ def push(data)
161
+ @queue.push data
162
+ end
163
+
164
+ def flush
165
+ @flush_count += 1
166
+ end
167
+ end
@@ -0,0 +1,83 @@
1
+ require_relative '../../../lib/application_insights/channel/telemetry_context'
2
+ require 'test/unit'
3
+
4
+ include ApplicationInsights::Channel
5
+
6
+ class TestTelemetryContext < Test::Unit::TestCase
7
+ def test_initialize
8
+ context = TelemetryContext.new
9
+ assert_nil context.instrumentation_key
10
+ assert_not_nil context.application
11
+ assert_not_nil context.device
12
+ assert_not_nil context.user
13
+ assert_not_nil context.session
14
+ assert_not_nil context.operation
15
+ assert_not_nil context.location
16
+ assert_not_nil context.properties
17
+ end
18
+
19
+ def test_application_works_as_expected
20
+ context = TelemetryContext.new
21
+ assert_not_nil context.application
22
+ expected = Contracts::Application.new
23
+ context.application = expected
24
+ assert_same expected, context.application
25
+ end
26
+
27
+ def test_cloud_works_as_expected
28
+ context = TelemetryContext.new
29
+ assert_not_nil context.cloud
30
+ expected = Contracts::Cloud.new
31
+ context.cloud = expected
32
+ assert_same expected, context.cloud
33
+ end
34
+
35
+ def test_device_works_as_expected
36
+ context = TelemetryContext.new
37
+ assert_not_nil context.device
38
+ expected = Contracts::Device.new
39
+ context.device = expected
40
+ assert_same expected, context.device
41
+ end
42
+
43
+ def test_user_works_as_expected
44
+ context = TelemetryContext.new
45
+ assert_not_nil context.user
46
+ expected = Contracts::User.new
47
+ context.user = expected
48
+ assert_same expected, context.user
49
+ end
50
+
51
+ def test_session_works_as_expected
52
+ context = TelemetryContext.new
53
+ assert_not_nil context.session
54
+ expected = Contracts::Session.new
55
+ context.session = expected
56
+ assert_same expected, context.session
57
+ end
58
+
59
+ def test_operation_works_as_expected
60
+ context = TelemetryContext.new
61
+ assert_not_nil context.operation
62
+ expected = Contracts::Operation.new
63
+ context.operation = expected
64
+ assert_same expected, context.operation
65
+ end
66
+
67
+ def test_location_works_as_expected
68
+ context = TelemetryContext.new
69
+ assert_not_nil context.location
70
+ expected = Contracts::Location.new
71
+ context.location = expected
72
+ assert_same expected, context.location
73
+ end
74
+
75
+ def test_properties_works_as_expected
76
+ context = TelemetryContext.new
77
+ assert_not_nil context.properties
78
+ assert_equal 0, context.properties.count
79
+ expected = {:a => 'a', :b => 'b'}
80
+ context.properties = expected
81
+ assert_same expected, context.properties
82
+ end
83
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../../lib/application_insights/channel/synchronous_sender'
2
+ require_relative '../../lib/application_insights/channel/asynchronous_sender'
3
+
4
+ include ApplicationInsights::Channel
5
+
6
+ class MockSynchronousSender < SynchronousSender
7
+ def initialize
8
+ @buffer = []
9
+ super
10
+ end
11
+
12
+ attr_accessor :buffer
13
+
14
+ def send(data)
15
+ @buffer << data
16
+ end
17
+ end
18
+
19
+ class MockAsynchronousSender < AsynchronousSender
20
+ def initialize
21
+ @buffer = []
22
+ @start_call_count = 0
23
+ super
24
+ end
25
+
26
+ attr_accessor :start_call_count
27
+ attr_accessor :buffer
28
+
29
+ def start
30
+ @start_call_count += 1
31
+ super
32
+ end
33
+
34
+ def send(data)
35
+ @buffer << data
36
+ end
37
+ end
@@ -0,0 +1,182 @@
1
+ require 'test/unit'
2
+ require 'mocha/test_unit'
3
+ require 'rack/mock'
4
+ require_relative '../mock_sender'
5
+ require_relative '../../../lib/application_insights/rack/track_request'
6
+
7
+ include ApplicationInsights::Rack
8
+
9
+ class TestTrackRequest < Test::Unit::TestCase
10
+
11
+ TIME_SPAN_FORMAT = /^(?<day>\d{2})\.(?<hour>\d{2}):(?<minute>\d{2}):(?<second>\d{2}).(?<fraction>\d{7})$/
12
+
13
+ def test_call_works_as_expected
14
+ response_code = rand(200..204)
15
+ app = Proc.new {|env| sleep(2.5); [response_code, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
16
+ url = "http://localhost:8080/foo?a=b"
17
+ http_method = 'PUT'
18
+ env = Rack::MockRequest.env_for(url, :method => http_method)
19
+ instrumentation_key = 'key'
20
+ sender = MockAsynchronousSender.new
21
+ track_request = TrackRequest.new app, instrumentation_key, 500, 1
22
+ track_request.send(:sender=, sender)
23
+ start_time = Time.now
24
+
25
+ SecureRandom.expects(:base64).with(10).returns('y0NM2eOY/fnQPw==')
26
+ result = track_request.call(env)
27
+
28
+ app_result = app.call(env)
29
+ assert_equal app_result, result
30
+ sleep(sender.send_interval)
31
+
32
+ assert_equal 1, sender.buffer.count
33
+ payload = sender.buffer[0]
34
+ assert_equal instrumentation_key, payload[0].i_key
35
+
36
+ request_data = payload[0].data.base_data
37
+ assert_equal "#{http_method} /foo", request_data.name
38
+ assert_equal response_code, request_data.response_code
39
+ assert_equal true, request_data.success
40
+ assert_equal http_method, request_data.http_method
41
+ assert_equal url, request_data.url
42
+ assert_equal true, request_data.duration.start_with?("00.00:00:02")
43
+ assert Time.parse(request_data.start_time) - start_time < 0.01
44
+ end
45
+
46
+ def test_call_with_failed_request
47
+ response_code = rand(400..600)
48
+ app = Proc.new {|env| [response_code, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
49
+ url = "http://localhost:8080/foo"
50
+ http_method = 'PUT'
51
+ env = Rack::MockRequest.env_for(url, :method => http_method)
52
+ instrumentation_key = 'key'
53
+ sender = MockAsynchronousSender.new
54
+ track_request = TrackRequest.new app, instrumentation_key, 500, 1
55
+ track_request.send(:sender=, sender)
56
+ track_request.call(env)
57
+ sleep(sender.send_interval)
58
+
59
+ payload = sender.buffer[0]
60
+ request_data = payload[0].data.base_data
61
+ assert_equal false, request_data.success
62
+ end
63
+
64
+ def test_call_with_unhandled_exception
65
+ app = Proc.new {|env| raise StandardError, 'Boom!'}
66
+ env = Rack::MockRequest.env_for( "http://localhost:8080/foo", :method => "GET")
67
+ instrumentation_key = 'key'
68
+ sender = MockAsynchronousSender.new
69
+ track_request = TrackRequest.new app, instrumentation_key, 500, 1
70
+ track_request.send(:sender=, sender)
71
+
72
+ begin
73
+ track_request.call(env)
74
+ rescue => ex
75
+ assert_equal 'Boom!', ex.message
76
+ end
77
+
78
+ sleep(sender.send_interval)
79
+ payload = sender.buffer[0]
80
+ assert_equal 2, payload.count
81
+ request_data = payload[0].data.base_data
82
+ assert_equal false, request_data.success
83
+ assert_equal 500, request_data.response_code
84
+
85
+ exception_data = payload[1].data.base_data
86
+ assert_equal instrumentation_key, payload[1].i_key
87
+ assert_equal 'Unhandled', exception_data.handled_at
88
+ end
89
+
90
+ def test_internal_client
91
+ app = Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
92
+ env = Rack::MockRequest.env_for('http://localhost:8080/foo', :method => "GET")
93
+ buffer_size = 30
94
+ send_interval = 5
95
+ track_request = TrackRequest.new app, 'key', buffer_size, send_interval
96
+ client = track_request.send(:client)
97
+ # test client initialization
98
+ assert_equal ApplicationInsights::TelemetryClient, client.class
99
+
100
+ track_request.call(env)
101
+ client = track_request.send(:client)
102
+ channel = client.channel
103
+ assert_equal buffer_size, channel.queue.max_queue_length
104
+ assert_equal send_interval, channel.sender.send_interval
105
+ end
106
+
107
+ def test_format_request_duration_less_than_a_day
108
+ app = Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
109
+ track_request = TrackRequest.new app, 'one'
110
+ duration_seconds = rand(86400) + rand
111
+ time_span = track_request.send(:format_request_duration, duration_seconds)
112
+
113
+ match = TIME_SPAN_FORMAT.match time_span
114
+ assert_not_nil match
115
+ days = duration_seconds.to_i/86400
116
+ assert_equal days, match['day'].to_i
117
+ hours = (duration_seconds - days * 86400).to_i/3600
118
+ assert_equal hours, match['hour'].to_i
119
+ minutes = (duration_seconds - days * 86400 - hours * 3600).to_i/60
120
+ assert_equal minutes, match['minute'].to_i
121
+ seconds = (duration_seconds - days * 86400 - hours * 3600 - minutes * 60).to_i
122
+ assert_equal seconds, match['second'].to_i
123
+ fraction = ((duration_seconds - duration_seconds.to_i) * 10 ** 7).to_i
124
+ assert_equal fraction, match['fraction'].to_i
125
+ end
126
+
127
+ def test_format_request_duration_more_than_a_day
128
+ app = Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
129
+ track_request = TrackRequest.new app, 'one'
130
+ duration_seconds = rand(86400..240000) + rand
131
+ time_span = track_request.send(:format_request_duration, duration_seconds)
132
+
133
+ match = TIME_SPAN_FORMAT.match time_span
134
+ assert_not_nil match
135
+ assert_equal 1, match['day'].to_i
136
+ assert_equal 0, match['hour'].to_i
137
+ assert_equal 0, match['minute'].to_i
138
+ assert_equal 0, match['second'].to_i
139
+ assert_equal 0, match['fraction'].to_i
140
+ end
141
+
142
+ def test_request_id_is_generated_correctly
143
+ app = Proc.new {|env| [200, {"Content-Type" => "text/html"}, ["Hello Rack!"]]}
144
+ url = "http://localhost:8080/foo?a=b"
145
+ http_method = 'PUT'
146
+ env = Rack::MockRequest.env_for(url, :method => http_method)
147
+ instrumentation_key = 'key'
148
+ sender = MockAsynchronousSender.new
149
+ track_request = TrackRequest.new app, instrumentation_key, 500, 0
150
+ track_request.send(:sender=, sender)
151
+
152
+ # ignores ids that don't begin with | (16 chars)
153
+ env['HTTP_REQUEST_ID'] = 'ab456_1.ea6741a'
154
+ SecureRandom.expects(:base64).with(10).returns('y0NM2eOY/fnQPw==')
155
+ track_request.call(env)
156
+ assert_equal '|y0NM2eOY/fnQPw==.', env['ApplicationInsights.request.id']
157
+
158
+ # appends to ids with a dot (8 chars)
159
+ env['HTTP_REQUEST_ID'] = '|1234.'
160
+ SecureRandom.expects(:base64).with(5).returns('eXsMFHs=')
161
+ track_request.call(env)
162
+ assert_equal '|1234.eXsMFHs=_', env['ApplicationInsights.request.id']
163
+
164
+ # appends to ids with an underscore (8 chars)
165
+ env['HTTP_REQUEST_ID'] = '|1234_'
166
+ SecureRandom.expects(:base64).with(5).returns('eXsMFHs=')
167
+ track_request.call(env)
168
+ assert_equal '|1234_eXsMFHs=_', env['ApplicationInsights.request.id']
169
+
170
+ # appends a dot if neither a dot or underscore are present (8 chars)
171
+ env['HTTP_REQUEST_ID'] = '|ab456_1.ea6741a'
172
+ SecureRandom.expects(:base64).with(5).returns('eXsMFHs=')
173
+ track_request.call(env)
174
+ assert_equal '|ab456_1.ea6741a.eXsMFHs=_', env['ApplicationInsights.request.id']
175
+
176
+ # generates a stand-alone id if one is not provided (16 chars)
177
+ env.delete('HTTP_REQUEST_ID')
178
+ SecureRandom.expects(:base64).with(10).returns('y0NM2eOY/fnQPw==')
179
+ track_request.call(env)
180
+ assert_equal '|y0NM2eOY/fnQPw==.', env['ApplicationInsights.request.id']
181
+ end
182
+ end
@@ -0,0 +1,10 @@
1
+ class TestLogger < Logger
2
+ def initialize
3
+ @strio = StringIO.new
4
+ super(@strio)
5
+ end
6
+
7
+ def messages
8
+ @strio.string
9
+ end
10
+ end