honest_pubsub 0.2.2
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.
- checksums.yaml +7 -0
- data/#.ruby-gemset# +0 -0
- data/.gitignore +22 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +92 -0
- data/Rakefile +1 -0
- data/bin/start_subscribers +14 -0
- data/config/pubsub.yml +17 -0
- data/honest_pubsub.gemspec +38 -0
- data/lib/honest_pubsub/cli.rb +94 -0
- data/lib/honest_pubsub/configuration.rb +38 -0
- data/lib/honest_pubsub/context.rb +70 -0
- data/lib/honest_pubsub/db_logger.rb +52 -0
- data/lib/honest_pubsub/exceptions/payload_error.rb +2 -0
- data/lib/honest_pubsub/logger.rb +49 -0
- data/lib/honest_pubsub/logging.rb +42 -0
- data/lib/honest_pubsub/message.rb +50 -0
- data/lib/honest_pubsub/middleware.rb +14 -0
- data/lib/honest_pubsub/publisher.rb +93 -0
- data/lib/honest_pubsub/railtie.rb +27 -0
- data/lib/honest_pubsub/server/client_queue_listener.rb +54 -0
- data/lib/honest_pubsub/server/client_worker.rb +86 -0
- data/lib/honest_pubsub/server/subscriber_server.rb +84 -0
- data/lib/honest_pubsub/server.rb +8 -0
- data/lib/honest_pubsub/subscriber.rb +69 -0
- data/lib/honest_pubsub/version.rb +3 -0
- data/lib/honest_pubsub.rb +46 -0
- data/spec/config.yml +20 -0
- data/spec/honest_pubsub/#subscriber_spec.rb# +9 -0
- data/spec/honest_pubsub/cli_spec.rb +145 -0
- data/spec/honest_pubsub/server/client_queue_listener_spec.rb +76 -0
- data/spec/honest_pubsub/server/client_worker_spec.rb +161 -0
- data/spec/honest_pubsub/subscriber_spec.rb +5 -0
- data/spec/logger_spec.rb +110 -0
- data/spec/message_spec.rb +65 -0
- data/spec/spec_helper.rb +49 -0
- metadata +259 -0
@@ -0,0 +1,76 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HonestPubsub::Server::ClientQueueListener do
|
4
|
+
describe "#message_received" do
|
5
|
+
|
6
|
+
subject { -> { queue_listener.message_received( {}, {routing_key: routing_key}, {context: context, payload: payload}) } }
|
7
|
+
let(:queue_listener) { HonestPubsub::Server::ClientQueueListener.new(Klass, routing_key, 'queue') }
|
8
|
+
let(:routing_key) { 'some.test.key' }
|
9
|
+
let(:context) { {} }
|
10
|
+
let(:payload) { {hello: 'moto'} }
|
11
|
+
let(:context_before){}
|
12
|
+
let(:validators){ [] }
|
13
|
+
|
14
|
+
before do
|
15
|
+
context_before
|
16
|
+
Object.const_set :Klass, Class.new(HonestPubsub::Server::ClientWorker)
|
17
|
+
validators.each do |validator|
|
18
|
+
Klass.validates_payload_with validator
|
19
|
+
end
|
20
|
+
|
21
|
+
klass_instance
|
22
|
+
|
23
|
+
allow(Klass).to receive(:new).and_return(klass_instance)
|
24
|
+
subject.call unless respond_to? :no_call
|
25
|
+
end
|
26
|
+
|
27
|
+
after do
|
28
|
+
Object.send :remove_const, :Klass
|
29
|
+
end
|
30
|
+
|
31
|
+
context "Normal execution" do
|
32
|
+
let!(:klass_instance){
|
33
|
+
instance = Klass.new({},{})
|
34
|
+
allow(instance).to receive(:perform)
|
35
|
+
instance
|
36
|
+
}
|
37
|
+
|
38
|
+
context "test exception" do
|
39
|
+
let(:no_call) { true }
|
40
|
+
|
41
|
+
it { should_not raise_exception }
|
42
|
+
end
|
43
|
+
|
44
|
+
context "call" do
|
45
|
+
it { expect(klass_instance).to have_received(:perform).with({}, payload) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "Exception during execution" do
|
50
|
+
let!(:klass_instance) {
|
51
|
+
instance = Klass.new({},{})
|
52
|
+
allow(instance).to receive(:perform){
|
53
|
+
raise StandardError.new("test")
|
54
|
+
}
|
55
|
+
instance
|
56
|
+
}
|
57
|
+
let(:no_call) { true }
|
58
|
+
|
59
|
+
it { should_not raise_exception }
|
60
|
+
end
|
61
|
+
|
62
|
+
context "Payload error" do
|
63
|
+
let!(:klass_instance) {
|
64
|
+
instance = Klass.new({},{})
|
65
|
+
allow(instance).to receive(:perform)
|
66
|
+
instance
|
67
|
+
}
|
68
|
+
|
69
|
+
let!(:validators) { [lambda{ |payload| false }] }
|
70
|
+
|
71
|
+
let(:context_before){ allow(Airbrake).to receive(:notify)}
|
72
|
+
|
73
|
+
it {expect(Airbrake).to have_received(:notify)}
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HonestPubsub::Server::ClientWorker do
|
4
|
+
before(:each) { Object.const_set :Klass, Class.new(HonestPubsub::Server::ClientWorker) }
|
5
|
+
after(:each) { Object.send :remove_const, :Klass }
|
6
|
+
|
7
|
+
describe "#routing_key" do
|
8
|
+
subject { HonestPubsub::Server::ClientWorker.new(routing_data, properties).routing_key }
|
9
|
+
|
10
|
+
let(:properties) { {} }
|
11
|
+
|
12
|
+
context "Routing key is 'honest.test.route'" do
|
13
|
+
let(:routing_data) { {routing_key: "honest.test.route"} }
|
14
|
+
|
15
|
+
it { should eq "honest.test.route" }
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "self.validates_payload_with" do
|
20
|
+
before do
|
21
|
+
validators.each do |validator|
|
22
|
+
Klass.validates_payload_with validator
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "One validator" do
|
27
|
+
let(:validators){ [lambda{|payload| true}] }
|
28
|
+
it {
|
29
|
+
expect(Klass.new({},{}).payload_validators.length).to eq 1 }
|
30
|
+
end
|
31
|
+
|
32
|
+
context "Two validators" do
|
33
|
+
let(:validators){ [lambda{|payload| true}, lambda{|payload| true}] }
|
34
|
+
it { expect(Klass.new({},{}).payload_validators.length).to eq 2 }
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#valid_payload?' do
|
39
|
+
context
|
40
|
+
subject { Klass.new({},{}).valid_payload?({}) }
|
41
|
+
|
42
|
+
before do
|
43
|
+
validators.each do |validator|
|
44
|
+
Klass.validates_payload_with validator
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
context "Two validators returning true" do
|
49
|
+
let(:validators){ [lambda{|payload| true}, lambda{|payload| true}] }
|
50
|
+
it { should be true }
|
51
|
+
end
|
52
|
+
|
53
|
+
context "One validator returning true one returning false" do
|
54
|
+
let(:validators){ [lambda{|payload| false}, lambda{|payload| true}] }
|
55
|
+
it { should be false }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "No validators" do
|
59
|
+
let(:validators){ [] }
|
60
|
+
it { should be true }
|
61
|
+
end
|
62
|
+
|
63
|
+
context "validators as symbol" do
|
64
|
+
context "method returns true" do
|
65
|
+
before {
|
66
|
+
Klass.class_variable_set(:@@foo_bar_called, false)
|
67
|
+
Klass.send :define_method, :foo_bar do |payload|
|
68
|
+
Klass.class_variable_set(:@@foo_bar_called, true)
|
69
|
+
true
|
70
|
+
end
|
71
|
+
}
|
72
|
+
let(:validators) { [lambda{|payload| true}, :foo_bar] }
|
73
|
+
it {
|
74
|
+
should be true
|
75
|
+
expect(Klass.class_variable_get(:@@foo_bar_called)).to be true
|
76
|
+
}
|
77
|
+
|
78
|
+
end
|
79
|
+
|
80
|
+
context "method returns false" do
|
81
|
+
before {
|
82
|
+
Klass.class_variable_set(:@@foo_bar_called, false)
|
83
|
+
Klass.send :define_method, :foo_bar do |payload|
|
84
|
+
Klass.class_variable_set(:@@foo_bar_called, true)
|
85
|
+
false
|
86
|
+
end
|
87
|
+
}
|
88
|
+
let(:validators) { [lambda{|payload| true}, :foo_bar] }
|
89
|
+
it {
|
90
|
+
should be false
|
91
|
+
expect(Klass.class_variable_get(:@@foo_bar_called)).to be true
|
92
|
+
}
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
context "proc returns false" do
|
97
|
+
before {
|
98
|
+
Klass.class_variable_set(:@@foo_bar_called, false)
|
99
|
+
Klass.send :define_method, :foo_bar do |payload|
|
100
|
+
Klass.class_variable_set(:@@foo_bar_called, true)
|
101
|
+
true
|
102
|
+
end
|
103
|
+
}
|
104
|
+
let(:validators) { [lambda{|payload| false}, :foo_bar] }
|
105
|
+
it "method shouldn't be called" do
|
106
|
+
should be false
|
107
|
+
expect(Klass.class_variable_get(:@@foo_bar_called)).to be false
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
describe "#validate_routing_key_name" do
|
115
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef')).to eq(true) }
|
116
|
+
it { expect(Klass.send(:validate_routing_key_name, '')).to eq(true) }
|
117
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.abcdef')).to eq(true) }
|
118
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.abcdef.asd')).to eq(true) }
|
119
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.abcdef/')).to eq(false) }
|
120
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.abcdef.a123')).to eq(false) }
|
121
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcdef.')).to eq(false) }
|
122
|
+
it { expect(Klass.send(:validate_routing_key_name, 'abcAf')).to eq(false) }
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "#generated_queue_name" do
|
126
|
+
before(:each) { allow(HonestPubsub::Configuration).to receive(:application_name).and_return("app_name")}
|
127
|
+
|
128
|
+
it { expect(Klass.send(:generated_queue_name, 'abcdef', 'queue_name')).to eq('queue_name') }
|
129
|
+
it { expect(Klass.send(:generated_queue_name, 'abcdef', nil)).to eq('app_name_abcdef') }
|
130
|
+
it { expect(Klass.send(:generated_queue_name, 'abcdef.abcd', nil)).to eq('app_name_abcdef_abcd') }
|
131
|
+
it { expect(Klass.send(:generated_queue_name, 'a.b.c', nil)).to eq('app_name_a_b_c') }
|
132
|
+
end
|
133
|
+
|
134
|
+
describe '.subscribe_to' do
|
135
|
+
before(:each) { allow(HonestPubsub::Configuration).to receive(:application_name).and_return("app_name")}
|
136
|
+
context 'routing_key without queue_name' do
|
137
|
+
before { Klass.subscribe_to 'a.b.c' }
|
138
|
+
it 'sets the routing key and queue name' do
|
139
|
+
expect(Klass.subscribed_key).to eq('a.b.c')
|
140
|
+
expect(Klass.subscribed_queue).to eq('app_name_a_b_c')
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
context 'routing_key with queue_name' do
|
145
|
+
before { Klass.subscribe_to 'a.b.c', on: 'foo_bar' }
|
146
|
+
it 'sets the routing key and queue name' do
|
147
|
+
expect(Klass.subscribed_key).to eq('a.b.c')
|
148
|
+
expect(Klass.subscribed_queue).to eq('foo_bar')
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
context 'invalid routing key' do
|
153
|
+
before { }
|
154
|
+
it 'sets the routing key and queue name' do
|
155
|
+
expect {
|
156
|
+
Klass.subscribe_to 'a.b.c.', on: 'foo_bar'
|
157
|
+
}.to raise_error(ArgumentError)
|
158
|
+
end
|
159
|
+
end
|
160
|
+
end
|
161
|
+
end
|
data/spec/logger_spec.rb
ADDED
@@ -0,0 +1,110 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HonestPubsub::Logger do
|
4
|
+
let(:routing_key) { "test/logger" }
|
5
|
+
let(:subject) { HonestPubsub::Logger.new }
|
6
|
+
let(:payload) { {"hit"=>"me"} }
|
7
|
+
let(:message) { HonestPubsub::Message.new.serialize(context, routing_key, payload) }
|
8
|
+
let(:config_buffer) { StringIO.new }
|
9
|
+
let(:output_string) { config_buffer.string }
|
10
|
+
let(:config_override) { {} }
|
11
|
+
let(:context) { {unique_id: "1234", orig_ip_address: "127.0.0.1"}}
|
12
|
+
|
13
|
+
before do
|
14
|
+
HonestPubsub::Logging.instance_variable_set(:"@logger", nil)
|
15
|
+
directory = File::dirname(__FILE__)
|
16
|
+
full_name = File.join(directory, "config.yml")
|
17
|
+
@config_data = HashWithIndifferentAccess.new( YAML.load_file(full_name)[ENV["RAILS_ENV"]] )
|
18
|
+
@config_data.merge!(config_override)
|
19
|
+
@config_data[:logger][:file] = config_buffer
|
20
|
+
allow(HonestPubsub::Configuration).to receive(:configuration).at_least(1).and_return(@config_data)
|
21
|
+
end
|
22
|
+
|
23
|
+
describe "#log_publish" do
|
24
|
+
let(:result) { subject.log_publish(routing_key, message)}
|
25
|
+
|
26
|
+
|
27
|
+
it "should not fail" do
|
28
|
+
expect{ result }.not_to raise_error()
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should write a row to the log" do
|
32
|
+
result
|
33
|
+
expect(output_string.length).not_to eq(0)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#failed_publish" do
|
39
|
+
let(:result) { subject.failed_publish(routing_key, {}, message)}
|
40
|
+
|
41
|
+
context "warning log level" do
|
42
|
+
|
43
|
+
it "should not fail" do
|
44
|
+
expect{ result }.not_to raise_error()
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should respect the log level of the file" do
|
48
|
+
result
|
49
|
+
expect(output_string.length).not_to eq(0)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
context "fatal log level" do
|
54
|
+
let(:config_override) { { 'logger'=> { 'level'=>'fatal' } } }
|
55
|
+
|
56
|
+
it "should not fail" do
|
57
|
+
expect{ result }.not_to raise_error()
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should respect the log level of the file" do
|
61
|
+
result
|
62
|
+
expect(output_string.length).to eq(0)
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "#log_receive" do
|
68
|
+
let(:result) { subject.log_receive(routing_key, message)}
|
69
|
+
|
70
|
+
it "should not fail" do
|
71
|
+
expect{ result }.not_to raise_error()
|
72
|
+
end
|
73
|
+
|
74
|
+
it "should respect the log level of the file" do
|
75
|
+
result
|
76
|
+
expect(output_string.length).not_to eq(0)
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "#log_service" do
|
81
|
+
let(:service_name) { "test" }
|
82
|
+
let(:result) { subject.log_service(service_name, log_level, message)}
|
83
|
+
|
84
|
+
context "debugger log" do
|
85
|
+
let(:log_level) { :debug }
|
86
|
+
it "should not fail" do
|
87
|
+
expect{ result }.not_to raise_error()
|
88
|
+
end
|
89
|
+
|
90
|
+
it "should respect the log level of the file" do
|
91
|
+
result
|
92
|
+
expect(output_string.length).to eq(0)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
context "warn log" do
|
97
|
+
let(:log_level) { :warn }
|
98
|
+
it "should not fail" do
|
99
|
+
expect{ result }.not_to raise_error()
|
100
|
+
end
|
101
|
+
|
102
|
+
it "should respect the log level of the file" do
|
103
|
+
result
|
104
|
+
expect(output_string.length).not_to eq(0)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe HonestPubsub::Message do
|
4
|
+
let(:original) { {'data'=> 'found'} }
|
5
|
+
let(:subject) { HonestPubsub::Message.new }
|
6
|
+
let(:routing_key) { "test.message" }
|
7
|
+
let(:context) { {unique_id: "1234", orig_ip_address: "127.0.0.1"}}
|
8
|
+
|
9
|
+
describe "#serialize" do
|
10
|
+
let(:result) { subject.serialize(context, routing_key, original)}
|
11
|
+
|
12
|
+
it "should not fail" do
|
13
|
+
expect{ result }.not_to raise_error()
|
14
|
+
end
|
15
|
+
|
16
|
+
it "should have all the header items" do
|
17
|
+
expect(result[:ts]).not_to be_nil
|
18
|
+
expect(result[:v]).not_to be_nil
|
19
|
+
expect(result[:pub]).not_to be_nil
|
20
|
+
expect(result[:payload]).not_to be_nil
|
21
|
+
end
|
22
|
+
|
23
|
+
it "should have the data in the payload" do
|
24
|
+
expect(result[:payload][:data]).to eq('found')
|
25
|
+
end
|
26
|
+
|
27
|
+
it "should have the attributes readable" do
|
28
|
+
result
|
29
|
+
expect(subject.ts).not_to be_nil
|
30
|
+
expect(subject.version).not_to be_nil
|
31
|
+
expect(subject.pub).not_to be_nil
|
32
|
+
expect(subject.payload).not_to be_nil
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
describe "#parse" do
|
37
|
+
let(:payload) { subject.serialize(context, routing_key, original).to_json }
|
38
|
+
let(:result) { subject.parse(payload) }
|
39
|
+
|
40
|
+
it "should not fail" do
|
41
|
+
expect{ result }.not_to raise_error()
|
42
|
+
end
|
43
|
+
|
44
|
+
it "should have all the hash items" do
|
45
|
+
expect(result[:ts]).not_to be_nil
|
46
|
+
expect(result[:v]).not_to be_nil
|
47
|
+
expect(result[:pub]).not_to be_nil
|
48
|
+
expect(result[:payload]).not_to be_nil
|
49
|
+
end
|
50
|
+
|
51
|
+
it "should have the data in the payload" do
|
52
|
+
expect(result[:payload][:data]).to eq('found')
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should have the attributes readable" do
|
56
|
+
result
|
57
|
+
expect(subject.ts).not_to be_nil
|
58
|
+
expect(subject.version).not_to be_nil
|
59
|
+
expect(subject.pub).not_to be_nil
|
60
|
+
expect(subject.payload).not_to be_nil
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# This file was generated by the `rspec --init` command. Conventionally, all
|
2
|
+
# specs live under a `spec` directory, which RSpec adds to the `$LOAD_PATH`.
|
3
|
+
# Require this file using `require "spec_helper"` to ensure that it is only
|
4
|
+
# loaded once.
|
5
|
+
#
|
6
|
+
# See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
|
7
|
+
|
8
|
+
require 'awesome_print'
|
9
|
+
require 'honest_pubsub'
|
10
|
+
require 'honest_pubsub/server'
|
11
|
+
ENV['RAILS_ENV'] ||= "test"
|
12
|
+
ENV['RACK_ENV'] ||= "test"
|
13
|
+
|
14
|
+
|
15
|
+
RSpec.configure do |config|
|
16
|
+
# config.treat_symbols_as_metadata_keys_with_true_values = true
|
17
|
+
config.run_all_when_everything_filtered = true
|
18
|
+
# Don't want to filter on anything right now.
|
19
|
+
config.filter_run :focus
|
20
|
+
|
21
|
+
# Run specs in random order to surface order dependencies. If you find an
|
22
|
+
# order dependency and want to debug it, you can fix the order by providing
|
23
|
+
# the seed, which is printed after each run.
|
24
|
+
# --seed 1234
|
25
|
+
# For now, always in same order
|
26
|
+
config.order = 'random'
|
27
|
+
end
|
28
|
+
|
29
|
+
HonestPubsub::Configuration.configure_with("test", File.join(HonestPubsub.root,"spec/config.yml") )
|
30
|
+
|
31
|
+
# Some test subscriber classes to make testing easier
|
32
|
+
class MyTestSubscriber1 < HonestPubsub::Server::ClientWorker
|
33
|
+
|
34
|
+
def perform(payload)
|
35
|
+
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class MyTestSubscriber2 < HonestPubsub::Server::ClientWorker
|
40
|
+
def perform(payload)
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
class MyTestSubscriber3 < MyTestSubscriber1
|
46
|
+
def perform(payload)
|
47
|
+
|
48
|
+
end
|
49
|
+
end
|