pulse-meter-client-backport 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +19 -0
- data/.rbenv-version +1 -0
- data/.rspec +1 -0
- data/.rvmrc +1 -0
- data/.travis.yml +3 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/Procfile +3 -0
- data/README.md +98 -0
- data/Rakefile +53 -0
- data/bin/pulse +6 -0
- data/examples/readme_client_example.rb +52 -0
- data/lib/pulse-meter.rb +17 -0
- data/lib/pulse-meter/mixins/dumper.rb +76 -0
- data/lib/pulse-meter/mixins/utils.rb +93 -0
- data/lib/pulse-meter/sensor.rb +44 -0
- data/lib/pulse-meter/sensor/base.rb +75 -0
- data/lib/pulse-meter/sensor/counter.rb +36 -0
- data/lib/pulse-meter/sensor/hashed_counter.rb +31 -0
- data/lib/pulse-meter/sensor/indicator.rb +33 -0
- data/lib/pulse-meter/sensor/timeline.rb +180 -0
- data/lib/pulse-meter/sensor/timelined/average.rb +26 -0
- data/lib/pulse-meter/sensor/timelined/counter.rb +16 -0
- data/lib/pulse-meter/sensor/timelined/hashed_counter.rb +22 -0
- data/lib/pulse-meter/sensor/timelined/max.rb +25 -0
- data/lib/pulse-meter/sensor/timelined/median.rb +14 -0
- data/lib/pulse-meter/sensor/timelined/min.rb +25 -0
- data/lib/pulse-meter/sensor/timelined/percentile.rb +31 -0
- data/lib/pulse-meter/version.rb +3 -0
- data/pulse-meter-client-backport.gemspec +33 -0
- data/spec/pulse_meter/mixins/dumper_spec.rb +141 -0
- data/spec/pulse_meter/mixins/utils_spec.rb +125 -0
- data/spec/pulse_meter/sensor/base_spec.rb +97 -0
- data/spec/pulse_meter/sensor/counter_spec.rb +54 -0
- data/spec/pulse_meter/sensor/hashed_counter_spec.rb +39 -0
- data/spec/pulse_meter/sensor/indicator_spec.rb +43 -0
- data/spec/pulse_meter/sensor/timeline_spec.rb +45 -0
- data/spec/pulse_meter/sensor/timelined/average_spec.rb +6 -0
- data/spec/pulse_meter/sensor/timelined/counter_spec.rb +6 -0
- data/spec/pulse_meter/sensor/timelined/hashed_counter_spec.rb +8 -0
- data/spec/pulse_meter/sensor/timelined/max_spec.rb +7 -0
- data/spec/pulse_meter/sensor/timelined/median_spec.rb +7 -0
- data/spec/pulse_meter/sensor/timelined/min_spec.rb +7 -0
- data/spec/pulse_meter/sensor/timelined/percentile_spec.rb +17 -0
- data/spec/pulse_meter_spec.rb +16 -0
- data/spec/shared_examples/timeline_sensor.rb +274 -0
- data/spec/shared_examples/timelined_subclass.rb +23 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/support/matchers.rb +45 -0
- metadata +276 -0
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module PulseMeter
|
4
|
+
module Sensor
|
5
|
+
module Timelined
|
6
|
+
# Counts multiple types of events per interval.
|
7
|
+
# Good replacement for multiple counters to be visualized together
|
8
|
+
class HashedCounter < Timeline
|
9
|
+
def aggregate_event(key, data)
|
10
|
+
data.each_pair {|k, v| redis.hincrby(key, k, v)}
|
11
|
+
end
|
12
|
+
|
13
|
+
def summarize(key)
|
14
|
+
redis.
|
15
|
+
hgetall(key).
|
16
|
+
inject({}) {|h, (k, v)| h[k] = v.to_i; h}.
|
17
|
+
to_json
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PulseMeter
|
2
|
+
module Sensor
|
3
|
+
module Timelined
|
4
|
+
# Calculates max value in interval
|
5
|
+
class Max < Timeline
|
6
|
+
|
7
|
+
def aggregate_event(key, value)
|
8
|
+
redis.zadd(key, value, "#{value}::#{uniqid}")
|
9
|
+
redis.zremrangebyrank(key, 0, -2)
|
10
|
+
end
|
11
|
+
|
12
|
+
def summarize(key)
|
13
|
+
count = redis.zcard(key)
|
14
|
+
if count > 0
|
15
|
+
max_el = redis.zrange(key, -1, -1)[0]
|
16
|
+
redis.zscore(key, max_el).to_f
|
17
|
+
else
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module PulseMeter
|
2
|
+
module Sensor
|
3
|
+
module Timelined
|
4
|
+
# Calculates min value in interval
|
5
|
+
class Min < Timeline
|
6
|
+
|
7
|
+
def aggregate_event(key, value)
|
8
|
+
redis.zadd(key, value, "#{value}::#{uniqid}")
|
9
|
+
redis.zremrangebyrank(key, 1, -1)
|
10
|
+
end
|
11
|
+
|
12
|
+
def summarize(key)
|
13
|
+
count = redis.zcard(key)
|
14
|
+
if count > 0
|
15
|
+
min_el = redis.zrange(key, 0, 0)[0]
|
16
|
+
redis.zscore(key, min_el).to_f
|
17
|
+
else
|
18
|
+
nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module PulseMeter
|
2
|
+
module Sensor
|
3
|
+
module Timelined
|
4
|
+
# Calculates n'th percentile in interval
|
5
|
+
class Percentile < Timeline
|
6
|
+
attr_reader :p_value
|
7
|
+
|
8
|
+
def initialize(name, options)
|
9
|
+
@p_value = assert_ranged_float!(options, :p, 0, 1)
|
10
|
+
super(name, options)
|
11
|
+
end
|
12
|
+
|
13
|
+
def aggregate_event(key, value)
|
14
|
+
redis.zadd(key, value, "#{value}::#{uniqid}")
|
15
|
+
end
|
16
|
+
|
17
|
+
def summarize(key)
|
18
|
+
count = redis.zcard(key)
|
19
|
+
if count > 0
|
20
|
+
position = @p_value > 0 ? (@p_value * count).round - 1 : 0
|
21
|
+
el = redis.zrange(key, position, position)[0]
|
22
|
+
redis.zscore(key, el).to_f
|
23
|
+
else
|
24
|
+
nil
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/pulse-meter/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Ilya Averyanov", "Sergey Averyanov"]
|
6
|
+
gem.email = ["av@fun-box.ru", "averyanov@gmail.com"]
|
7
|
+
gem.description = %q{Ruby 1.8 compatible PulseMeter client}
|
8
|
+
gem.summary = %q{
|
9
|
+
Ruby 1.8 compatible PulseMeter client
|
10
|
+
}
|
11
|
+
gem.homepage = ""
|
12
|
+
|
13
|
+
gem.required_ruby_version = '~> 1.8.0'
|
14
|
+
gem.files = `git ls-files`.split($\)
|
15
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
16
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
17
|
+
gem.name = "pulse-meter-client-backport"
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
gem.version = PulseMeter::VERSION
|
20
|
+
|
21
|
+
gem.add_runtime_dependency('json')
|
22
|
+
gem.add_runtime_dependency('redis')
|
23
|
+
|
24
|
+
gem.add_development_dependency('foreman')
|
25
|
+
gem.add_development_dependency('mock_redis')
|
26
|
+
gem.add_development_dependency('rack-test')
|
27
|
+
gem.add_development_dependency('rake')
|
28
|
+
gem.add_development_dependency('redcarpet')
|
29
|
+
gem.add_development_dependency('rspec')
|
30
|
+
gem.add_development_dependency('timecop')
|
31
|
+
gem.add_development_dependency('yard')
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,141 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Mixins::Dumper do
|
4
|
+
class Base
|
5
|
+
include PulseMeter::Mixins::Dumper
|
6
|
+
end
|
7
|
+
|
8
|
+
class Bad < Base; end
|
9
|
+
|
10
|
+
class Good < Base
|
11
|
+
attr_accessor :foo
|
12
|
+
def name; foo.to_s; end
|
13
|
+
|
14
|
+
def redis; PulseMeter.redis; end
|
15
|
+
|
16
|
+
def initialize(foo)
|
17
|
+
@foo = foo
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
let(:bad_obj){ Bad.new }
|
22
|
+
let(:good_obj){ Good.new(:foo) }
|
23
|
+
let(:another_good_obj){ Good.new(:bar) }
|
24
|
+
let(:redis){ PulseMeter.redis }
|
25
|
+
|
26
|
+
describe '#dump' do
|
27
|
+
context "when class violates dump contract" do
|
28
|
+
context "when it has no name attribute" do
|
29
|
+
it "should raise exception" do
|
30
|
+
def bad_obj.redis; PulseMeter.redis; end
|
31
|
+
expect{ bad_obj.dump! }.to raise_exception(PulseMeter::DumpError)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context "when it has no redis attribute" do
|
36
|
+
it "should raise exception" do
|
37
|
+
def bad_obj.name; :foo; end
|
38
|
+
expect{ bad_obj.dump! }.to raise_exception(PulseMeter::DumpError)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when redis is not avalable" do
|
43
|
+
it "should raise exception" do
|
44
|
+
def bad_obj.name; :foo; end
|
45
|
+
def bad_obj.redis; nil; end
|
46
|
+
expect{ bad_obj.dump! }.to raise_exception(PulseMeter::DumpError)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when class follows dump contract" do
|
52
|
+
it "should not raise dump exception" do
|
53
|
+
expect {good_obj.dump!}.not_to raise_exception(PulseMeter::DumpError)
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should save dump to redis" do
|
57
|
+
expect {good_obj.dump!}.to change {redis.hlen(Good::DUMP_REDIS_KEY)}.by(1)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
describe ".restore" do
|
63
|
+
context "when object has never been dumped" do
|
64
|
+
it "should raise exception" do
|
65
|
+
expect{ Base.restore(:nonexistant) }.to raise_exception(PulseMeter::RestoreError)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
context "when object was dumped" do
|
70
|
+
before do
|
71
|
+
good_obj.dump!
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should keep object class" do
|
75
|
+
Base.restore(good_obj.name).should be_instance_of(good_obj.class)
|
76
|
+
end
|
77
|
+
|
78
|
+
it "should restore object data" do
|
79
|
+
restored = Base.restore(good_obj.name)
|
80
|
+
restored.foo.should == good_obj.foo
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should restore last dumped object" do
|
84
|
+
good_obj.foo = :bar
|
85
|
+
good_obj.dump!
|
86
|
+
restored = Base.restore(good_obj.name)
|
87
|
+
restored.foo.should == :bar
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
describe ".list_names" do
|
93
|
+
context "when redis is not available" do
|
94
|
+
before do
|
95
|
+
PulseMeter.stub(:redis).and_return(nil)
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should raise exception" do
|
99
|
+
expect {Base.list_names}.to raise_exception(PulseMeter::RestoreError)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
context "when redis if fine" do
|
104
|
+
it "should return empty list if nothing is registered" do
|
105
|
+
Base.list_names.should == []
|
106
|
+
end
|
107
|
+
|
108
|
+
it "should return list of registered objects" do
|
109
|
+
good_obj.dump!
|
110
|
+
another_good_obj.dump!
|
111
|
+
Base.list_names.should =~ [good_obj.name, another_good_obj.name]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe ".list_objects" do
|
117
|
+
before do
|
118
|
+
good_obj.dump!
|
119
|
+
another_good_obj.dump!
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should return restored objects" do
|
123
|
+
objects = Base.list_objects
|
124
|
+
objects.map(&:name).should =~ [good_obj.name, another_good_obj.name]
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should skip unrestorable objects" do
|
128
|
+
Base.stub(:list_names).and_return([good_obj.name, "scoundrel", another_good_obj.name])
|
129
|
+
objects = Base.list_objects
|
130
|
+
objects.map(&:name).should =~ [good_obj.name, another_good_obj.name]
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
describe "#cleanup_dump" do
|
135
|
+
it "should remove data from redis" do
|
136
|
+
good_obj.dump!
|
137
|
+
another_good_obj.dump!
|
138
|
+
expect {good_obj.cleanup_dump}.to change{good_obj.class.list_names.count}.by(-1)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Mixins::Utils do
|
4
|
+
class Dummy
|
5
|
+
include PulseMeter::Mixins::Utils
|
6
|
+
end
|
7
|
+
|
8
|
+
let(:dummy){ Dummy.new }
|
9
|
+
|
10
|
+
describe '#constantize' do
|
11
|
+
context "when argument is a string with a valid class name" do
|
12
|
+
it "should return class" do
|
13
|
+
dummy.constantize("PulseMeter::Mixins::Utils").should == PulseMeter::Mixins::Utils
|
14
|
+
end
|
15
|
+
end
|
16
|
+
context "when argument is a string with invalid class name" do
|
17
|
+
it "should return nil" do
|
18
|
+
dummy.constantize("Pumpkin::Eater").should be_nil
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#assert_positive_integer!" do
|
24
|
+
it "should extract integer value from hash by passed key" do
|
25
|
+
dummy.assert_positive_integer!({:val => 4}, :val).should == 4
|
26
|
+
end
|
27
|
+
|
28
|
+
context "when no default value given" do
|
29
|
+
context "when the value by the passed key is not integer" do
|
30
|
+
it "should convert non-integers to integers" do
|
31
|
+
dummy.assert_positive_integer!({:val => 4.4}, :val).should == 4
|
32
|
+
end
|
33
|
+
|
34
|
+
it "should change the original value to the obtained integer" do
|
35
|
+
h = {:val => 4.4}
|
36
|
+
dummy.assert_positive_integer!(h, :val).should == 4
|
37
|
+
h[:val].should == 4
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should raise exception if the value is not positive" do
|
43
|
+
expect{ dummy.assert_positive_integer!({:val => -1}, :val) }.to raise_exception(ArgumentError)
|
44
|
+
end
|
45
|
+
|
46
|
+
it "should raise exception if the value is not defined" do
|
47
|
+
expect{ dummy.assert_positive_integer!({}, :val) }.to raise_exception(ArgumentError)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
context "when default value given" do
|
52
|
+
it "should prefer value from options to default" do
|
53
|
+
dummy.assert_positive_integer!({:val => 4}, :val, 22).should == 4
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should use default value when there is no one in options" do
|
57
|
+
dummy.assert_positive_integer!({}, :val, 22).should == 22
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should check default value if it is to be used" do
|
61
|
+
expect{dummy.assert_positive_integer!({}, :val, -1)}.to raise_exception(ArgumentError)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
describe "#assert_ranged_float!" do
|
67
|
+
|
68
|
+
it "should extract float value from hash by passed key" do
|
69
|
+
dummy.assert_ranged_float!({:val => 4}, :val, 0, 100).should be_generally_equal(4)
|
70
|
+
end
|
71
|
+
|
72
|
+
context "when the value by the passed key is not float" do
|
73
|
+
it "should convert non-floats to floats" do
|
74
|
+
dummy.assert_ranged_float!({:val => "4.0000"}, :val, 0, 100).should be_generally_equal(4)
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should change the original value to the obtained float" do
|
78
|
+
h = {:val => "4.000"}
|
79
|
+
dummy.assert_ranged_float!(h, :val, 0, 100).should be_generally_equal(4)
|
80
|
+
h[:val].should be_generally_equal(4)
|
81
|
+
end
|
82
|
+
|
83
|
+
it "should raise exception if the original value cannot be converted to float" do
|
84
|
+
expect{ dummy.assert_ranged_float!({:val => :bad_float}, :val, 0, 100) }.to raise_exception(ArgumentError)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
it "should raise exception if the value is not within range" do
|
89
|
+
expect{ dummy.assert_ranged_float!({:val => -0.1}, :val, 0, 100) }.to raise_exception(ArgumentError)
|
90
|
+
expect{ dummy.assert_ranged_float!({:val => 100.1}, :val, 0, 100) }.to raise_exception(ArgumentError)
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should raise exception if the value is not defined" do
|
94
|
+
expect{ dummy.assert_ranged_float!({}, :val) }.to raise_exception(ArgumentError)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
describe "#uniqid" do
|
99
|
+
it "should return uniq strings" do
|
100
|
+
uniq_values = (1..1000).map{|_| dummy.uniqid}
|
101
|
+
uniq_values.uniq.count.should == uniq_values.count
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
describe "#titleize" do
|
106
|
+
it "should convert identificator to title" do
|
107
|
+
dummy.titleize("aaa_bbb").should == 'Aaa Bbb'
|
108
|
+
dummy.titleize(:aaa_bbb).should == 'Aaa Bbb'
|
109
|
+
dummy.titleize("aaa bbb").should == 'Aaa Bbb'
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe "#camelize" do
|
114
|
+
it "should camelize string" do
|
115
|
+
dummy.camelize("aa_bb_cc").should == "aaBbCc"
|
116
|
+
dummy.camelize("aa_bb_cc", true).should == "AaBbCc"
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
describe "#camelize_keys" do
|
121
|
+
it "should deeply camelize keys in hashes" do
|
122
|
+
dummy.camelize_keys({ :aa_bb_cc => [ { :dd_ee => 123 }, 456 ] }).should =={ 'aaBbCc' => [ { 'ddEe' => 123 }, 456 ] }
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe PulseMeter::Sensor::Base do
|
4
|
+
let(:name){ :some_sensor }
|
5
|
+
let(:description) {"Le awesome description"}
|
6
|
+
let!(:sensor){ described_class.new(name) }
|
7
|
+
let(:redis){ PulseMeter.redis }
|
8
|
+
|
9
|
+
describe '#initialize' do
|
10
|
+
context 'when PulseMeter.redis is not initialized' do
|
11
|
+
it "should raise RedisNotInitialized exception" do
|
12
|
+
PulseMeter.redis = nil
|
13
|
+
expect{ described_class.new(:foo) }.to raise_exception(PulseMeter::RedisNotInitialized)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
context 'when PulseMeter.redis is initialized' do
|
18
|
+
|
19
|
+
context 'when passed sensor name is bad' do
|
20
|
+
it "should raise BadSensorName exception" do
|
21
|
+
['name with whitespace', 'name|with|bad|characters'].each do |bad_name|
|
22
|
+
expect{ described_class.new(bad_name) }.to raise_exception(PulseMeter::BadSensorName)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
context 'when passed sensor name is valid' do
|
28
|
+
it "should successfully create object" do
|
29
|
+
described_class.new(:foo).should_not be_nil
|
30
|
+
end
|
31
|
+
|
32
|
+
it "should initialize attributes #redis and #name" do
|
33
|
+
sensor = described_class.new(:foo)
|
34
|
+
sensor.name.should == 'foo'
|
35
|
+
sensor.redis.should == PulseMeter.redis
|
36
|
+
end
|
37
|
+
|
38
|
+
it "should save dump to redis automatically to let the object be restored by name" do
|
39
|
+
described_class.restore(name).should be_instance_of(described_class)
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should annotate object if annotation given" do
|
43
|
+
described_class.new(:foo, :annotation => "annotation")
|
44
|
+
sensor = described_class.restore(:foo)
|
45
|
+
sensor.annotation.should == "annotation"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe '#annotate' do
|
52
|
+
|
53
|
+
it "should store sensor annotation in redis" do
|
54
|
+
expect {sensor.annotate(description)}.to change{redis.keys('*').count}.by(1)
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#annotation' do
|
60
|
+
context "when sensor was annotated" do
|
61
|
+
it "should return stored annotation" do
|
62
|
+
sensor.annotate(description)
|
63
|
+
sensor.annotation.should == description
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context "when sensor was not annotated" do
|
68
|
+
it "should return nil" do
|
69
|
+
sensor.annotation.should be_nil
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
context "after sensor data was cleaned" do
|
74
|
+
it "should return nil" do
|
75
|
+
sensor.annotate(description)
|
76
|
+
sensor.cleanup
|
77
|
+
sensor.annotation.should be_nil
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
describe "#cleanup" do
|
83
|
+
it "should remove from redis all sensor data" do
|
84
|
+
sensor.event(123)
|
85
|
+
sensor.annotate(description)
|
86
|
+
sensor.cleanup
|
87
|
+
redis.keys('*').should be_empty
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
describe "#event" do
|
92
|
+
it "should actually do nothing for base sensor" do
|
93
|
+
sensor.event(nil)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|