meta_events 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,93 @@
1
+ describe ::MetaEvents::Definition::Version do
2
+ let(:definition_set) do
3
+ out = double("definition_set")
4
+ allow(out).to receive(:kind_of?).with(::MetaEvents::Definition::DefinitionSet).and_return(true)
5
+ allow(out).to receive(:global_events_prefix).with().and_return("gep")
6
+ out
7
+ end
8
+
9
+ let(:klass) { ::MetaEvents::Definition::Version }
10
+ let(:instance) { klass.new(definition_set, 3, "2014-02-03") }
11
+
12
+ it "should require valid parameters for construction" do
13
+ expect { klass.new(double("not-a-definition-set"), 1, "2014-01-01") }.to raise_error(ArgumentError)
14
+ expect { klass.new(definition_set, "foo", "2014-01-01") }.to raise_error(ArgumentError)
15
+
16
+ # Ruby 1.8.x's Time.parse method will accept "foo" for Time.parse and just return Time.now --
17
+ # which is deeply unfortunate, but there's really no good way around it.
18
+ unless RUBY_VERSION =~ /^1\.8\./
19
+ expect { klass.new(definition_set, 1, "foo") }.to raise_error(ArgumentError)
20
+ end
21
+
22
+ expect { klass.new(definition_set, 1, nil) }.to raise_error
23
+ expect { klass.new(definition_set, 1, "2014-01-01", :foo => :bar) }.to raise_error(ArgumentError, /foo/i)
24
+ end
25
+
26
+ it "should return the definition set, number, and introduction time" do
27
+ expect(instance.definition_set).to be(definition_set)
28
+ expect(instance.number).to eq(3)
29
+ expect(instance.introduced).to eq(Time.parse("2014-02-03"))
30
+ end
31
+
32
+ it "should evaluate its block in its own context" do
33
+ expect_any_instance_of(klass).to receive(:foobar).once.with(:bonk)
34
+ klass.new(definition_set, 3, "2014-02-03") { foobar(:bonk) }
35
+ end
36
+
37
+ it "should return the prefix correctly" do
38
+ instance.prefix.should == "gep3_"
39
+ end
40
+
41
+ context "with one category" do
42
+ let(:category) do
43
+ out = double("category")
44
+ allow(out).to receive(:name).with().and_return(:quux)
45
+ out
46
+ end
47
+
48
+ it "should be able to create a new category, and retrieve it" do
49
+ blk = lambda { :whatever }
50
+ expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, ' FooBar ', :bar => :baz, &blk).and_return(category)
51
+ instance.category(' FooBar ', :bar => :baz, &blk)
52
+
53
+ expect(instance.category_named(:quux)).to be(category)
54
+ end
55
+
56
+ it "should not allow creating duplicate categories" do
57
+ expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, :quux, { }).and_return(category)
58
+ instance.category(:quux)
59
+
60
+ category_2 = double("category-2")
61
+ allow(category_2).to receive(:name).with().and_return(:quux)
62
+ expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, :baz, { }).and_return(category_2)
63
+ expect { instance.category(:baz) }.to raise_error(ArgumentError, /baz/i)
64
+ end
65
+
66
+ it "should allow retrieving the category, and normalize the name" do
67
+ expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, :quux, { }).and_return(category)
68
+ instance.category(:quux)
69
+
70
+ instance.category_named(' QuuX ').should be(category)
71
+ end
72
+
73
+ it "should delegate to the category on #fetch_event" do
74
+ expect(::MetaEvents::Definition::Category).to receive(:new).once.with(instance, :quux, { }).and_return(category)
75
+ instance.category(:quux)
76
+
77
+ expect(category).to receive(:event_named).once.with(:foobar).and_return(:bonk)
78
+ instance.fetch_event(:quux, :foobar).should == :bonk
79
+ end
80
+ end
81
+
82
+ it "should return the #retired_at properly" do
83
+ expect(instance.retired_at).to be_nil
84
+
85
+ new_instance = klass.new(definition_set, 4, "2014-01-01", :retired_at => "2015-01-01")
86
+ expect(new_instance.retired_at).to eq(Time.parse("2015-01-01"))
87
+ end
88
+
89
+ it "should turn itself into a string reasonably" do
90
+ expect(instance.to_s).to match(/Version/)
91
+ expect(instance.to_s).to match(/3/)
92
+ end
93
+ end
@@ -0,0 +1,466 @@
1
+ require 'active_support/core_ext/numeric/time'
2
+ require 'ipaddr'
3
+
4
+ describe MetaEvents::Tracker do
5
+ subject(:klass) { MetaEvents::Tracker }
6
+ let(:definition_set) do
7
+ MetaEvents::Definition::DefinitionSet.new(:global_events_prefix => "xy") do
8
+ version 1, '2014-01-31' do
9
+ category :foo do
10
+ event :bar, '2014-01-31', 'this is bar'
11
+ event :baz, '2014-01-31', 'this is baz'
12
+ event :nolonger, '2014-01-31', 'should be retired', :retired_at => '2020-01-01'
13
+ end
14
+ end
15
+
16
+ version 2, '2014-06-01' do
17
+ category :foo do
18
+ event :quux, '2014-01-31', 'this is quux'
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ let(:receiver_1) do
25
+ out = double("receiver_1")
26
+ @receiver_1_calls = [ ]
27
+ rc1c = @receiver_1_calls
28
+ allow(out).to receive(:track) { |*args| rc1c << args }
29
+ out
30
+ end
31
+
32
+ let(:receiver_2) do
33
+ out = double("receiver_2")
34
+ @receiver_2_calls = [ ]
35
+ rc2c = @receiver_2_calls
36
+ allow(out).to receive(:track) { |*args| rc2c << args }
37
+ out
38
+ end
39
+
40
+ def new_instance(*args)
41
+ out = klass.new(*args)
42
+ out.event_receivers << receiver_1
43
+ out
44
+ end
45
+
46
+ before :each do
47
+ ::MetaEvents::Tracker.default_event_receivers = [ ]
48
+ @user = tep_object(:name => 'wilfred', :hometown => 'Fridley')
49
+ @distinct_id = rand(1_000_000_000)
50
+ @remote_ip = "128.12.34.56"
51
+ @instance = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1, :implicit_properties => { :user => @user } )
52
+ end
53
+
54
+ after :each do
55
+ expect(@receiver_1_calls).to eq([ ])
56
+ expect((@receiver_2_calls || [ ])).to eq([ ])
57
+ end
58
+
59
+ def expect_event(event_name, event_properties, options = { })
60
+ receiver_name = options[:receiver_name] || "receiver_1"
61
+ expected_distinct_id = if options.has_key?(:distinct_id) then options[:distinct_id] else @distinct_id end
62
+
63
+ calls = instance_variable_get("@#{receiver_name}_calls")
64
+
65
+ expect(calls.length).to be > 0
66
+ next_event = calls.shift
67
+ expect(next_event[0]).to eq(expected_distinct_id)
68
+ expect(next_event[1]).to eq(event_name)
69
+ expect(next_event[2]).to eq(event_properties)
70
+ end
71
+
72
+ def tep_object(props)
73
+ out = double("tep_object")
74
+ allow(out).to receive(:to_event_properties).with().and_return(props)
75
+ out
76
+ end
77
+
78
+ describe "#initialize" do
79
+ it "should validate its arguments" do
80
+ expect { new_instance(@distinct_id, nil, :version => 1, :definitions => definition_set, :foo => :bar) }.to raise_error(ArgumentError, /foo/i)
81
+ expect { new_instance(@distinct_id, "whatever", :version => 1, :definitions => definition_set) }.to raise_error(ArgumentError)
82
+ expect { new_instance(@distinct_id, /foobar/, :version => 1, :definitions => definition_set) }.to raise_error(ArgumentError, /foobar/i)
83
+ end
84
+
85
+ it "should allow a nil distinct_id" do
86
+ expect { new_instance(nil, nil, :definitions => definition_set, :version => 1) }.not_to raise_error
87
+ end
88
+
89
+ it "should not allow an invalid distinct_id" do
90
+ expect { new_instance(/foobar/, nil, :definitions => definition_set, :version => 1) }.to raise_error(ArgumentError)
91
+ end
92
+
93
+ it "should be able to read definitions from a file" do
94
+ require 'tempfile'
95
+
96
+ f = Tempfile.new('tracker_events')
97
+ begin
98
+ f.puts <<-EOS
99
+ global_events_prefix :fb
100
+
101
+ version 1, '2014-02-19' do
102
+ category :baz do
103
+ event :quux, '2014-01-31', 'this is quux'
104
+ end
105
+ end
106
+ EOS
107
+ f.close
108
+
109
+ the_instance = new_instance(@distinct_id, nil, :definitions => f.path, :version => 1)
110
+ expect { the_instance.event!(:baz, :quux, :foo => :bar) }.not_to raise_error
111
+ expect_event("fb1_baz_quux", { 'foo' => 'bar' })
112
+ expect { the_instance.event!(:baz, :foo, :foo => :bar) }.to raise_error
113
+ ensure
114
+ f.close
115
+ f.unlink
116
+ end
117
+ end
118
+ end
119
+
120
+ describe "#distinct_id" do
121
+ it "should allow reading the distinct ID" do
122
+ expect(@instance.distinct_id).to eq(@distinct_id)
123
+ end
124
+
125
+ it "should allow changing the distinct ID at runtime" do
126
+ i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
127
+ i.event!(:foo, :bar, { })
128
+ expect_event('xy1_foo_bar', { }, :distinct_id => @distinct_id)
129
+ i.distinct_id = '12345yoyoyo'
130
+ expect(i.distinct_id).to eq('12345yoyoyo')
131
+ i.event!(:foo, :bar, { })
132
+ expect_event('xy1_foo_bar', { }, :distinct_id => '12345yoyoyo')
133
+ end
134
+
135
+ it "should allow changing the distinct ID to nil at runtime" do
136
+ i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
137
+ expect(i.distinct_id).to eq(@distinct_id)
138
+ i.distinct_id = nil
139
+ expect(i.distinct_id).to eq(nil)
140
+ i.event!(:foo, :bar, { })
141
+ expect_event('xy1_foo_bar', { }, :distinct_id => nil)
142
+ end
143
+
144
+ it "should not allow setting the distinct ID to an unsupported object" do
145
+ i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
146
+ expect { i.distinct_id = /foobar/ }.to raise_error(ArgumentError)
147
+ expect(i.distinct_id).to eq(@distinct_id)
148
+ end
149
+ end
150
+
151
+ describe "#track_event" do
152
+ it "should allow firing a valid event" do
153
+ i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
154
+ i.event!(:foo, :bar, { })
155
+ expect_event('xy1_foo_bar', { })
156
+ end
157
+
158
+ describe "ip-address passing" do
159
+ it "should pass through an IPv4 String IP" do
160
+ i = new_instance(@distinct_id, "138.93.206.193", :definitions => definition_set, :version => 1)
161
+ i.event!(:foo, :bar, { })
162
+ expect_event('xy1_foo_bar', { 'ip' => "138.93.206.193" })
163
+ end
164
+
165
+ it "should pass through an IPv6 String IP" do
166
+ i = new_instance(@distinct_id, "2607:f0d0:1002:0051:0000:0000:0000:0004", :definitions => definition_set, :version => 1)
167
+ i.event!(:foo, :bar, { })
168
+ expect_event('xy1_foo_bar', { 'ip' => "2607:f0d0:1002:51::4" })
169
+ end
170
+
171
+ it "should pass through an Integer IP" do
172
+ i = new_instance(@distinct_id, 2321403585, :definitions => definition_set, :version => 1)
173
+ i.event!(:foo, :bar, { })
174
+ expect_event('xy1_foo_bar', { 'ip' => "138.93.206.193" })
175
+ end
176
+
177
+ it "should pass through an IPv4 IPAddr" do
178
+ i = new_instance(@distinct_id, IPAddr.new("138.93.206.193"), :definitions => definition_set, :version => 1)
179
+ i.event!(:foo, :bar, { })
180
+ expect_event('xy1_foo_bar', { 'ip' => "138.93.206.193" })
181
+ end
182
+
183
+ it "should pass through an IPv6 IPAddr" do
184
+ i = new_instance(@distinct_id, IPAddr.new("2607:f0d0:1002:0051:0000:0000:0000:0004"), :definitions => definition_set, :version => 1)
185
+ i.event!(:foo, :bar, { })
186
+ expect_event('xy1_foo_bar', { 'ip' => "2607:f0d0:1002:51::4" })
187
+ end
188
+ end
189
+
190
+ it "should include the distinct_id set in the constructor" do
191
+ i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
192
+ i.event!(:foo, :bar, { })
193
+ expect_event('xy1_foo_bar', { }, :distinct_id => @distinct_id)
194
+ end
195
+
196
+ it "should let you override the IP address on a per-event basis" do
197
+ i = new_instance(@distinct_id, "138.93.206.193", :definitions => definition_set, :version => 1)
198
+ i.event!(:foo, :bar, { 'ip' => '203.196.4.32' })
199
+ expect_event('xy1_foo_bar', { 'ip' => '203.196.4.32' }, :distinct_id => @distinct_id)
200
+ end
201
+
202
+ it "should allow overriding the distinct_id set in the constructor" do
203
+ i = new_instance(@distinct_id, nil, :definitions => definition_set, :version => 1)
204
+ i.event!(:foo, :bar, { :distinct_id => 12345 })
205
+ expect_event('xy1_foo_bar', { }, :distinct_id => 12345)
206
+ end
207
+
208
+ it "should allow a nil distinct_id" do
209
+ i = new_instance(nil, nil, :definitions => definition_set, :version => 1)
210
+ i.event!(:foo, :bar, { })
211
+ expect_event('xy1_foo_bar', { }, :distinct_id => nil)
212
+ end
213
+
214
+ it "should allow a String distinct_id" do
215
+ i = new_instance("foobarfoobar", nil, :definitions => definition_set, :version => 1)
216
+ i.event!(:foo, :bar, { })
217
+ expect_event('xy1_foo_bar', { }, :distinct_id => 'foobarfoobar')
218
+ end
219
+
220
+ it "should send the event to both receivers if asked" do
221
+ @instance.event_receivers = [ receiver_1, receiver_2 ]
222
+ @instance.event!(:foo, :bar, { })
223
+
224
+ expect_event("xy1_foo_bar", { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley' }, :receiver_name => :receiver_1)
225
+ expect_event("xy1_foo_bar", { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley' }, :receiver_name => :receiver_2)
226
+ end
227
+
228
+ it "should clone the class's default-receiver list on creation" do
229
+ expect(klass.default_event_receivers).to eq([ ])
230
+
231
+ begin
232
+ klass.default_event_receivers = [ receiver_2 ]
233
+ i = klass.new(@distinct_id, nil, :definitions => definition_set, :version => 1)
234
+ i.event!(:foo, :bar, { })
235
+ expect_event('xy1_foo_bar', { }, :receiver_name => :receiver_2)
236
+ ensure
237
+ klass.default_event_receivers = [ ]
238
+ end
239
+ end
240
+
241
+ it "should allow overriding the list of receivers in the constructor" do
242
+ i = klass.new(@distinct_id, nil, :definitions => definition_set, :version => 1, :event_receivers => [ receiver_2 ])
243
+ i.event!(:foo, :bar, { })
244
+ expect_event('xy1_foo_bar', { }, :receiver_name => :receiver_2)
245
+ end
246
+
247
+ it "should use the class's list of default definitions by default" do
248
+ klass.default_definitions = MetaEvents::Definition::DefinitionSet.new(:global_events_prefix => "zz") do
249
+ version 1, '2014-01-31' do
250
+ category :marph do
251
+ event :bonk, '2014-01-31', 'this is bar'
252
+ end
253
+ end
254
+ end
255
+
256
+ i = klass.new(@distinct_id, nil, :version => 1, :event_receivers => receiver_1)
257
+ i.event!(:marph, :bonk)
258
+ expect_event('zz1_marph_bonk', { })
259
+ end
260
+
261
+ it "should pick up the default version from the class" do
262
+ klass.default_version = 2
263
+
264
+ i = klass.new(@distinct_id, nil, :event_receivers => receiver_1, :definitions => definition_set)
265
+ i.event!(:foo, :quux)
266
+ expect_event("xy2_foo_quux", { })
267
+ end
268
+
269
+ it "should allow firing a valid event, and include implicit properties" do
270
+ @instance.event!(:foo, :bar, { })
271
+ expect_event('xy1_foo_bar', { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley' })
272
+ end
273
+
274
+ it "should not allow firing an event that doesn't exist" do
275
+ expect { @instance.event!(:foo, :quux, { }) }.to raise_error(ArgumentError, /quux/i)
276
+ end
277
+
278
+ it "should not allow firing a category that doesn't exist" do
279
+ expect { @instance.event!(:bogus, :signed_up, { }) }.to raise_error(ArgumentError, /bogus/i)
280
+ end
281
+
282
+ it "should include defined properties with the event" do
283
+ @instance.event!(:foo, :bar, { :awesomeness => 123, :foo => 'bar' })
284
+ expect_event('xy1_foo_bar', { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley', 'awesomeness' => 123, 'foo' => 'bar' })
285
+ end
286
+
287
+ it "should fail if there's a circular reference" do
288
+ circular = { :foo => :bar }
289
+ circular[:baz] = circular
290
+
291
+ expect { @instance.event!(:foo, :bar, circular) }.to raise_error(/circular/i)
292
+ end
293
+
294
+ it "should pass through lots of different kinds of properties" do
295
+ the_time = Time.parse("2008-09-04 3:46:12 PM -08:00")
296
+ @instance.event!(:foo, :bar, {
297
+ :num1 => 42,
298
+ :num2 => 6.0221413e+23,
299
+ :true => true,
300
+ :false => false,
301
+ :string => 'foobar',
302
+ :symbol => :bazbar,
303
+ :time_interval => 3.months,
304
+ :pos_infinity => (+1.0/0.0),
305
+ :nan => (0.0/0.0),
306
+ :neg_infinity => (-1.0/0.0),
307
+ :time => the_time,
308
+ :stuff => [ "foo", :bar, 123, -9.45e+17, the_time, false, nil, true, " BoNk " ]
309
+ })
310
+ expect_event('xy1_foo_bar', {
311
+ 'user_name' => 'wilfred',
312
+ 'user_hometown' => 'Fridley',
313
+ 'num1' => 42,
314
+ 'num2' => 6.0221413e+23,
315
+ 'true' => true,
316
+ 'false' => false,
317
+ 'string' => 'foobar',
318
+ 'symbol' => 'bazbar',
319
+ 'time_interval' => 7776000,
320
+ 'pos_infinity' => "+infinity",
321
+ 'nan' => 'NaN',
322
+ 'neg_infinity' => "-infinity",
323
+ 'time' => "2008-09-04T23:46:12",
324
+ 'stuff' => [ "foo", "bar", 123, -9.45e+17, "2008-09-04T23:46:12", false, nil, true, "BoNk" ],
325
+ })
326
+ end
327
+
328
+ it "should let me override implicit properties with user-defined ones" do
329
+ @instance.event!(:foo, :bar, { :user_name => 'bongo' })
330
+ expect_event('xy1_foo_bar', { 'user_name' => 'bongo', 'user_hometown' => 'Fridley' })
331
+ end
332
+
333
+ it "should allow nested properties in the event, and expand them out" do
334
+ @instance.event!(:foo, :bar, { :location => { :city => 'Edina', :zip => 55343 } })
335
+ expect_event('xy1_foo_bar', { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley', 'location_city' => 'Edina', 'location_zip' => 55343 })
336
+ end
337
+
338
+ it "should accept nested properties from #to_event_properties, and expand them out" do
339
+ @instance.event!(:foo, :bar, { :location => tep_object(:city => 'Edina', :zip => 55343 )})
340
+ expect_event('xy1_foo_bar', { 'user_name' => 'wilfred', 'user_hometown' => 'Fridley', 'location_city' => 'Edina', 'location_zip' => 55343 })
341
+ end
342
+
343
+ it "should not allow firing a retired event" do
344
+ expect { @instance.event!(:foo, :nolonger, { }) }.to raise_error(::MetaEvents::Definition::DefinitionSet::RetiredEventError)
345
+ end
346
+ end
347
+
348
+ describe "#effective_properties" do
349
+ it "should validate the event" do
350
+ expect { @instance.effective_properties(:foo, :whatever) }.to raise_error(ArgumentError)
351
+ expect { @instance.effective_properties(:foo, :nolonger) }.to raise_error(::MetaEvents::Definition::DefinitionSet::RetiredEventError)
352
+ end
353
+
354
+ it "should include the fully-qualified event name" do
355
+ expect(@instance.effective_properties(:foo, :bar)[:event_name]).to eq('xy1_foo_bar')
356
+ end
357
+
358
+ it "should include the distinct ID" do
359
+ expect(@instance.effective_properties(:foo, :bar)[:distinct_id]).to eq(@distinct_id)
360
+ end
361
+
362
+ it "should include a distinct ID of nil if there is none" do
363
+ i = new_instance(nil, nil, :definitions => definition_set, :version => 1)
364
+ h = i.effective_properties(:foo, :bar)
365
+ expect(h.has_key?(:distinct_id)).to be_true
366
+ expect(h[:distinct_id]).to be_nil
367
+ end
368
+
369
+ it "should include the set of implicit and explicit properties" do
370
+ expect(@instance.effective_properties(:foo, :bar)[:properties]).to eq('user_name' => 'wilfred', 'user_hometown' => 'Fridley')
371
+ props = @instance.effective_properties(:foo, :bar, :baz => { :a => 1, :b => 'hoo' }, :user_name => 'bongo')[:properties]
372
+ expect(props).to eq('user_name' => 'bongo', 'user_hometown' => 'Fridley', 'baz_a' => 1, 'baz_b' => 'hoo')
373
+ end
374
+
375
+ it "should include no additional keys" do
376
+ expect(@instance.effective_properties(:foo, :bar).keys.sort_by(&:to_s)).to eq(%w{distinct_id event_name properties}.map(&:to_sym).sort_by(&:to_s))
377
+ end
378
+ end
379
+
380
+ describe "#merge_properties" do
381
+ def expand(hash)
382
+ out = { }
383
+ klass.merge_properties(out, hash, "", 0)
384
+ out
385
+ end
386
+
387
+ def expand_scalar(value)
388
+ expand({ 'foo' => value })['foo']
389
+ end
390
+
391
+ it "should return proper values for scalars" do
392
+ expect(expand_scalar('foo')).to eq('foo')
393
+ expect(expand_scalar(' FoO ')).to eq('FoO')
394
+
395
+ expect(expand_scalar(:foo)).to eq('foo')
396
+ expect(expand_scalar(:' FoOO ')).to eq('FoOO')
397
+
398
+ expect(expand_scalar(123)).to eq(123)
399
+ expect(expand_scalar(4.2867)).to eq(4.2867)
400
+ expect(expand_scalar(1234567890123456789012345678901234567890123456789012345678901234567890)).to eq(1234567890123456789012345678901234567890123456789012345678901234567890)
401
+ expect(expand_scalar(true)).to eq(true)
402
+ expect(expand_scalar(false)).to eq(false)
403
+ expect(expand_scalar(nil)).to eq(nil)
404
+ end
405
+
406
+ it "should raise an error for unknown data, including Hashes" do
407
+ expect { expand_scalar(/foobar/) }.to raise_error(ArgumentError)
408
+ expect { expand_scalar([ "a", "b", /foo/ ]) }.to raise_error(ArgumentError)
409
+ end
410
+
411
+ it "should stringify symbols on both keys and values" do
412
+ expect(expand({ :foo => :bar })).to eq({ 'foo' => 'bar' })
413
+ end
414
+
415
+ it "should correctly expand simple hashes" do
416
+ expect(expand({ :foo => 'bar' })).to eq({ 'foo' => 'bar' })
417
+ expect(expand({ :zzz => ' bonkO ', 'bar' => :' baZZ ' })).to eq({ 'zzz' => 'bonkO', 'bar' => 'baZZ' })
418
+ end
419
+
420
+ it "should recursively expand hashes" do
421
+ expect(expand({ :foo => { :bar => ' whatEVs '} })).to eq({ 'foo_bar' => 'whatEVs' })
422
+ expect { expand({ :foo => { :bar => [ 1, 2, /foo/ ] } }) }.to raise_error(ArgumentError)
423
+ end
424
+
425
+ it "should call #to_event_properties for any object, and recursively expand that" do
426
+ expect(expand(:baz => tep_object({ :foo => ' BaR '}))).to eq({ 'baz_foo' => 'BaR' })
427
+ expect(expand(:baz => tep_object({ :foo => { :bar => ' yo yo yo '} }))).to eq({ 'baz_foo_bar' => 'yo yo yo' })
428
+
429
+ subsidiary = tep_object({ :bar => :baz })
430
+ expect(expand(:baz => tep_object({ :foo => subsidiary }))).to eq({ 'baz_foo_bar' => 'baz' })
431
+ end
432
+
433
+ it "should raise if it detects a property-name conflict" do
434
+ expect { expand(:foo_bar => :quux1, :foo => { :bar => :quux }) }.to raise_error(MetaEvents::Tracker::PropertyCollisionError)
435
+ end
436
+ end
437
+
438
+ describe "#normalize_scalar_property_value" do
439
+ it "should return the correct results for scalars" do
440
+ infinity = (1.0 / 0.0)
441
+ nan = (0.0 / 0.0)
442
+
443
+ t = Time.parse("2008-09-04 3:46:12 PM -08:00")
444
+ {
445
+ nil => nil,
446
+ true => true,
447
+ false => false,
448
+ 3 => 3,
449
+ 42.5e+17 => 42.5e+17,
450
+ 3.months => 7776000,
451
+ infinity => "+infinity",
452
+ nan => "NaN",
453
+ -infinity => "-infinity",
454
+ :foobar => 'foobar',
455
+ :' FooBar ' => 'FooBar',
456
+ ' FooBar ' => 'FooBar',
457
+ t => "2008-09-04T23:46:12",
458
+ [ "foo", :bar, 123, -9.45e+17, t, false, nil, true, " BoNk " ] => [ "foo", "bar", 123, -9.45e+17, "2008-09-04T23:46:12", false, nil, true, "BoNk" ],
459
+ /foobar/ => :invalid_property_value,
460
+ Object.new => :invalid_property_value
461
+ }.each do |input, output|
462
+ expect(klass.normalize_scalar_property_value(input)).to eq(output)
463
+ end
464
+ end
465
+ end
466
+ end