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.
- 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
|