receptor_controller-client 0.0.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/CHANGELOG.md +16 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +202 -0
- data/README.md +8 -0
- data/Rakefile +7 -0
- data/lib/receptor_controller-client.rb +4 -0
- data/lib/receptor_controller/client.rb +94 -0
- data/lib/receptor_controller/client/configuration.rb +85 -0
- data/lib/receptor_controller/client/directive.rb +33 -0
- data/lib/receptor_controller/client/directive_blocking.rb +87 -0
- data/lib/receptor_controller/client/directive_non_blocking.rb +144 -0
- data/lib/receptor_controller/client/exception.rb +10 -0
- data/lib/receptor_controller/client/response_worker.rb +214 -0
- data/lib/receptor_controller/client/version.rb +5 -0
- data/receptor_controller-client.gemspec +41 -0
- data/spec/receptor_controller/client_spec.rb +87 -0
- data/spec/receptor_controller/directive_blocking_spec.rb +197 -0
- data/spec/receptor_controller/directive_non_blocking_spec.rb +160 -0
- data/spec/receptor_controller/response_worker_spec.rb +169 -0
- data/spec/spec_helper.rb +19 -0
- metadata +268 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
=begin
|
|
4
|
+
# Receptor-Controller Client
|
|
5
|
+
=end
|
|
6
|
+
|
|
7
|
+
$:.push File.expand_path("../lib", __FILE__)
|
|
8
|
+
require "receptor_controller/client/version"
|
|
9
|
+
|
|
10
|
+
Gem::Specification.new do |s|
|
|
11
|
+
s.name = "receptor_controller-client"
|
|
12
|
+
s.version = ReceptorController::Client::VERSION
|
|
13
|
+
s.platform = Gem::Platform::RUBY
|
|
14
|
+
s.authors = ["Martin Slemr"]
|
|
15
|
+
s.email = ["mslemr@redhat.com"]
|
|
16
|
+
s.homepage = "https://github.com/RedHatInsights/receptor_controller-client-ruby"
|
|
17
|
+
s.summary = "Client for communication with Platform Receptor Controller - Gem"
|
|
18
|
+
s.description = "Client for communication with Platform Receptor Controller"
|
|
19
|
+
s.license = "Apache-2.0"
|
|
20
|
+
s.required_ruby_version = ">= 2.5"
|
|
21
|
+
|
|
22
|
+
s.add_runtime_dependency 'activesupport', '~> 5.2.4.3'
|
|
23
|
+
s.add_runtime_dependency 'concurrent-ruby', '~> 1.1', '>= 1.1.6'
|
|
24
|
+
s.add_runtime_dependency 'faraday', '~> 1.0'
|
|
25
|
+
s.add_runtime_dependency 'json', '~> 2.3', '>= 2.3.0'
|
|
26
|
+
s.add_runtime_dependency 'manageiq-loggers', '~> 0.5.0'
|
|
27
|
+
s.add_runtime_dependency 'manageiq-messaging', '~> 0.1.5'
|
|
28
|
+
|
|
29
|
+
s.add_development_dependency 'bundler', '~> 2.0'
|
|
30
|
+
s.add_development_dependency 'rake', '>= 12.3.3'
|
|
31
|
+
s.add_development_dependency 'rspec', '~> 3.6', '>= 3.6.0'
|
|
32
|
+
s.add_development_dependency 'rubocop', '~>0.69.0'
|
|
33
|
+
s.add_development_dependency 'rubocop-performance', '~>1.3'
|
|
34
|
+
s.add_development_dependency 'simplecov', '~> 0.17.1'
|
|
35
|
+
s.add_development_dependency 'webmock'
|
|
36
|
+
|
|
37
|
+
s.files = `find *`.split("\n").uniq.sort.select { |f| !f.empty? }
|
|
38
|
+
s.test_files = `find spec/*`.split("\n")
|
|
39
|
+
s.executables = []
|
|
40
|
+
s.require_paths = ["lib"]
|
|
41
|
+
end
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
require "receptor_controller/client"
|
|
2
|
+
|
|
3
|
+
RSpec.describe ReceptorController::Client do
|
|
4
|
+
let(:external_tenant) { '0000001' }
|
|
5
|
+
let(:organization_id) { '000001' }
|
|
6
|
+
let(:identity) do
|
|
7
|
+
{"x-rh-identity" => Base64.strict_encode64({"identity" => {"account_number" => external_tenant, "user" => {"is_org_admin" => true}, "internal" => {"org_id" => organization_id}}}.to_json)}
|
|
8
|
+
end
|
|
9
|
+
let(:headers) do
|
|
10
|
+
{"Content-Type" => "application/json",
|
|
11
|
+
"Accept" => "*/*",
|
|
12
|
+
"Accept-Encoding" => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3'}.merge(identity)
|
|
13
|
+
end
|
|
14
|
+
let(:receptor_scheme) { 'http' }
|
|
15
|
+
let(:receptor_host) { 'localhost:9090' }
|
|
16
|
+
let(:receptor_node) { 'testing-receptor' }
|
|
17
|
+
let(:receptor_config) do
|
|
18
|
+
ReceptorController::Client::Configuration.new do |config|
|
|
19
|
+
config.controller_scheme = receptor_scheme
|
|
20
|
+
config.controller_host = receptor_host
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
let(:satellite_uid) { '1234567890' }
|
|
24
|
+
|
|
25
|
+
subject { described_class.new(:config => receptor_config) }
|
|
26
|
+
|
|
27
|
+
before do
|
|
28
|
+
subject.identity_header = identity
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
describe "#connection_status" do
|
|
32
|
+
it "makes a POST request to receptor and returns status key-value if successful" do
|
|
33
|
+
response = {"status" => "connected"}
|
|
34
|
+
stub_request(:post, "#{receptor_scheme}://#{receptor_host}/connection/status")
|
|
35
|
+
.with(:body => {"account" => external_tenant, "node_id" => receptor_node}.to_json,
|
|
36
|
+
:headers => headers)
|
|
37
|
+
.to_return(:status => 200, :body => response.to_json, :headers => {})
|
|
38
|
+
|
|
39
|
+
expect(subject.connection_status(external_tenant, receptor_node)).to eq(response)
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it "makes a POST request to receptor and returns disconnected status in case of error" do
|
|
43
|
+
stub_request(:post, "#{receptor_scheme}://#{receptor_host}/connection/status")
|
|
44
|
+
.with(:body => {"account" => external_tenant, "node_id" => receptor_node}.to_json,
|
|
45
|
+
:headers => headers)
|
|
46
|
+
.to_return(:status => 401, :body => {"errors" => [{"status" => 401, "detail" => "Unauthorized"}]}.to_json, :headers => {})
|
|
47
|
+
|
|
48
|
+
expect(subject.connection_status(external_tenant, receptor_node)).to eq(described_class::STATUS_DISCONNECTED)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "makes a POST request and returns disconnected if receptor unavailable" do
|
|
52
|
+
allow(Faraday).to receive(:post).and_raise(Faraday::ConnectionFailed, "Failed to open TCP connection to #{receptor_host}")
|
|
53
|
+
|
|
54
|
+
expect(subject.connection_status(external_tenant, receptor_node)).to eq(described_class::STATUS_DISCONNECTED)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
describe "#directive" do
|
|
59
|
+
it "creates blocking or non-blocking directive" do
|
|
60
|
+
%i[blocking non_blocking].each do |type|
|
|
61
|
+
directive = subject.directive(nil,
|
|
62
|
+
nil,
|
|
63
|
+
:payload => nil,
|
|
64
|
+
:directive => 'xxx',
|
|
65
|
+
:type => type)
|
|
66
|
+
klass = type == :blocking ? ReceptorController::Client::DirectiveBlocking : ReceptorController::Client::DirectiveNonBlocking
|
|
67
|
+
expect(directive).to be_a_kind_of(klass)
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
describe "#headers" do
|
|
73
|
+
let(:pre_shared_key) { '123456789' }
|
|
74
|
+
|
|
75
|
+
it "uses identity_header if PSK is not provided" do
|
|
76
|
+
expect(subject.headers).to eq({"Content-Type" => "application/json"}.merge(identity))
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
it "uses pre-shared key if provided" do
|
|
80
|
+
subject.config.configure do |config|
|
|
81
|
+
config.pre_shared_key = pre_shared_key
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
expect(subject.headers("000001")).to eq("Content-Type" => "application/json", "x-rh-receptor-controller-account" => "000001", "x-rh-receptor-controller-client-id" => "topological-inventory", "x-rh-receptor-controller-psk" => "123456789")
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
require "receptor_controller/client/directive_blocking"
|
|
2
|
+
|
|
3
|
+
RSpec.describe ReceptorController::Client::DirectiveBlocking do
|
|
4
|
+
# TODO: definitions below contain the same like non-blocking spec
|
|
5
|
+
let(:external_tenant) { '0000001' }
|
|
6
|
+
let(:organization_id) { '000001' }
|
|
7
|
+
let(:identity) do
|
|
8
|
+
{"x-rh-identity" => Base64.strict_encode64({"identity" => {"account_number" => external_tenant, "user" => {"is_org_admin" => true}, "internal" => {"org_id" => organization_id}}}.to_json)}
|
|
9
|
+
end
|
|
10
|
+
let(:headers) do
|
|
11
|
+
{"Content-Type" => "application/json",
|
|
12
|
+
"Accept" => "*/*",
|
|
13
|
+
"Accept-Encoding" => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3'}.merge(identity)
|
|
14
|
+
end
|
|
15
|
+
let(:receptor_scheme) { 'http' }
|
|
16
|
+
let(:receptor_host) { 'localhost:9090' }
|
|
17
|
+
let(:receptor_node) { 'testing-receptor' }
|
|
18
|
+
let(:receptor_config) do
|
|
19
|
+
ReceptorController::Client::Configuration.new do |config|
|
|
20
|
+
config.controller_scheme = receptor_scheme
|
|
21
|
+
config.controller_host = receptor_host
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
let(:receptor_client) do
|
|
25
|
+
client = ReceptorController::Client.new(:config => receptor_config)
|
|
26
|
+
client.identity_header = identity
|
|
27
|
+
client
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
let(:payload) { {'method' => :get, 'url' => 'tower.example.com', 'headers' => {}, 'ssl' => false}.to_json }
|
|
31
|
+
let(:directive) { 'receptor_http:execute' }
|
|
32
|
+
|
|
33
|
+
subject { described_class.new(:name => directive, :account => external_tenant, :node_id => receptor_node, :payload => payload, :client => receptor_client) }
|
|
34
|
+
|
|
35
|
+
describe "#call" do
|
|
36
|
+
it "makes POST /job request to receptor and registers received message ID" do
|
|
37
|
+
allow(subject).to receive(:wait_for_response)
|
|
38
|
+
|
|
39
|
+
response = {"id" => '1234'}
|
|
40
|
+
|
|
41
|
+
stub_request(:post, "#{receptor_scheme}://#{receptor_host}/job")
|
|
42
|
+
.with(:body => subject.default_body.to_json,
|
|
43
|
+
:headers => headers)
|
|
44
|
+
.to_return(:status => 200, :body => response.to_json, :headers => {})
|
|
45
|
+
|
|
46
|
+
expect(subject.response_worker).to receive(:register_message).with(response['id'], subject)
|
|
47
|
+
|
|
48
|
+
subject.call
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
it "raises ControllerResponseError if POST /job returns error" do
|
|
52
|
+
stub_request(:post, "#{receptor_scheme}://#{receptor_host}/job")
|
|
53
|
+
.with(:body => subject.default_body.to_json,
|
|
54
|
+
:headers => headers)
|
|
55
|
+
.to_return(:status => 401, :body => {"errors" => [{"status" => 401, "detail" => "Unauthorized"}]}.to_json, :headers => {})
|
|
56
|
+
|
|
57
|
+
expect(subject.response_worker).not_to receive(:register_message)
|
|
58
|
+
|
|
59
|
+
expect { subject.call }.to raise_error(ReceptorController::Client::ControllerResponseError)
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context "waiting for callbacks" do
|
|
63
|
+
let(:http_response) { {"id" => '1234'} }
|
|
64
|
+
let(:kafka_client) { double("Kafka client") }
|
|
65
|
+
let(:kafka_response) { double("Kafka response") }
|
|
66
|
+
|
|
67
|
+
before do
|
|
68
|
+
stub_request(:post, "#{receptor_scheme}://#{receptor_host}/job")
|
|
69
|
+
.with(:body => subject.default_body.to_json,
|
|
70
|
+
:headers => headers)
|
|
71
|
+
.to_return(:status => 200, :body => http_response.to_json, :headers => {})
|
|
72
|
+
|
|
73
|
+
allow(kafka_response).to receive(:ack)
|
|
74
|
+
|
|
75
|
+
allow(ManageIQ::Messaging::Client).to receive(:open).and_return(kafka_client)
|
|
76
|
+
allow(kafka_client).to receive(:subscribe_topic).and_yield(kafka_response)
|
|
77
|
+
allow(kafka_client).to receive(:close)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it "waits for successful response and sets data" do
|
|
81
|
+
response_message = {'code' => 0,
|
|
82
|
+
'in_response_to' => http_response['id'],
|
|
83
|
+
'message_type' => subject.class::MESSAGE_TYPE_RESPONSE,
|
|
84
|
+
'payload' => 'Test payload'}
|
|
85
|
+
|
|
86
|
+
allow(kafka_response).to receive(:payload).and_return(response_message.to_json)
|
|
87
|
+
|
|
88
|
+
expect(subject).to(receive(:response_success)
|
|
89
|
+
.with(response_message['in_response_to'],
|
|
90
|
+
response_message['message_type'],
|
|
91
|
+
response_message['payload'])
|
|
92
|
+
.and_wrap_original) do |m, *args|
|
|
93
|
+
m.call(*args) # Doesn't release lock, only EOF response can
|
|
94
|
+
subject.send(:response_waiting).signal
|
|
95
|
+
subject.response_worker.stop
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
subject.response_worker.start
|
|
99
|
+
subject.call
|
|
100
|
+
|
|
101
|
+
expect(subject.send(:response_data)).to eq(response_message['payload'])
|
|
102
|
+
expect(subject.send(:response_exception)).to be_nil
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it "waits for EOF response" do
|
|
106
|
+
response_message = {'code' => 0,
|
|
107
|
+
'in_response_to' => http_response['id'],
|
|
108
|
+
'message_type' => subject.class::MESSAGE_TYPE_EOF,
|
|
109
|
+
'payload' => 'Unimportant'}
|
|
110
|
+
|
|
111
|
+
allow(kafka_response).to receive(:payload).and_return(response_message.to_json)
|
|
112
|
+
|
|
113
|
+
expect(subject).to(receive(:response_success)
|
|
114
|
+
.with(response_message['in_response_to'],
|
|
115
|
+
response_message['message_type'],
|
|
116
|
+
response_message['payload'])
|
|
117
|
+
.and_wrap_original) do |m, *args|
|
|
118
|
+
m.call(*args) # original call releases lock
|
|
119
|
+
subject.response_worker.stop
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
subject.response_worker.start
|
|
123
|
+
subject.call
|
|
124
|
+
|
|
125
|
+
expect(subject.send(:response_data)).to be_nil
|
|
126
|
+
expect(subject.send(:response_exception)).to be_nil
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
it "raises UnknownResponseTypeError if unknown successful response received" do
|
|
130
|
+
response_message = {'code' => 0,
|
|
131
|
+
'in_response_to' => http_response['id'],
|
|
132
|
+
'message_type' => 'unknown',
|
|
133
|
+
'payload' => 'Unimportant'}
|
|
134
|
+
|
|
135
|
+
allow(kafka_response).to receive(:payload).and_return(response_message.to_json)
|
|
136
|
+
|
|
137
|
+
expect(subject).to(receive(:response_success)
|
|
138
|
+
.with(response_message['in_response_to'],
|
|
139
|
+
response_message['message_type'],
|
|
140
|
+
response_message['payload'])
|
|
141
|
+
.and_wrap_original) do |m, *args|
|
|
142
|
+
m.call(*args) # original call releases lock
|
|
143
|
+
subject.response_worker.stop
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
subject.response_worker.start
|
|
147
|
+
expect { subject.call }.to raise_error(ReceptorController::Client::UnknownResponseTypeError)
|
|
148
|
+
|
|
149
|
+
expect(subject.send(:response_data)).to be_nil
|
|
150
|
+
expect(subject.send(:response_exception)).to be_kind_of(ReceptorController::Client::UnknownResponseTypeError)
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it "raises ResponseError if error response received" do
|
|
154
|
+
response_message = {'code' => 1,
|
|
155
|
+
'in_response_to' => http_response['id'],
|
|
156
|
+
'payload' => 'Some error message'}
|
|
157
|
+
|
|
158
|
+
allow(kafka_response).to receive(:payload).and_return(response_message.to_json)
|
|
159
|
+
|
|
160
|
+
expect(subject).to(receive(:response_error)
|
|
161
|
+
.with(response_message['in_response_to'],
|
|
162
|
+
response_message['code'],
|
|
163
|
+
response_message['payload'])
|
|
164
|
+
.and_wrap_original) do |m, *args|
|
|
165
|
+
m.call(*args) # original calls releases lock
|
|
166
|
+
subject.response_worker.stop
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
subject.response_worker.start
|
|
170
|
+
expect { subject.call }.to raise_error(ReceptorController::Client::ResponseError)
|
|
171
|
+
|
|
172
|
+
expect(subject.send(:response_data)).to be_nil
|
|
173
|
+
expect(subject.send(:response_exception)).to be_kind_of(ReceptorController::Client::ResponseError)
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
it "raises ResponseTimeoutError if timeout response received" do
|
|
177
|
+
receptor_client.config.response_timeout = 1.second
|
|
178
|
+
receptor_client.config.response_timeout_poll_time = 0
|
|
179
|
+
|
|
180
|
+
allow(kafka_client).to receive(:subscribe_topic).and_return(nil)
|
|
181
|
+
|
|
182
|
+
expect(subject).to(receive(:response_timeout)
|
|
183
|
+
.with(http_response['id'])
|
|
184
|
+
.and_wrap_original) do |m, *args|
|
|
185
|
+
m.call(*args) # original call releases lock
|
|
186
|
+
subject.response_worker.stop
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
subject.response_worker.start
|
|
190
|
+
expect { subject.call }.to raise_error(ReceptorController::Client::ResponseTimeoutError)
|
|
191
|
+
|
|
192
|
+
expect(subject.send(:response_data)).to be_nil
|
|
193
|
+
expect(subject.send(:response_exception)).to be_kind_of(ReceptorController::Client::ResponseTimeoutError)
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
end
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
require "receptor_controller/client/directive_non_blocking"
|
|
2
|
+
|
|
3
|
+
RSpec.describe ReceptorController::Client::DirectiveNonBlocking do
|
|
4
|
+
let(:external_tenant) { '0000001' }
|
|
5
|
+
let(:organization_id) { '000001' }
|
|
6
|
+
let(:identity) do
|
|
7
|
+
{"x-rh-identity" => Base64.strict_encode64({"identity" => {"account_number" => external_tenant, "user" => {"is_org_admin" => true}, "internal" => {"org_id" => organization_id}}}.to_json)}
|
|
8
|
+
end
|
|
9
|
+
let(:headers) do
|
|
10
|
+
{"Content-Type" => "application/json",
|
|
11
|
+
"Accept" => "*/*",
|
|
12
|
+
"Accept-Encoding" => 'gzip;q=1.0,deflate;q=0.6,identity;q=0.3'}.merge(identity)
|
|
13
|
+
end
|
|
14
|
+
let(:receptor_scheme) { 'http' }
|
|
15
|
+
let(:receptor_host) { 'localhost:9090' }
|
|
16
|
+
let(:receptor_node) { 'testing-receptor' }
|
|
17
|
+
let(:receptor_config) do
|
|
18
|
+
ReceptorController::Client::Configuration.new do |config|
|
|
19
|
+
config.controller_scheme = receptor_scheme
|
|
20
|
+
config.controller_host = receptor_host
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
let(:receptor_client) do
|
|
24
|
+
client = ReceptorController::Client.new
|
|
25
|
+
client.identity_header = identity
|
|
26
|
+
client
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
let(:satellite_uid) { '1234567890' }
|
|
30
|
+
let(:payload) { {'satellite_instance_id' => satellite_uid.to_s}.to_json }
|
|
31
|
+
let(:directive) { 'receptor_satellite:health_check' }
|
|
32
|
+
|
|
33
|
+
subject { described_class.new(:name => directive, :account => external_tenant, :node_id => receptor_node, :payload => payload, :client => receptor_client) }
|
|
34
|
+
|
|
35
|
+
describe "#call" do
|
|
36
|
+
it "makes POST /job request to receptor, registers received message ID and returns it" do
|
|
37
|
+
response = {"id" => '1234'}
|
|
38
|
+
|
|
39
|
+
stub_request(:post, "#{receptor_scheme}://#{receptor_host}/job")
|
|
40
|
+
.with(:body => subject.default_body.to_json,
|
|
41
|
+
:headers => headers)
|
|
42
|
+
.to_return(:status => 200, :body => response.to_json, :headers => {})
|
|
43
|
+
|
|
44
|
+
expect(subject.response_worker).to receive(:register_message).with(response['id'], subject)
|
|
45
|
+
|
|
46
|
+
expect(subject.call).to eq(response['id'])
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
it "makes POST /job request to receptor, doesn't register message and returns nil in case of error" do
|
|
50
|
+
stub_request(:post, "#{receptor_scheme}://#{receptor_host}/job")
|
|
51
|
+
.with(:body => subject.default_body.to_json,
|
|
52
|
+
:headers => headers)
|
|
53
|
+
.to_return(:status => 401, :body => {"errors" => [{"status" => 401, "detail" => "Unauthorized"}]}.to_json, :headers => {})
|
|
54
|
+
|
|
55
|
+
expect(subject.response_worker).not_to receive(:register_message)
|
|
56
|
+
|
|
57
|
+
expect(subject.call).to be_nil
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
it "makes a POST request and returns disconnected if receptor unavailable" do
|
|
61
|
+
allow(Faraday).to receive(:post).and_raise(Faraday::ConnectionFailed, "Failed to open TCP connection to #{receptor_host}")
|
|
62
|
+
|
|
63
|
+
expect(subject.response_worker).not_to receive(:register_message)
|
|
64
|
+
|
|
65
|
+
expect(subject.call).to be_nil
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
context "callbacks" do
|
|
70
|
+
let(:response_id) { '1234' }
|
|
71
|
+
let(:response) { double('Kafka response', :ack => nil) }
|
|
72
|
+
|
|
73
|
+
before do
|
|
74
|
+
# HTTP request
|
|
75
|
+
stub_request(:post, "#{receptor_scheme}://#{receptor_host}/job")
|
|
76
|
+
.with(:body => subject.default_body.to_json,
|
|
77
|
+
:headers => headers)
|
|
78
|
+
.to_return(:status => 200, :body => {'id' => response_id}.to_json, :headers => {})
|
|
79
|
+
|
|
80
|
+
subject.call
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it "calls success/eof blocks" do
|
|
84
|
+
mutex = Mutex.new
|
|
85
|
+
cv = ConditionVariable.new
|
|
86
|
+
|
|
87
|
+
# Callbacks
|
|
88
|
+
result = {}
|
|
89
|
+
subject
|
|
90
|
+
.on_success do |msg_id, payload|
|
|
91
|
+
result[:first] = {:id => msg_id, :payload => payload}
|
|
92
|
+
end
|
|
93
|
+
.on_success do |msg_id, payload|
|
|
94
|
+
result[:second] = {:id => msg_id, :payload => payload}
|
|
95
|
+
end
|
|
96
|
+
.on_eof do |msg_id|
|
|
97
|
+
result[:eof] = {:id => msg_id}
|
|
98
|
+
mutex.synchronize { cv.signal }
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
# Kafka response - 'response' type
|
|
102
|
+
expect(subject).to receive(:response_success).twice.and_call_original
|
|
103
|
+
message_type, payload = 'response', 'Testing payload'
|
|
104
|
+
response_payload = {'code' => 0, 'in_response_to' => response_id, 'message_type' => message_type, 'payload' => payload}
|
|
105
|
+
allow(response).to receive(:payload).and_return(response_payload.to_json)
|
|
106
|
+
subject.response_worker.send(:process_message, response)
|
|
107
|
+
|
|
108
|
+
# Kafka response - 'eof' type
|
|
109
|
+
message_type = 'eof'
|
|
110
|
+
response_payload = {'code' => 0, 'in_response_to' => response_id, 'message_type' => message_type, 'payload' => nil}
|
|
111
|
+
allow(response).to receive(:payload).and_return(response_payload.to_json)
|
|
112
|
+
subject.response_worker.send(:process_message, response)
|
|
113
|
+
|
|
114
|
+
mutex.synchronize { cv.wait(mutex) }
|
|
115
|
+
|
|
116
|
+
# Result containing both "on_success" blocks
|
|
117
|
+
block_result = {:id => response_id, :payload => payload}
|
|
118
|
+
expect(result).to eq(:first => block_result,
|
|
119
|
+
:second => block_result,
|
|
120
|
+
:eof => {:id => response_id})
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
it "processes eof blocks always after all success blocks" do
|
|
124
|
+
success_cnt = Concurrent::AtomicFixnum.new(0)
|
|
125
|
+
|
|
126
|
+
success_calls = 3
|
|
127
|
+
|
|
128
|
+
mutex = Mutex.new
|
|
129
|
+
cv = ConditionVariable.new
|
|
130
|
+
|
|
131
|
+
# Callbacks
|
|
132
|
+
subject
|
|
133
|
+
.on_success do |_msg_id, _payload|
|
|
134
|
+
success_cnt.increment
|
|
135
|
+
end
|
|
136
|
+
.on_eof do |_msg_id|
|
|
137
|
+
# Tests
|
|
138
|
+
expect(success_cnt.value).to eq(success_calls)
|
|
139
|
+
|
|
140
|
+
mutex.synchronize { cv.signal }
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
# Kafka response - 'response' type - success
|
|
144
|
+
message_type, payload = 'response', 'Testing payload'
|
|
145
|
+
response_payload = {'code' => 0, 'in_response_to' => response_id, 'message_type' => message_type, 'payload' => payload}
|
|
146
|
+
allow(response).to receive(:payload).and_return(response_payload.to_json)
|
|
147
|
+
success_calls.times do
|
|
148
|
+
subject.response_worker.send(:process_message, response)
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
# Kafka response - 'eof' type
|
|
152
|
+
message_type = 'eof'
|
|
153
|
+
response_payload = {'code' => 0, 'in_response_to' => response_id, 'message_type' => message_type, 'payload' => nil}
|
|
154
|
+
allow(response).to receive(:payload).and_return(response_payload.to_json)
|
|
155
|
+
subject.response_worker.send(:process_message, response)
|
|
156
|
+
|
|
157
|
+
mutex.synchronize { cv.wait(mutex) }
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|