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.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/#.ruby-gemset# +0 -0
  3. data/.gitignore +22 -0
  4. data/.ruby-version +1 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +92 -0
  8. data/Rakefile +1 -0
  9. data/bin/start_subscribers +14 -0
  10. data/config/pubsub.yml +17 -0
  11. data/honest_pubsub.gemspec +38 -0
  12. data/lib/honest_pubsub/cli.rb +94 -0
  13. data/lib/honest_pubsub/configuration.rb +38 -0
  14. data/lib/honest_pubsub/context.rb +70 -0
  15. data/lib/honest_pubsub/db_logger.rb +52 -0
  16. data/lib/honest_pubsub/exceptions/payload_error.rb +2 -0
  17. data/lib/honest_pubsub/logger.rb +49 -0
  18. data/lib/honest_pubsub/logging.rb +42 -0
  19. data/lib/honest_pubsub/message.rb +50 -0
  20. data/lib/honest_pubsub/middleware.rb +14 -0
  21. data/lib/honest_pubsub/publisher.rb +93 -0
  22. data/lib/honest_pubsub/railtie.rb +27 -0
  23. data/lib/honest_pubsub/server/client_queue_listener.rb +54 -0
  24. data/lib/honest_pubsub/server/client_worker.rb +86 -0
  25. data/lib/honest_pubsub/server/subscriber_server.rb +84 -0
  26. data/lib/honest_pubsub/server.rb +8 -0
  27. data/lib/honest_pubsub/subscriber.rb +69 -0
  28. data/lib/honest_pubsub/version.rb +3 -0
  29. data/lib/honest_pubsub.rb +46 -0
  30. data/spec/config.yml +20 -0
  31. data/spec/honest_pubsub/#subscriber_spec.rb# +9 -0
  32. data/spec/honest_pubsub/cli_spec.rb +145 -0
  33. data/spec/honest_pubsub/server/client_queue_listener_spec.rb +76 -0
  34. data/spec/honest_pubsub/server/client_worker_spec.rb +161 -0
  35. data/spec/honest_pubsub/subscriber_spec.rb +5 -0
  36. data/spec/logger_spec.rb +110 -0
  37. data/spec/message_spec.rb +65 -0
  38. data/spec/spec_helper.rb +49 -0
  39. 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
@@ -0,0 +1,5 @@
1
+ require 'spec_helper'
2
+
3
+ describe HonestPubsub::Subscriber do
4
+ subject { HonestPubsub::Subscriber.new( routing_key ) }
5
+ end
@@ -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
@@ -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