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.
- checksums.yaml +7 -0
- data/.github/workflows/gem-push.yml +34 -0
- data/.gitignore +40 -0
- data/.travis.yml +22 -0
- data/CHANGELOG.md +17 -0
- data/CONTRIBUTING.md +68 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +11 -0
- data/README.md +161 -0
- data/Rakefile +15 -0
- data/application_insights.gemspec +30 -0
- data/lib/application_insights/channel/asynchronous_queue.rb +58 -0
- data/lib/application_insights/channel/asynchronous_sender.rb +133 -0
- data/lib/application_insights/channel/contracts/application.rb +14 -0
- data/lib/application_insights/channel/contracts/cloud.rb +14 -0
- data/lib/application_insights/channel/contracts/data.rb +14 -0
- data/lib/application_insights/channel/contracts/data_point.rb +24 -0
- data/lib/application_insights/channel/contracts/data_point_type.rb +7 -0
- data/lib/application_insights/channel/contracts/dependency_kind.rb +9 -0
- data/lib/application_insights/channel/contracts/dependency_source_type.rb +9 -0
- data/lib/application_insights/channel/contracts/device.rb +28 -0
- data/lib/application_insights/channel/contracts/envelope.rb +40 -0
- data/lib/application_insights/channel/contracts/event_data.rb +28 -0
- data/lib/application_insights/channel/contracts/exception_data.rb +37 -0
- data/lib/application_insights/channel/contracts/exception_details.rb +28 -0
- data/lib/application_insights/channel/contracts/internal.rb +14 -0
- data/lib/application_insights/channel/contracts/json_serializable.rb +59 -0
- data/lib/application_insights/channel/contracts/location.rb +16 -0
- data/lib/application_insights/channel/contracts/message_data.rb +24 -0
- data/lib/application_insights/channel/contracts/metric_data.rb +27 -0
- data/lib/application_insights/channel/contracts/operation.rb +19 -0
- data/lib/application_insights/channel/contracts/page_view_data.rb +30 -0
- data/lib/application_insights/channel/contracts/remote_dependency_data.rb +56 -0
- data/lib/application_insights/channel/contracts/request_data.rb +36 -0
- data/lib/application_insights/channel/contracts/session.rb +15 -0
- data/lib/application_insights/channel/contracts/severity_level.rb +13 -0
- data/lib/application_insights/channel/contracts/stack_frame.rb +17 -0
- data/lib/application_insights/channel/contracts/user.rb +19 -0
- data/lib/application_insights/channel/event.rb +68 -0
- data/lib/application_insights/channel/queue_base.rb +73 -0
- data/lib/application_insights/channel/sender_base.rb +88 -0
- data/lib/application_insights/channel/synchronous_queue.rb +45 -0
- data/lib/application_insights/channel/synchronous_sender.rb +17 -0
- data/lib/application_insights/channel/telemetry_channel.rb +131 -0
- data/lib/application_insights/channel/telemetry_context.rb +85 -0
- data/lib/application_insights/rack/track_request.rb +158 -0
- data/lib/application_insights/telemetry_client.rb +229 -0
- data/lib/application_insights/unhandled_exception.rb +49 -0
- data/lib/application_insights/version.rb +3 -0
- data/lib/application_insights.rb +9 -0
- data/test/application_insights/channel/contracts/test_application.rb +44 -0
- data/test/application_insights/channel/contracts/test_cloud.rb +44 -0
- data/test/application_insights/channel/contracts/test_data.rb +44 -0
- data/test/application_insights/channel/contracts/test_data_point.rb +109 -0
- data/test/application_insights/channel/contracts/test_device.rb +200 -0
- data/test/application_insights/channel/contracts/test_envelope.rb +209 -0
- data/test/application_insights/channel/contracts/test_event_data.rb +62 -0
- data/test/application_insights/channel/contracts/test_exception_data.rb +111 -0
- data/test/application_insights/channel/contracts/test_exception_details.rb +106 -0
- data/test/application_insights/channel/contracts/test_internal.rb +44 -0
- data/test/application_insights/channel/contracts/test_location.rb +70 -0
- data/test/application_insights/channel/contracts/test_message_data.rb +66 -0
- data/test/application_insights/channel/contracts/test_metric_data.rb +50 -0
- data/test/application_insights/channel/contracts/test_operation.rb +109 -0
- data/test/application_insights/channel/contracts/test_page_view_data.rb +88 -0
- data/test/application_insights/channel/contracts/test_remote_dependency_data.rb +209 -0
- data/test/application_insights/channel/contracts/test_request_data.rb +153 -0
- data/test/application_insights/channel/contracts/test_session.rb +57 -0
- data/test/application_insights/channel/contracts/test_stack_frame.rb +83 -0
- data/test/application_insights/channel/contracts/test_user.rb +96 -0
- data/test/application_insights/channel/test_asynchronous_queue.rb +47 -0
- data/test/application_insights/channel/test_asynchronous_sender.rb +81 -0
- data/test/application_insights/channel/test_event.rb +53 -0
- data/test/application_insights/channel/test_queue_base.rb +89 -0
- data/test/application_insights/channel/test_sender_base.rb +94 -0
- data/test/application_insights/channel/test_synchronous_queue.rb +28 -0
- data/test/application_insights/channel/test_synchronous_sender.rb +11 -0
- data/test/application_insights/channel/test_telemetry_channel.rb +167 -0
- data/test/application_insights/channel/test_telemetry_context.rb +83 -0
- data/test/application_insights/mock_sender.rb +37 -0
- data/test/application_insights/rack/test_track_request.rb +182 -0
- data/test/application_insights/test_logger.rb +10 -0
- data/test/application_insights/test_telemetry_client.rb +138 -0
- data/test/application_insights/test_unhandled_exception.rb +23 -0
- data/test/application_insights.rb +8 -0
- 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
|