pulse-meter-client-backport 0.1.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.
Files changed (50) hide show
  1. data/.gitignore +19 -0
  2. data/.rbenv-version +1 -0
  3. data/.rspec +1 -0
  4. data/.rvmrc +1 -0
  5. data/.travis.yml +3 -0
  6. data/Gemfile +3 -0
  7. data/LICENSE +22 -0
  8. data/Procfile +3 -0
  9. data/README.md +98 -0
  10. data/Rakefile +53 -0
  11. data/bin/pulse +6 -0
  12. data/examples/readme_client_example.rb +52 -0
  13. data/lib/pulse-meter.rb +17 -0
  14. data/lib/pulse-meter/mixins/dumper.rb +76 -0
  15. data/lib/pulse-meter/mixins/utils.rb +93 -0
  16. data/lib/pulse-meter/sensor.rb +44 -0
  17. data/lib/pulse-meter/sensor/base.rb +75 -0
  18. data/lib/pulse-meter/sensor/counter.rb +36 -0
  19. data/lib/pulse-meter/sensor/hashed_counter.rb +31 -0
  20. data/lib/pulse-meter/sensor/indicator.rb +33 -0
  21. data/lib/pulse-meter/sensor/timeline.rb +180 -0
  22. data/lib/pulse-meter/sensor/timelined/average.rb +26 -0
  23. data/lib/pulse-meter/sensor/timelined/counter.rb +16 -0
  24. data/lib/pulse-meter/sensor/timelined/hashed_counter.rb +22 -0
  25. data/lib/pulse-meter/sensor/timelined/max.rb +25 -0
  26. data/lib/pulse-meter/sensor/timelined/median.rb +14 -0
  27. data/lib/pulse-meter/sensor/timelined/min.rb +25 -0
  28. data/lib/pulse-meter/sensor/timelined/percentile.rb +31 -0
  29. data/lib/pulse-meter/version.rb +3 -0
  30. data/pulse-meter-client-backport.gemspec +33 -0
  31. data/spec/pulse_meter/mixins/dumper_spec.rb +141 -0
  32. data/spec/pulse_meter/mixins/utils_spec.rb +125 -0
  33. data/spec/pulse_meter/sensor/base_spec.rb +97 -0
  34. data/spec/pulse_meter/sensor/counter_spec.rb +54 -0
  35. data/spec/pulse_meter/sensor/hashed_counter_spec.rb +39 -0
  36. data/spec/pulse_meter/sensor/indicator_spec.rb +43 -0
  37. data/spec/pulse_meter/sensor/timeline_spec.rb +45 -0
  38. data/spec/pulse_meter/sensor/timelined/average_spec.rb +6 -0
  39. data/spec/pulse_meter/sensor/timelined/counter_spec.rb +6 -0
  40. data/spec/pulse_meter/sensor/timelined/hashed_counter_spec.rb +8 -0
  41. data/spec/pulse_meter/sensor/timelined/max_spec.rb +7 -0
  42. data/spec/pulse_meter/sensor/timelined/median_spec.rb +7 -0
  43. data/spec/pulse_meter/sensor/timelined/min_spec.rb +7 -0
  44. data/spec/pulse_meter/sensor/timelined/percentile_spec.rb +17 -0
  45. data/spec/pulse_meter_spec.rb +16 -0
  46. data/spec/shared_examples/timeline_sensor.rb +274 -0
  47. data/spec/shared_examples/timelined_subclass.rb +23 -0
  48. data/spec/spec_helper.rb +21 -0
  49. data/spec/support/matchers.rb +45 -0
  50. metadata +276 -0
@@ -0,0 +1,54 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Counter do
4
+ let(:name){ :some_counter }
5
+ let(:sensor){ described_class.new(name) }
6
+ let(:redis){ PulseMeter.redis }
7
+
8
+ describe "#event" do
9
+ it "should increment sensor value by passed value" do
10
+ expect{ sensor.event(10) }.to change{ sensor.value }.from(0).to(10)
11
+ expect{ sensor.event(15) }.to change{ sensor.value }.from(10).to(25)
12
+ end
13
+
14
+ it "should truncate increment value" do
15
+ expect{ sensor.event(10.4) }.to change{ sensor.value }.from(0).to(10)
16
+ expect{ sensor.event(15.1) }.to change{ sensor.value }.from(10).to(25)
17
+ end
18
+ end
19
+
20
+ describe "#value_key" do
21
+ it "should be composed of sensor name and pulse_meter:value: prefix" do
22
+ sensor.value_key.should == "pulse_meter:value:#{name}"
23
+ end
24
+ end
25
+
26
+ describe "#value" do
27
+ it "should have initial value 0" do
28
+ sensor.value.should == 0
29
+ end
30
+
31
+ it "should store stringified value by value_key" do
32
+ sensor.event(123)
33
+ sensor.value.should == 123
34
+ redis.get(sensor.value_key).should == '123'
35
+ end
36
+ end
37
+
38
+ describe "#incr" do
39
+ it "should increment value by 1" do
40
+ expect{ sensor.incr }.to change{ sensor.value }.from(0).to(1)
41
+ expect{ sensor.incr }.to change{ sensor.value }.from(1).to(2)
42
+ end
43
+ end
44
+
45
+ describe "#cleanup" do
46
+ it "should remove all sensor data" do
47
+ sensor.annotate("My Counter")
48
+ sensor.event(123)
49
+ sensor.cleanup
50
+ redis.keys('*').should be_empty
51
+ end
52
+ end
53
+
54
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::HashedCounter do
4
+ let(:name){ :some_counter }
5
+ let(:sensor){ described_class.new(name) }
6
+ let(:redis){ PulseMeter.redis }
7
+
8
+ describe "#event" do
9
+ it "should increment sensor value by passed value" do
10
+ expect{ sensor.event({"foo" => 10}) }.to change{ sensor.value["foo"] }.from(0).to(10)
11
+ expect{ sensor.event({"foo" => 15}) }.to change{ sensor.value["foo"] }.from(10).to(25)
12
+ end
13
+
14
+ it "should truncate increment value" do
15
+ expect{ sensor.event({"foo" => 10.4}) }.to change{ sensor.value["foo"] }.from(0).to(10)
16
+ expect{ sensor.event({"foo" => 15.1}) }.to change{ sensor.value["foo"] }.from(10).to(25)
17
+ end
18
+ end
19
+
20
+ describe "#value" do
21
+ it "should have initial value 0" do
22
+ sensor.value["foo"].should == 0
23
+ end
24
+
25
+ it "should store redis hash by value_key" do
26
+ sensor.event({"foo" => 1})
27
+ sensor.value.should == {"foo" => 1}
28
+ redis.hgetall(sensor.value_key).should == {"foo" => "1"}
29
+ end
30
+ end
31
+
32
+ describe "#incr" do
33
+ it "should increment key value by 1" do
34
+ expect{ sensor.incr("foo") }.to change{ sensor.value["foo"] }.from(0).to(1)
35
+ expect{ sensor.incr("foo") }.to change{ sensor.value["foo"] }.from(1).to(2)
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,43 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Indicator do
4
+ let(:name){ :some_value }
5
+ let(:sensor){ described_class.new(name) }
6
+ let(:redis){ PulseMeter.redis }
7
+
8
+ describe "#event" do
9
+ it "should set sensor value to passed value" do
10
+ expect{ sensor.event(10.4) }.to change{ sensor.value }.from(0).to(10.4)
11
+ expect{ sensor.event(15.1) }.to change{ sensor.value }.from(10.4).to(15.1)
12
+ end
13
+ end
14
+
15
+ describe "#value_key" do
16
+ it "should be composed of sensor name and pulse_meter:value: prefix" do
17
+ sensor.value_key.should == "pulse_meter:value:#{name}"
18
+ end
19
+ end
20
+
21
+ describe "#value" do
22
+ it "should have initial value 0" do
23
+ sensor.value.should == 0
24
+ end
25
+
26
+ it "should store stringified value by value_key" do
27
+ sensor.event(123)
28
+ sensor.value.should == 123
29
+ redis.get(sensor.value_key) == '123'
30
+ end
31
+ end
32
+
33
+ describe "#cleanup" do
34
+ it "should remove all sensor data" do
35
+ sensor.annotate("My Indicator")
36
+ sensor.event(123)
37
+ sensor.cleanup
38
+ redis.keys('*').should be_empty
39
+ end
40
+ end
41
+
42
+ end
43
+
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Timeline do
4
+ let(:name){ :some_value_with_history }
5
+ let(:ttl){ 100 }
6
+ let(:raw_data_ttl){ 10 }
7
+ let(:interval){ 5 }
8
+ let(:reduce_delay){ 3 }
9
+ let(:good_init_values){ {:ttl => ttl, :raw_data_ttl => raw_data_ttl, :interval => interval, :reduce_delay => reduce_delay} }
10
+ let(:sensor){ described_class.new(name, good_init_values) }
11
+ let(:redis){ PulseMeter.redis }
12
+
13
+ it_should_behave_like "timeline sensor"
14
+
15
+ describe '#new' do
16
+ INIT_VALUE_NAMES = {
17
+ :with_defaults => [:raw_data_ttl, :reduce_delay],
18
+ :without_defaults => [:ttl, :interval]
19
+ }
20
+
21
+ it "should initialize #ttl #raw_data_ttl #interval and #name attributes" do
22
+ sensor.name.should == name.to_s
23
+
24
+ sensor.ttl.should == ttl
25
+ sensor.raw_data_ttl.should == raw_data_ttl
26
+ sensor.interval.should == interval
27
+ end
28
+
29
+ INIT_VALUE_NAMES[:with_defaults].each do |value|
30
+ it "should not raise exception if #{value.inspect} is not defined" do
31
+ values = good_init_values
32
+ values.delete(value)
33
+ expect {described_class.new(name, good_init_values)}.not_to raise_exception(ArgumentError)
34
+ end
35
+
36
+ it "should assign default value to #{value.inspect} if it is not defined" do
37
+ values = good_init_values
38
+ values.delete(value)
39
+ obj = described_class.new(name, good_init_values)
40
+ obj.send(value).should be_kind_of(Fixnum)
41
+ end
42
+ end
43
+
44
+ end
45
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Timelined::Average do
4
+ it_should_behave_like "timeline sensor"
5
+ it_should_behave_like "timelined subclass", [1, 2], 1.5
6
+ end
@@ -0,0 +1,6 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Timelined::Counter do
4
+ it_should_behave_like "timeline sensor"
5
+ it_should_behave_like "timelined subclass", [1, 2], 3
6
+ end
@@ -0,0 +1,8 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Timelined::HashedCounter do
4
+ it_should_behave_like "timeline sensor", {}, {:foo => 1}
5
+ it_should_behave_like "timelined subclass", [{:foo => 1}, {:foo => 2}], {:foo => 3}.to_json
6
+ it_should_behave_like "timelined subclass", [{:foo => 1}, {:foo => :bad_value}], {:foo => 1}.to_json
7
+ it_should_behave_like "timelined subclass", [{:foo => 1}, {:boo => 2}], {:foo => 1, :boo => 2}.to_json
8
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Timelined::Max do
4
+ it_should_behave_like "timeline sensor"
5
+ it_should_behave_like "timelined subclass", [1, 2, -1, -1, 5, 0], 5
6
+ it_should_behave_like "timelined subclass", [1], 1
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Timelined::Median do
4
+ it_should_behave_like "timeline sensor"
5
+ it_should_behave_like "timelined subclass", [5, 4, 3, 2, 1], 3
6
+ it_should_behave_like "timelined subclass", [1], 1
7
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Timelined::Min do
4
+ it_should_behave_like "timeline sensor"
5
+ it_should_behave_like "timelined subclass", [1, 2, -1, -1, 5, 0], -1
6
+ it_should_behave_like "timelined subclass", [1], 1
7
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter::Sensor::Timelined::Percentile do
4
+ it_should_behave_like "timeline sensor", {:p => 0.8}
5
+ it_should_behave_like "timelined subclass", [5, 4, 2, 2, 2, 2, 2, 2, 2, 1], 2, {:p => 0.8}
6
+ it_should_behave_like "timelined subclass", [1], 1, {:p => 0.8}
7
+
8
+ let(:init_values) {{:ttl => 1, :raw_data_ttl => 1, :interval => 1, :reduce_delay => 1}}
9
+ let(:name) {"percentile"}
10
+
11
+ it "should raise exception when percentile is not between 0 and 1" do
12
+ expect {described_class.new(name, init_values.merge({:p => -1}))}.to raise_exception(ArgumentError)
13
+ expect {described_class.new(name, init_values.merge({:p => 1.1}))}.to raise_exception(ArgumentError)
14
+ expect {described_class.new(name, init_values.merge({:p => 0.1}))}.not_to raise_exception(ArgumentError)
15
+ end
16
+
17
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec_helper'
2
+
3
+ describe PulseMeter do
4
+ describe "::redis=" do
5
+ it "should store redis" do
6
+ PulseMeter.redis = 'redis'
7
+ PulseMeter.redis.should == 'redis'
8
+ end
9
+ end
10
+ describe "::redis" do
11
+ it "should retrieve redis" do
12
+ PulseMeter.redis = 'redis'
13
+ PulseMeter.redis.should == 'redis'
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,274 @@
1
+ shared_examples_for "timeline sensor" do |extra_init_values, default_event|
2
+ class Dummy
3
+ include PulseMeter::Mixins::Dumper
4
+ def name; :dummy end
5
+ def redis; PulseMeter.redis; end
6
+ end
7
+
8
+ let(:name){ :some_value_with_history }
9
+ let(:ttl){ 100 }
10
+ let(:raw_data_ttl){ 30 }
11
+ let(:interval){ 5 }
12
+ let(:reduce_delay){ 3 }
13
+ let(:good_init_values){ {:ttl => ttl, :raw_data_ttl => raw_data_ttl, :interval => interval, :reduce_delay => reduce_delay}.merge(extra_init_values || {}) }
14
+ let!(:sensor){ described_class.new(name, good_init_values) }
15
+ let(:dummy) {Dummy.new}
16
+ let(:base_class){ PulseMeter::Sensor::Base }
17
+ let(:redis){ PulseMeter.redis }
18
+ let(:sample_event) {default_event || 123}
19
+
20
+ before(:each) do
21
+ @interval_id = (Time.now.to_i / interval) * interval
22
+ @raw_data_key = sensor.raw_data_key(@interval_id)
23
+ @next_raw_data_key = sensor.raw_data_key(@interval_id + interval)
24
+ @start_of_interval = Time.at(@interval_id)
25
+ end
26
+
27
+ describe "#dump" do
28
+ it "should be dumped succesfully" do
29
+ expect {sensor.dump!}.not_to raise_exception
30
+ end
31
+ end
32
+
33
+ describe ".restore" do
34
+ before do
35
+ # no need to call sensor.dump! explicitly for it
36
+ # will be called automatically after creation
37
+ @restored = base_class.restore(sensor.name)
38
+ end
39
+
40
+ it "should restore #{described_class} instance" do
41
+ @restored.should be_instance_of(described_class)
42
+ end
43
+
44
+ it "should restore object with the same data" do
45
+ def inner_data(obj)
46
+ obj.instance_variables.sort.map {|v| obj.instance_variable_get(v)}
47
+ end
48
+
49
+ inner_data(sensor).should == inner_data(@restored)
50
+ end
51
+ end
52
+
53
+ describe "#event" do
54
+ it "should write events to redis" do
55
+ expect{
56
+ sensor.event(sample_event)
57
+ }.to change{ redis.keys('*').count }.by(1)
58
+ end
59
+
60
+ it "should write data so that it totally expires after :raw_data_ttl" do
61
+ key_count = redis.keys('*').count
62
+ sensor.event(sample_event)
63
+ Timecop.freeze(Time.now + raw_data_ttl + 1) do
64
+ redis.keys('*').count.should == key_count
65
+ end
66
+ end
67
+
68
+ it "should write data to bucket indicated by truncated timestamp" do
69
+ key = sensor.raw_data_key(@interval_id)
70
+ expect{
71
+ Timecop.freeze(@start_of_interval) do
72
+ sensor.event(sample_event)
73
+ end
74
+ }.to change{ redis.ttl(key) }
75
+ end
76
+ end
77
+
78
+ describe "#summarize" do
79
+ it "should convert data stored by raw_data_key to a value defined only by stored data" do
80
+ Timecop.freeze(@start_of_interval) do
81
+ sensor.event(sample_event)
82
+ end
83
+ Timecop.freeze(@start_of_interval + interval) do
84
+ sensor.event(sample_event)
85
+ end
86
+ sensor.summarize(@raw_data_key).should == sensor.summarize(@next_raw_data_key)
87
+ sensor.summarize(@raw_data_key).should_not be_nil
88
+ end
89
+ end
90
+
91
+ describe "#reduce" do
92
+ it "should store summarized value into data_key" do
93
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
94
+ val = sensor.summarize(@raw_data_key)
95
+ val.should_not be_nil
96
+ sensor.reduce(@interval_id)
97
+ redis.get(sensor.data_key(@interval_id)).should == val.to_s
98
+ end
99
+
100
+ it "should remove original raw_data_key" do
101
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
102
+ expect{
103
+ sensor.reduce(@interval_id)
104
+ }.to change{ redis.keys(sensor.raw_data_key(@interval_id)).count }.from(1).to(0)
105
+ end
106
+
107
+ it "should expire stored summarized data" do
108
+ Timecop.freeze(@start_of_interval) do
109
+ sensor.event(sample_event)
110
+ sensor.reduce(@interval_id)
111
+ redis.keys(sensor.data_key(@interval_id)).count.should == 1
112
+ end
113
+ Timecop.freeze(@start_of_interval + ttl + 1) do
114
+ redis.keys(sensor.data_key(@interval_id)).count.should == 0
115
+ end
116
+ end
117
+
118
+ it "should not store data if there is no corresponding raw data" do
119
+ Timecop.freeze(@start_of_interval) do
120
+ sensor.reduce(@interval_id)
121
+ redis.keys(sensor.data_key(@interval_id)).count.should == 0
122
+ end
123
+ end
124
+ end
125
+
126
+ describe "#reduce_all_raw" do
127
+ it "should reduce all data older than reduce_delay" do
128
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
129
+ val0 = sensor.summarize(@raw_data_key)
130
+ Timecop.freeze(@start_of_interval + interval){ sensor.event(sample_event) }
131
+ val1 = sensor.summarize(@next_raw_data_key)
132
+ expect{
133
+ Timecop.freeze(@start_of_interval + interval + interval + reduce_delay + 1) { sensor.reduce_all_raw }
134
+ }.to change{ redis.keys(sensor.raw_data_key('*')).count }.from(2).to(0)
135
+
136
+ redis.get(sensor.data_key(@interval_id)).should == val0.to_s
137
+ redis.get(sensor.data_key(@interval_id + interval)).should == val1.to_s
138
+ end
139
+
140
+ it "should not reduce fresh data" do
141
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
142
+
143
+ expect{
144
+ Timecop.freeze(@start_of_interval + interval + reduce_delay - 1) { sensor.reduce_all_raw }
145
+ }.not_to change{ redis.keys(sensor.raw_data_key('*')).count }
146
+
147
+ expect{
148
+ Timecop.freeze(@start_of_interval + interval + reduce_delay - 1) { sensor.reduce_all_raw }
149
+ }.not_to change{ redis.keys(sensor.data_key('*')).count }
150
+ end
151
+ end
152
+
153
+ describe ".reduce_all_raw" do
154
+ it "should silently skip objects without reduce logic" do
155
+ dummy.dump!
156
+ expect {described_class.reduce_all_raw}.not_to raise_exception
157
+ end
158
+
159
+ it "should send reduce_all_raw to all dumped objects" do
160
+ described_class.any_instance.should_receive(:reduce_all_raw)
161
+ described_class.reduce_all_raw
162
+ end
163
+ end
164
+
165
+ describe "#timeline_within" do
166
+ it "shoulde raise exception unless both arguments are Time objects" do
167
+ [:q, nil, -1].each do |bad_value|
168
+ expect{ sensor.timeline_within(Time.now, bad_value) }.to raise_exception(ArgumentError)
169
+ expect{ sensor.timeline_within(bad_value, Time.now) }.to raise_exception(ArgumentError)
170
+ end
171
+ end
172
+
173
+ it "should return an array of SensorData objects corresponding to stored data for passed interval" do
174
+ sensor.event(sample_event)
175
+ now = Time.now
176
+ timeline = sensor.timeline_within(now - 1, now)
177
+ timeline.should be_kind_of(Array)
178
+ timeline.each{|i| i.should be_kind_of(SensorData) }
179
+ end
180
+
181
+ it "should return array of results containing as many results as there are sensor interval beginnings in the passed interval" do
182
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
183
+ Timecop.freeze(@start_of_interval + interval){ sensor.event(sample_event) }
184
+
185
+ future = @start_of_interval + 3600
186
+ Timecop.freeze(future) do
187
+ sensor.timeline_within(
188
+ Time.at(@start_of_interval + interval - 1),
189
+ Time.at(@start_of_interval + interval + 1)
190
+ ).size.should == 1
191
+
192
+ sensor.timeline_within(
193
+ Time.at(@start_of_interval - 1),
194
+ Time.at(@start_of_interval + interval + 1)
195
+ ).size.should == 2
196
+ end
197
+
198
+ Timecop.freeze(@start_of_interval + interval + 2) do
199
+ sensor.timeline_within(
200
+ Time.at(@start_of_interval + interval + 1),
201
+ Time.at(@start_of_interval + interval + 2)
202
+ ).size.should == 0
203
+ end
204
+ end
205
+ end
206
+
207
+ describe "#timeline" do
208
+
209
+ it "should request timeline within interval from given number of seconds ago till now" do
210
+ Timecop.freeze do
211
+ now = Time.now
212
+ ago = interval * 100
213
+ sensor.timeline(ago).should == sensor.timeline_within(now - ago, now)
214
+ end
215
+ end
216
+
217
+ it "should return array of results containing as many results as there are sensor interval beginnings in the passed interval" do
218
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
219
+ Timecop.freeze(@start_of_interval + interval){ sensor.event(sample_event) }
220
+
221
+ Timecop.freeze(@start_of_interval + interval + 1) do
222
+ sensor.timeline(2).size.should == 1
223
+ end
224
+ Timecop.freeze(@start_of_interval + interval + 2) do
225
+ sensor.timeline(1).size.should == 0
226
+ end
227
+ Timecop.freeze(@start_of_interval + interval + 1) do
228
+ sensor.timeline(2 + interval).size.should == 2
229
+ end
230
+ end
231
+ end
232
+
233
+ describe "SensorData value for an interval" do
234
+ def check_sensor_data(sensor, value)
235
+ data = sensor.timeline(2).first
236
+ data.value.should == value
237
+ data.start_time.to_i.should == @interval_id
238
+ end
239
+
240
+ it "should contain summarized value stored by data_key for reduced intervals" do
241
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
242
+ sensor.reduce(@interval_id)
243
+ Timecop.freeze(@start_of_interval + 1){
244
+ check_sensor_data(sensor, redis.get(sensor.data_key(@interval_id)))
245
+ }
246
+ end
247
+
248
+ it "should contain summarized value based on raw data for intervals not yet reduced" do
249
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
250
+ Timecop.freeze(@start_of_interval + 1){
251
+ check_sensor_data(sensor, sensor.summarize(@raw_data_key))
252
+ }
253
+ end
254
+
255
+ it "should contain nil for intervals without any data" do
256
+ Timecop.freeze(@start_of_interval + 1) {
257
+ check_sensor_data(sensor, nil)
258
+ }
259
+ end
260
+ end
261
+
262
+ describe "#cleanup" do
263
+ it "should remove all sensor data (raw data, reduced data, annotations) from redis" do
264
+ Timecop.freeze(@start_of_interval){ sensor.event(sample_event) }
265
+ sensor.reduce(@interval_id)
266
+ Timecop.freeze(@start_of_interval + interval){ sensor.event(sample_event) }
267
+ sensor.annotate("Fooo sensor")
268
+
269
+ sensor.cleanup
270
+ redis.keys('*').should be_empty
271
+ end
272
+ end
273
+
274
+ end