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
|