heap 0.3.0 → 1.0.0

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.
@@ -0,0 +1,18 @@
1
+ # Superclass for all Heap-related errors.
2
+ class HeapAPI::Error < RuntimeError
3
+ end
4
+
5
+ # Raised when the Heap API server returns an error response.
6
+ class HeapAPI::ApiError < HeapAPI::Error
7
+ # @param [Faraday::Response] response the error response returned by the Heap
8
+ # analytics server
9
+ def initialize(response)
10
+ @response = response
11
+
12
+ super "Heap API server error: #{response.status} #{response.body}"
13
+ end
14
+
15
+ # @return [Faraday::Response] response the error response returned by the
16
+ # Heap analytics server
17
+ attr_reader :response
18
+ end
@@ -0,0 +1,57 @@
1
+ # Internal methods used to validate API input.
2
+
3
+ class HeapAPI::Client
4
+ # Makes sure that the client's app_id property is set.
5
+ #
6
+ # @raise RuntimeError if the client's app_id is nil
7
+ # @return [HeapAPI::Client] self
8
+ def ensure_valid_app_id!
9
+ raise RuntimeError, 'Heap app_id not set' unless @app_id
10
+ self
11
+ end
12
+ private :ensure_valid_app_id!
13
+
14
+ # Validates a Heap server-side API event name.
15
+ #
16
+ # @param [String] event the name of the server-side event
17
+ # @raise ArgumentError if the event name is invalid
18
+ # @return [HeapAPI::Client] self
19
+ def ensure_valid_event_name!(event)
20
+ raise ArgumentError, 'Missing or empty event name' if event.empty?
21
+ raise ArgumentError, 'Event name too long' if event.length > 1024
22
+ self
23
+ end
24
+ private :ensure_valid_event_name!
25
+
26
+ # Validates a bag of properties sent to a Heap server-side API.
27
+ #
28
+ # @param [Hash<String, String|Number>] properties key-value property bag;
29
+ # each key must have fewer than 1024 characters; each value must be a
30
+ # Number or String with fewer than 1024 characters
31
+ # @raise ArgumentError if the property bag is invalid
32
+ # @return [HeapAPI::Client] self
33
+ def ensure_valid_properties!(properties)
34
+ unless properties.respond_to?(:each)
35
+ raise ArgumentError, 'Properties object does not implement #each'
36
+ end
37
+
38
+ properties.each do |key, value|
39
+ if key.to_s.length > 1024
40
+ raise ArgumentError, "Property name #{key} too long; " +
41
+ "#{key.to_s.length} is above the 1024-character limit"
42
+ end
43
+ if value.kind_of? Numeric
44
+ # TODO(pwnall): Check numerical limits, if necessary.
45
+ elsif value.kind_of?(String) || value.kind_of?(Symbol)
46
+ if value.to_s.length > 1024
47
+ raise ArgumentError, "Property #{key} value #{value.inspect} too " +
48
+ "long; #{value.to_s.length} is above the 1024-character limit"
49
+ end
50
+ else
51
+ raise ArgumentError,
52
+ "Unsupported type for property #{key} value #{value.inspect}"
53
+ end
54
+ end
55
+ end
56
+ private :ensure_valid_properties!
57
+ end
@@ -0,0 +1,124 @@
1
+ require 'helper'
2
+
3
+ class ClientAddUserPropertiesTest < MiniTest::Test
4
+ def setup
5
+ @stubs = Faraday::Adapter::Test::Stubs.new
6
+ @heap = HeapAPI::Client.new
7
+ @heap.app_id = 'test-app-id'
8
+ @heap.faraday_adapter = :test
9
+ @heap.faraday_adapter_args = [@stubs]
10
+ end
11
+
12
+ def teardown
13
+ @stubs.verify_stubbed_calls
14
+ end
15
+
16
+ def test_add_user_properties_without_app_id
17
+ @heap.app_id = nil
18
+ exception = assert_raises RuntimeError do
19
+ @heap.add_user_properties 'test-handle', 'key' => 'value'
20
+ end
21
+ assert_equal RuntimeError, exception.class
22
+ assert_equal 'Heap app_id not set', exception.message
23
+ end
24
+
25
+ def test_add_user_properties_with_long_property_name
26
+ long_name = 'A' * 1025
27
+
28
+ exception = assert_raises ArgumentError do
29
+ @heap.add_user_properties 'test-handle', long_name => 'value'
30
+ end
31
+ assert_equal ArgumentError, exception.class
32
+ assert_equal "Property name #{long_name} too long; " +
33
+ '1025 is above the 1024-character limit', exception.message
34
+ end
35
+
36
+ def test_add_user_properties_with_long_string_property_value
37
+ long_value = 'A' * 1025
38
+ exception = assert_raises ArgumentError do
39
+ @heap.add_user_properties 'test-handle', 'long_value_name' => long_value
40
+ end
41
+ assert_equal ArgumentError, exception.class
42
+ assert_equal "Property long_value_name value \"#{long_value}\" too " +
43
+ 'long; 1025 is above the 1024-character limit', exception.message
44
+ end
45
+
46
+ def test_add_user_properties_with_long_symbol_property_value
47
+ long_value = ('A' * 1025).to_sym
48
+ exception = assert_raises ArgumentError do
49
+ @heap.add_user_properties 'test-handle', 'long_value_name' => long_value
50
+ end
51
+ assert_equal ArgumentError, exception.class
52
+ assert_equal "Property long_value_name value :#{long_value} too long; " +
53
+ '1025 is above the 1024-character limit', exception.message
54
+ end
55
+
56
+ def test_add_user_properties_with_array_property_value
57
+ exception = assert_raises ArgumentError do
58
+ @heap.add_user_properties 'test-handle', 'array_value_name' => []
59
+ end
60
+ assert_equal ArgumentError, exception.class
61
+ assert_equal 'Unsupported type for property array_value_name value []',
62
+ exception.message
63
+ end
64
+
65
+
66
+ def test_add_user_properties
67
+ @stubs.post '/api/identify' do |env|
68
+ golden_body = {
69
+ 'app_id' => 'test-app-id',
70
+ 'identity' => 'test-handle',
71
+ 'properties' => { 'foo' => 'bar', 'heap' => 'hurray' }
72
+ }
73
+ assert_equal 'application/json', env[:request_headers]['Content-Type']
74
+ assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
75
+ assert_equal golden_body, JSON.parse(env[:body])
76
+
77
+ [200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
78
+ end
79
+
80
+ assert_equal @heap, @heap.add_user_properties('test-handle',
81
+ 'foo' => 'bar', :heap => :hurray)
82
+ end
83
+
84
+ def test_add_user_properties_error
85
+ @stubs.post '/api/identify' do |env|
86
+ [400, { 'Content-Type' => 'text/plain; encoding=utf8' }, 'Bad request']
87
+ end
88
+
89
+ exception = assert_raises HeapAPI::Error do
90
+ @heap.add_user_properties 'test-handle', 'foo' => 'bar'
91
+ end
92
+
93
+ assert_equal HeapAPI::ApiError, exception.class
94
+ assert_equal 'Heap API server error: 400 Bad request', exception.message
95
+ assert_kind_of Faraday::Response, exception.response
96
+ assert_equal 400, exception.response.status
97
+ end
98
+
99
+ def test_add_user_properties_integration
100
+ @heap.app_id = '3000610572'
101
+ @heap.faraday_adapter = :net_http
102
+ @heap.faraday_adapter_args = []
103
+
104
+ assert_equal @heap, @heap.add_user_properties('test-handle',
105
+ 'language/ruby' => 1, 'heap/heap-ruby' => 1)
106
+ end
107
+
108
+ def test_add_user_properties_error_integration
109
+ @heap.faraday_adapter = :net_http
110
+ @heap.faraday_adapter_args = []
111
+
112
+ assert_raises HeapAPI::Error do
113
+ @heap.add_user_properties 'test-handle',
114
+ 'language/ruby' => 1, 'heap/heap-ruby' => 1
115
+ end
116
+ end
117
+
118
+ def test_add_user_properties_with_stubbed_connection
119
+ @heap.stubbed = true
120
+
121
+ assert_equal @heap, @heap.add_user_properties('test-handle',
122
+ 'foo' => 'bar', :heap => :hurray)
123
+ end
124
+ end
@@ -0,0 +1,78 @@
1
+ require 'helper'
2
+
3
+ class ClientTest < MiniTest::Test
4
+ def setup
5
+ @heap = HeapAPI::Client.new
6
+ end
7
+
8
+ def test_default_app_id
9
+ assert_equal nil, @heap.app_id
10
+ end
11
+
12
+ def test_default_stubbed
13
+ assert_equal false, @heap.stubbed
14
+ end
15
+
16
+ def test_default_faraday_adapter
17
+ assert_equal Faraday.default_adapter, @heap.faraday_adapter
18
+ end
19
+
20
+ def test_default_faraday_adapter_args
21
+ assert_equal([], @heap.faraday_adapter_args)
22
+ end
23
+
24
+ def test_faraday_adapter_args_setter
25
+ stubs = Faraday::Adapter::Test::Stubs.new
26
+ @heap.faraday_adapter_args = [stubs]
27
+ assert_equal([stubs], @heap.faraday_adapter_args)
28
+
29
+ @heap.faraday_adapter = :test
30
+ @heap.connection # Materialize the Faraday connection.
31
+ exception = assert_raises RuntimeError do
32
+ @heap.faraday_adapter_args = [:derp]
33
+ end
34
+ assert_equal RuntimeError, exception.class
35
+ assert_equal 'Faraday connection already initialized', exception.message
36
+ assert_equal [stubs], @heap.faraday_adapter_args
37
+ end
38
+
39
+ def test_stubbed_setter
40
+ @heap.app_id = 'test-app-id'
41
+ @heap.faraday_adapter = :net_http
42
+
43
+ @heap.stubbed = true
44
+ assert_equal @heap, @heap.track('test_stubbed_setter', 'test-handle',
45
+ 'language' => 'ruby', 'project' => 'heap/heap-ruby')
46
+ stubbed_connection = @heap.connection
47
+
48
+ @heap.stubbed = false
49
+ assert_raises HeapAPI::Error do
50
+ @heap.track 'test_stubbed_setter', 'test-handle',
51
+ 'language' => 'ruby', 'project' => 'heap/heap-ruby'
52
+ end
53
+ live_connection = @heap.connection
54
+
55
+ @heap.stubbed = true
56
+ assert_equal stubbed_connection, @heap.connection
57
+
58
+ @heap.stubbed = false
59
+ assert_equal live_connection, @heap.connection
60
+ end
61
+
62
+ def test_default_user_agent
63
+ assert_match(/^heap-ruby\/[0-9.]+ /, @heap.user_agent)
64
+ assert_match(/ faraday\/[0-9.]+ /, @heap.user_agent)
65
+ assert_match(/ ruby\/[0-9.]+ \(.+\)$/, @heap.user_agent)
66
+ end
67
+
68
+ def test_user_agent_setter
69
+ @heap.user_agent = 'sparky/4.2'
70
+ assert_equal 'sparky/4.2', @heap.user_agent
71
+ end
72
+
73
+ def test_constructor_options
74
+ heap = Heap.new :app_id => 'test-app-id', :stubbed => true
75
+ assert_equal 'test-app-id', heap.app_id
76
+ assert_equal true, heap.stubbed
77
+ end
78
+ end
@@ -0,0 +1,151 @@
1
+ require 'helper'
2
+
3
+ class ClientTrackTest < MiniTest::Test
4
+ def setup
5
+ @stubs = Faraday::Adapter::Test::Stubs.new do |stub|
6
+ end
7
+ @heap = HeapAPI::Client.new
8
+ @heap.app_id = 'test-app-id'
9
+ @heap.faraday_adapter = :test
10
+ @heap.faraday_adapter_args = [@stubs]
11
+ end
12
+
13
+ def teardown
14
+ @stubs.verify_stubbed_calls
15
+ end
16
+
17
+ def test_track_without_app_id
18
+ @heap.app_id = nil
19
+ exception = assert_raises RuntimeError do
20
+ @heap.track 'test_track_without_app_id', 'test-handle'
21
+ end
22
+ assert_equal RuntimeError, exception.class
23
+ assert_equal 'Heap app_id not set', exception.message
24
+ end
25
+
26
+ def test_track_without_event_name
27
+ exception = assert_raises ArgumentError do
28
+ @heap.track '', 'test-handle'
29
+ end
30
+ assert_equal ArgumentError, exception.class
31
+ assert_equal 'Missing or empty event name', exception.message
32
+ end
33
+
34
+ def test_track_with_long_event_name
35
+ exception = assert_raises ArgumentError do
36
+ @heap.track 'A' * 1025, 'test-handle'
37
+ end
38
+ assert_equal ArgumentError, exception.class
39
+ assert_equal 'Event name too long', exception.message
40
+ end
41
+
42
+ def test_track_with_long_property_name
43
+ long_name = 'A' * 1025
44
+ exception = assert_raises ArgumentError do
45
+ @heap.track 'test_track_with_long_property_name', 'test-handle',
46
+ long_name => 'value'
47
+ end
48
+ assert_equal ArgumentError, exception.class
49
+ assert_equal "Property name #{long_name} too long; " +
50
+ "1025 is above the 1024-character limit", exception.message
51
+ end
52
+
53
+ def test_track_with_long_string_property_value
54
+ long_value = 'A' * 1025
55
+ exception = assert_raises ArgumentError do
56
+ @heap.track 'test_track_with_long_string_property_value', 'test-handle',
57
+ 'long_value_name' => long_value
58
+ end
59
+ assert_equal ArgumentError, exception.class
60
+ assert_equal "Property long_value_name value \"#{long_value}\" too " +
61
+ 'long; 1025 is above the 1024-character limit', exception.message
62
+ end
63
+
64
+ def test_track_with_long_symbol_property_value
65
+ long_value = ('A' * 1025).to_sym
66
+ exception = assert_raises ArgumentError do
67
+ @heap.track 'test_track_with_long_symbol_property_value', 'test-handle',
68
+ 'long_value_name' => long_value
69
+ end
70
+ assert_equal ArgumentError, exception.class
71
+ assert_equal "Property long_value_name value :#{long_value} too long; " +
72
+ '1025 is above the 1024-character limit', exception.message
73
+ end
74
+
75
+ def test_track
76
+ @stubs.post '/api/track' do |env|
77
+ golden_body = {
78
+ 'app_id' => 'test-app-id',
79
+ 'identity' => 'test-handle',
80
+ 'event' => 'test_track',
81
+ }
82
+ assert_equal 'application/json', env[:request_headers]['Content-Type']
83
+ assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
84
+ assert_equal golden_body, JSON.parse(env[:body])
85
+
86
+ [200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
87
+ end
88
+
89
+ assert_equal @heap, @heap.track('test_track', 'test-handle')
90
+ end
91
+
92
+ def test_track_with_properties
93
+ @stubs.post '/api/track' do |env|
94
+ golden_body = {
95
+ 'app_id' => 'test-app-id',
96
+ 'identity' => 'test-handle',
97
+ 'event' => 'test_track_with_properties',
98
+ 'properties' => { 'foo' => 'bar', 'heap' => 'hurray' }
99
+ }
100
+ assert_equal 'application/json', env[:request_headers]['Content-Type']
101
+ assert_equal @heap.user_agent, env[:request_headers]['User-Agent']
102
+ assert_equal golden_body, JSON.parse(env[:body])
103
+
104
+ [200, { 'Content-Type' => 'text/plain; encoding=utf8' }, '']
105
+ end
106
+
107
+ assert_equal @heap, @heap.track('test_track_with_properties',
108
+ 'test-handle','foo' => 'bar', :heap => :hurray)
109
+ end
110
+
111
+ def test_track_error
112
+ @stubs.post '/api/track' do |env|
113
+ [400, { 'Content-Type' => 'text/plain; encoding=utf8' }, 'Bad request']
114
+ end
115
+
116
+ exception = assert_raises HeapAPI::Error do
117
+ @heap.track('test-handle', 'test_track')
118
+ end
119
+ assert_equal HeapAPI::ApiError, exception.class
120
+ assert_equal 'Heap API server error: 400 Bad request', exception.message
121
+ assert_kind_of Faraday::Response, exception.response
122
+ assert_equal 400, exception.response.status
123
+ end
124
+
125
+ def test_track_integration
126
+ @heap.app_id = '3000610572'
127
+ @heap.faraday_adapter = :net_http
128
+ @heap.faraday_adapter_args = []
129
+
130
+ assert_equal @heap, @heap.track('test_track_integration', 'test-handle',
131
+ 'language' => 'ruby', 'project' => 'heap/heap-ruby')
132
+ end
133
+
134
+ def test_track_error_integration
135
+ @heap.faraday_adapter = :net_http
136
+ @heap.faraday_adapter_args = []
137
+
138
+ assert_raises HeapAPI::Error do
139
+ @heap.track('test_track_integration', 'test-handle',
140
+ 'language' => 'ruby', 'project' => 'heap/heap-ruby')
141
+ end
142
+ end
143
+
144
+
145
+ def test_track_with_stubbed_connection
146
+ @heap.stubbed = true
147
+
148
+ assert_equal @heap, @heap.add_user_properties('test-handle',
149
+ 'foo' => 'bar', :heap => :hurray)
150
+ end
151
+ end
@@ -0,0 +1,23 @@
1
+ require 'helper'
2
+
3
+ class HeapTest < MiniTest::Test
4
+ def test_heap_app_id
5
+ assert_equal nil, Heap.app_id
6
+ begin
7
+ Heap.app_id = 'global-app-id'
8
+ assert_equal 'global-app-id', Heap.app_id
9
+ ensure
10
+ Heap.app_id = nil
11
+ end
12
+ end
13
+
14
+ def test_heap_stubbed
15
+ assert_equal false, Heap.stubbed
16
+ end
17
+
18
+ def test_heap_new
19
+ client = Heap.new :app_id => 'local-app-id', :stubbed => true
20
+ assert_equal 'local-app-id', client.app_id
21
+ assert_equal true, client.stubbed
22
+ end
23
+ end