fastly_nsq 0.13.2 → 1.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 +5 -5
- data/.env +4 -0
- data/.overcommit.yml +3 -3
- data/.rubocop.yml +11 -1
- data/.travis.yml +8 -1
- data/Gemfile +9 -0
- data/README.md +52 -82
- data/Rakefile +2 -0
- data/bin/fastly_nsq +1 -0
- data/docker-compose.yml +23 -0
- data/examples/.sample.env +0 -3
- data/fastly_nsq.gemspec +7 -8
- data/lib/fastly_nsq.rb +44 -50
- data/lib/fastly_nsq/cli.rb +20 -14
- data/lib/fastly_nsq/consumer.rb +26 -30
- data/lib/fastly_nsq/feeder.rb +16 -0
- data/lib/fastly_nsq/http/nsqd.rb +7 -1
- data/lib/fastly_nsq/http/nsqlookupd.rb +1 -1
- data/lib/fastly_nsq/launcher.rb +31 -23
- data/lib/fastly_nsq/listener.rb +34 -103
- data/lib/fastly_nsq/manager.rb +48 -72
- data/lib/fastly_nsq/message.rb +2 -0
- data/lib/fastly_nsq/messenger.rb +5 -5
- data/lib/fastly_nsq/priority_queue.rb +12 -0
- data/lib/fastly_nsq/priority_thread_pool.rb +32 -0
- data/lib/fastly_nsq/producer.rb +52 -32
- data/lib/fastly_nsq/testing.rb +239 -0
- data/lib/fastly_nsq/tls_options.rb +2 -0
- data/lib/fastly_nsq/version.rb +3 -1
- data/spec/{lib/fastly_nsq/cli_spec.rb → cli_spec.rb} +2 -0
- data/spec/consumer_spec.rb +59 -0
- data/spec/fastly_nsq_spec.rb +72 -0
- data/spec/feeder_spec.rb +22 -0
- data/spec/{lib/fastly_nsq/http → http}/nsqd_spec.rb +1 -1
- data/spec/{lib/fastly_nsq/http → http}/nsqlookupd_spec.rb +1 -1
- data/spec/{lib/fastly_nsq/http_spec.rb → http_spec.rb} +3 -1
- data/spec/integration_spec.rb +48 -0
- data/spec/launcher_spec.rb +50 -0
- data/spec/listener_spec.rb +184 -0
- data/spec/manager_spec.rb +111 -0
- data/spec/matchers/delegate.rb +32 -0
- data/spec/{lib/fastly_nsq/message_spec.rb → message_spec.rb} +2 -0
- data/spec/{lib/fastly_nsq/messenger_spec.rb → messenger_spec.rb} +7 -5
- data/spec/priority_thread_pool_spec.rb +19 -0
- data/spec/producer_spec.rb +94 -0
- data/spec/spec_helper.rb +32 -28
- data/spec/support/http.rb +37 -0
- data/spec/support/webmock.rb +22 -0
- data/spec/{lib/fastly_nsq/tls_options_spec.rb → tls_options_spec.rb} +2 -0
- metadata +54 -96
- data/env_configuration_for_local_gem_tests.yml +0 -5
- data/example_config_class.rb +0 -20
- data/examples/Rakefile +0 -41
- data/lib/fastly_nsq/fake_backend.rb +0 -114
- data/lib/fastly_nsq/listener/config.rb +0 -35
- data/lib/fastly_nsq/rake_task.rb +0 -78
- data/lib/fastly_nsq/strategy.rb +0 -36
- data/spec/lib/fastly_nsq/consumer_spec.rb +0 -72
- data/spec/lib/fastly_nsq/fake_backend_spec.rb +0 -135
- data/spec/lib/fastly_nsq/fastly_nsq_spec.rb +0 -10
- data/spec/lib/fastly_nsq/launcher_spec.rb +0 -56
- data/spec/lib/fastly_nsq/listener_spec.rb +0 -213
- data/spec/lib/fastly_nsq/manager_spec.rb +0 -127
- data/spec/lib/fastly_nsq/producer_spec.rb +0 -60
- data/spec/lib/fastly_nsq/rake_task_spec.rb +0 -142
- data/spec/lib/fastly_nsq/strategy_spec.rb +0 -36
- data/spec/lib/fastly_nsq_spec.rb +0 -18
- data/spec/support/env_helpers.rb +0 -15
@@ -1,56 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'fastly_nsq/launcher'
|
3
|
-
|
4
|
-
RSpec.describe FastlyNsq::Launcher do
|
5
|
-
let(:launcher) { FastlyNsq::Launcher.new options }
|
6
|
-
let(:manager) { instance_double 'Manager', start: nil, quiet: nil, stop: nil }
|
7
|
-
let(:thread) { instance_double 'Thread' }
|
8
|
-
let(:options) { { joe: 'biden', timeout: 9 } }
|
9
|
-
|
10
|
-
before do
|
11
|
-
allow(FastlyNsq::Manager).to receive(:new).and_return(manager)
|
12
|
-
allow(launcher).to receive(:safe_thread).and_return(thread)
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'creates a manager with correct options' do
|
16
|
-
expect(FastlyNsq::Manager).to have_received(:new).with(options)
|
17
|
-
end
|
18
|
-
|
19
|
-
describe '#run' do
|
20
|
-
it 'creates a heartbeat thread' do
|
21
|
-
launcher.run
|
22
|
-
expect(launcher).to have_received(:safe_thread).with('heartbeat')
|
23
|
-
expect(launcher.stopping?).to eq false
|
24
|
-
end
|
25
|
-
|
26
|
-
it 'starts the setup manager' do
|
27
|
-
launcher.run
|
28
|
-
expect(manager).to have_received(:start)
|
29
|
-
expect(launcher.stopping?).to eq false
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
describe '#quiet' do
|
34
|
-
it 'quites the manager and sets done' do
|
35
|
-
expect(launcher.stopping?).to eq false
|
36
|
-
launcher.quiet
|
37
|
-
expect(manager).to have_received(:quiet)
|
38
|
-
expect(launcher.stopping?).to eq true
|
39
|
-
end
|
40
|
-
end
|
41
|
-
|
42
|
-
describe '#stop' do
|
43
|
-
it 'stops the manager within a deadline' do
|
44
|
-
now = Time.now
|
45
|
-
allow(Time).to receive(:now).and_return(now)
|
46
|
-
launcher.stop
|
47
|
-
expect(manager).to have_received(:stop).with(now + options[:timeout])
|
48
|
-
end
|
49
|
-
|
50
|
-
it 'quites the manager' do
|
51
|
-
launcher.stop
|
52
|
-
expect(manager).to have_received(:quiet)
|
53
|
-
expect(launcher.stopping?).to eq true
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
@@ -1,213 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe FastlyNsq::Listener do
|
4
|
-
let(:topic) { 'testing_topic' }
|
5
|
-
let(:channel) { 'testing_channel' }
|
6
|
-
let(:consumer) { FastlyNsq::FakeBackend::Consumer.new topic: topic, channel: channel }
|
7
|
-
let(:logger) { double 'Logger', info: nil, debug: nil, error: nil }
|
8
|
-
|
9
|
-
module TestMessageProcessor
|
10
|
-
@@messages_processed = []
|
11
|
-
Message = Struct.new(:body, :topic) do
|
12
|
-
def finish
|
13
|
-
@did_finish = true
|
14
|
-
end
|
15
|
-
end
|
16
|
-
|
17
|
-
def self.process(incoming_message)
|
18
|
-
@@messages_processed.push Message.new(incoming_message.to_s)
|
19
|
-
end
|
20
|
-
|
21
|
-
def self.messages_processed
|
22
|
-
@@messages_processed
|
23
|
-
end
|
24
|
-
|
25
|
-
def self.clear
|
26
|
-
@@messages_processed = []
|
27
|
-
end
|
28
|
-
end
|
29
|
-
|
30
|
-
let(:listener) do
|
31
|
-
FastlyNsq::Listener.new topic: topic,
|
32
|
-
processor: TestMessageProcessor,
|
33
|
-
consumer: consumer,
|
34
|
-
logger: logger
|
35
|
-
end
|
36
|
-
|
37
|
-
let(:message) { TestMessageProcessor::Message.new 'this is message body' }
|
38
|
-
let(:messages_processed) { TestMessageProcessor.messages_processed }
|
39
|
-
let(:expected_message) { TestMessageProcessor::Message.new 'this is message body' }
|
40
|
-
let(:expected_messages) { [expected_message] }
|
41
|
-
|
42
|
-
describe 'instantiating without a consumer' do
|
43
|
-
it 'instantiates a consumer, passing the topic and channel' do
|
44
|
-
allow(FastlyNsq::Consumer).to receive(:new)
|
45
|
-
|
46
|
-
FastlyNsq::Listener.new topic: topic,
|
47
|
-
channel: channel,
|
48
|
-
processor: TestMessageProcessor,
|
49
|
-
consumer: nil
|
50
|
-
|
51
|
-
expect(FastlyNsq::Consumer).to have_received(:new).
|
52
|
-
with(topic: topic, channel: channel)
|
53
|
-
end
|
54
|
-
end
|
55
|
-
|
56
|
-
context 'when not passed a manager' do
|
57
|
-
it 'creates a blank manager' do
|
58
|
-
expect(listener.identity[:manager]).to_not be_nil
|
59
|
-
end
|
60
|
-
end
|
61
|
-
|
62
|
-
context 'when using the fake queue and it is empty', fake_queue: true do
|
63
|
-
before do
|
64
|
-
TestMessageProcessor.clear
|
65
|
-
FastlyNsq::FakeBackend.delay = 0.1
|
66
|
-
end
|
67
|
-
|
68
|
-
it 'blocks on the process for longer than the check cycle' do
|
69
|
-
delay = FastlyNsq::FakeBackend.delay + 0.1
|
70
|
-
|
71
|
-
expect do
|
72
|
-
Timeout.timeout(delay) do
|
73
|
-
listener.go run_once: true
|
74
|
-
end
|
75
|
-
end.to raise_error(Timeout::Error)
|
76
|
-
end
|
77
|
-
end
|
78
|
-
|
79
|
-
describe 'when processing next message' do
|
80
|
-
before(:each) do
|
81
|
-
TestMessageProcessor.clear
|
82
|
-
allow(consumer).to receive(:pop).and_return(message)
|
83
|
-
end
|
84
|
-
|
85
|
-
it 'processes the next message' do
|
86
|
-
listener.go run_once: true
|
87
|
-
|
88
|
-
expect(messages_processed).to eql(expected_messages)
|
89
|
-
end
|
90
|
-
|
91
|
-
it 'finishes the message' do
|
92
|
-
allow(message).to receive(:finish)
|
93
|
-
|
94
|
-
listener.go run_once: true
|
95
|
-
|
96
|
-
expect(message).to have_received(:finish).once
|
97
|
-
end
|
98
|
-
|
99
|
-
it 'logs info for the message body' do
|
100
|
-
allow(logger).to receive(:info)
|
101
|
-
listener.go run_once: true
|
102
|
-
|
103
|
-
expect(logger).to have_received(:info).once.with(/\[NSQ\] Message received on topic \[#{topic}\]: #{message.body}/)
|
104
|
-
end
|
105
|
-
|
106
|
-
context 'when preprocessor is provided' do
|
107
|
-
it 'calls the preprocessor' do
|
108
|
-
preprocessor_was_called = false
|
109
|
-
preprocessor = ->(*_args) { preprocessor_was_called = true }
|
110
|
-
|
111
|
-
listener = FastlyNsq::Listener.new topic: topic,
|
112
|
-
processor: TestMessageProcessor,
|
113
|
-
consumer: consumer,
|
114
|
-
logger: logger,
|
115
|
-
preprocessor: preprocessor
|
116
|
-
|
117
|
-
listener.go run_once: true
|
118
|
-
expect(preprocessor_was_called).to be_truthy
|
119
|
-
end
|
120
|
-
end
|
121
|
-
|
122
|
-
context 'when running as a thread' do
|
123
|
-
let(:manager) { double 'Manager', listener_stopped: nil, listener_killed: nil }
|
124
|
-
let(:thread) { double 'FakeThread', raise: nil, kill: nil, status: 'fake_thread' }
|
125
|
-
let(:listener) do
|
126
|
-
FastlyNsq::Listener.new topic: topic,
|
127
|
-
processor: TestMessageProcessor,
|
128
|
-
logger: logger,
|
129
|
-
consumer: consumer,
|
130
|
-
manager: manager
|
131
|
-
end
|
132
|
-
|
133
|
-
describe 'shutdown' do
|
134
|
-
it 'informs the manager of a shutdown when run once' do
|
135
|
-
listener.go run_once: true
|
136
|
-
|
137
|
-
expect(manager).to have_received(:listener_stopped).with(listener)
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
before do
|
142
|
-
allow(listener).to receive(:safe_thread).and_return(thread)
|
143
|
-
end
|
144
|
-
|
145
|
-
it 'starts and provide status' do
|
146
|
-
listener.start
|
147
|
-
expect(logger).to have_received(:info)
|
148
|
-
expect(listener.status).to eq 'fake_thread'
|
149
|
-
end
|
150
|
-
|
151
|
-
it 'can describe itself' do
|
152
|
-
id = listener.identity
|
153
|
-
expect(id[:consumer]).to_not be_nil
|
154
|
-
expect(id[:manager]).to be manager
|
155
|
-
expect(id[:preprocessor]).to be_nil
|
156
|
-
expect(id[:processor]).to_not be_nil
|
157
|
-
expect(id[:topic]).to_not be_nil
|
158
|
-
end
|
159
|
-
|
160
|
-
it 'can be cleanly duplicated' do
|
161
|
-
new_listener = listener.reset_then_dup
|
162
|
-
|
163
|
-
expect(listener.identity).to eq new_listener.identity
|
164
|
-
end
|
165
|
-
|
166
|
-
it 'can be terminated' do
|
167
|
-
listener.start
|
168
|
-
state = listener.instance_variable_get(:@done)
|
169
|
-
expect(state).to eq false
|
170
|
-
consumer = listener.instance_variable_get(:@consumer)
|
171
|
-
allow(consumer).to receive(:terminate)
|
172
|
-
listener.terminate
|
173
|
-
|
174
|
-
state = listener.instance_variable_get(:@done)
|
175
|
-
expect(logger).to have_received(:info).thrice
|
176
|
-
expect(consumer).to have_received(:terminate)
|
177
|
-
expect(state).to eq true
|
178
|
-
end
|
179
|
-
|
180
|
-
it 'raise in terminate to interupt blocking on an empty queue' do
|
181
|
-
listener.start
|
182
|
-
listener.terminate
|
183
|
-
|
184
|
-
expect(consumer.empty?).to be true
|
185
|
-
expect(thread).to have_received(:raise)
|
186
|
-
end
|
187
|
-
|
188
|
-
it 'terminate allows messages to be completed' do
|
189
|
-
100.times { FastlyNsq::Messenger.deliver(on_topic: topic, message: 'test_message') }
|
190
|
-
listener.start
|
191
|
-
listener.terminate
|
192
|
-
|
193
|
-
expect(consumer.empty?).to be false
|
194
|
-
expect(thread).to_not have_received(:raise)
|
195
|
-
end
|
196
|
-
|
197
|
-
it 'can be killed' do
|
198
|
-
listener.start
|
199
|
-
state = listener.instance_variable_get(:@done)
|
200
|
-
expect(state).to eq false
|
201
|
-
consumer = listener.instance_variable_get(:@consumer)
|
202
|
-
allow(consumer).to receive(:terminate)
|
203
|
-
listener.kill
|
204
|
-
|
205
|
-
state = listener.instance_variable_get(:@done)
|
206
|
-
expect(logger).to have_received(:info).thrice
|
207
|
-
expect(consumer).to have_received(:terminate)
|
208
|
-
expect(thread).to have_received(:raise).with(FastlyNsq::Shutdown)
|
209
|
-
expect(state).to eq true
|
210
|
-
end
|
211
|
-
end
|
212
|
-
end
|
213
|
-
end
|
@@ -1,127 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
require 'fastly_nsq/manager'
|
3
|
-
|
4
|
-
RSpec.describe FastlyNsq::Manager do
|
5
|
-
class TestProcessor
|
6
|
-
def self.process(message)
|
7
|
-
FastlyNsq.logger.info("IN PROCESS: #{message}")
|
8
|
-
end
|
9
|
-
end
|
10
|
-
|
11
|
-
let(:listener_1) { instance_double 'Listener1', new: nil, start: nil, terminate: nil, kill: nil, reset_then_dup: listener_dup }
|
12
|
-
let(:listener_2) { instance_double 'Listener2', new: nil, start: nil, terminate: nil, kill: nil, reset_then_dup: listener_dup }
|
13
|
-
let(:listener_dup) { instance_double 'ListenerDup', start: nil }
|
14
|
-
let(:manager) { FastlyNsq::Manager.new options }
|
15
|
-
let(:options) { { joe: 'biden' } }
|
16
|
-
|
17
|
-
let(:configed_topics) do
|
18
|
-
[
|
19
|
-
{ topic: 'warm_topic', klass: TestProcessor },
|
20
|
-
{ topic: 'cool_topic', klass: TestProcessor },
|
21
|
-
]
|
22
|
-
end
|
23
|
-
|
24
|
-
before do
|
25
|
-
logger = Logger.new(STDOUT)
|
26
|
-
logger.level = Logger::FATAL
|
27
|
-
|
28
|
-
FastlyNsq.configure do |config|
|
29
|
-
config.channel = 'william'
|
30
|
-
config.logger = logger
|
31
|
-
config.listener_config do |lc|
|
32
|
-
configed_topics.each do |t|
|
33
|
-
lc.add_topic t[:topic], t[:klass]
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
|
-
allow(FastlyNsq::Listener).to receive(:new).and_return(listener_1, listener_2)
|
39
|
-
manager.start
|
40
|
-
end
|
41
|
-
|
42
|
-
after do
|
43
|
-
FastlyNsq.reset_config
|
44
|
-
end
|
45
|
-
|
46
|
-
describe '#start' do
|
47
|
-
it 'sets up each configured listener' do
|
48
|
-
configed_topics.each do |t|
|
49
|
-
expect(FastlyNsq::Listener).to have_received(:new).with(
|
50
|
-
channel: FastlyNsq.channel,
|
51
|
-
manager: manager,
|
52
|
-
preprocessor: FastlyNsq.preprocessor,
|
53
|
-
processor: t[:klass],
|
54
|
-
topic: t[:topic],
|
55
|
-
)
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
it 'starts all listeners' do
|
60
|
-
expect(listener_1).to have_received(:start)
|
61
|
-
expect(listener_2).to have_received(:start)
|
62
|
-
end
|
63
|
-
|
64
|
-
it 'populates @listeners with all created listeners' do
|
65
|
-
expect(manager.listeners).to eq Set.new([listener_1, listener_2])
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
describe '#quiet' do
|
70
|
-
it 'does nothing if stopping' do
|
71
|
-
manager.instance_variable_set(:@done, true)
|
72
|
-
manager.quiet
|
73
|
-
expect(listener_1).to_not have_received(:terminate)
|
74
|
-
expect(listener_2).to_not have_received(:terminate)
|
75
|
-
end
|
76
|
-
|
77
|
-
it 'terminates all listeners' do
|
78
|
-
manager.quiet
|
79
|
-
expect(listener_1).to have_received(:terminate)
|
80
|
-
expect(listener_2).to have_received(:terminate)
|
81
|
-
end
|
82
|
-
end
|
83
|
-
|
84
|
-
describe '#stop' do
|
85
|
-
it 'does nothing if no listeners exist post quiet' do
|
86
|
-
allow(manager).to receive(:quiet)
|
87
|
-
allow(manager).to receive(:hard_shutdown)
|
88
|
-
manager.instance_variable_set(:@listeners, Set.new)
|
89
|
-
|
90
|
-
manager.stop(Time.now)
|
91
|
-
|
92
|
-
expect(manager).to have_received(:quiet)
|
93
|
-
expect(manager).to_not have_received(:hard_shutdown)
|
94
|
-
end
|
95
|
-
|
96
|
-
it 'forces shutdown if listeners remain after deadline' do
|
97
|
-
allow(manager).to receive(:hard_shutdown).and_call_original
|
98
|
-
|
99
|
-
manager.stop(Time.now)
|
100
|
-
|
101
|
-
expect(manager).to have_received(:hard_shutdown)
|
102
|
-
expect(listener_1).to have_received(:kill)
|
103
|
-
expect(listener_2).to have_received(:kill)
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
describe '#listener_stopped' do
|
108
|
-
it 'removes listeners from set of listeners' do
|
109
|
-
manager.listener_stopped(listener_1)
|
110
|
-
expect(manager.listeners).to eq Set.new([listener_2])
|
111
|
-
end
|
112
|
-
end
|
113
|
-
|
114
|
-
describe '#listener_killed' do
|
115
|
-
it 'removes listeners from set of listeners' do
|
116
|
-
manager.quiet # mark for stopping
|
117
|
-
manager.listener_killed(listener_1)
|
118
|
-
expect(manager.listeners).to eq Set.new([listener_2])
|
119
|
-
end
|
120
|
-
|
121
|
-
it 'creates and starts replacements if not stopping' do
|
122
|
-
manager.listener_killed(listener_1)
|
123
|
-
expect(manager.listeners).to eq Set.new([listener_dup, listener_2])
|
124
|
-
expect(listener_dup).to have_received(:start)
|
125
|
-
end
|
126
|
-
end
|
127
|
-
end
|
@@ -1,60 +0,0 @@
|
|
1
|
-
require 'spec_helper'
|
2
|
-
|
3
|
-
RSpec.describe FastlyNsq::Producer do
|
4
|
-
let(:topic) { 'death_star' }
|
5
|
-
let(:producer) { FastlyNsq::Producer.new(topic: topic) }
|
6
|
-
|
7
|
-
describe 'when connector connects to a backend Producer' do
|
8
|
-
let(:backend) { instance_double FastlyNsq::FakeBackend::Producer, write: nil, terminate: nil, connected?: true }
|
9
|
-
let(:connector) { double 'Connector', new: backend }
|
10
|
-
let(:producer) do
|
11
|
-
FastlyNsq::Producer.new topic: topic, connector: connector
|
12
|
-
end
|
13
|
-
|
14
|
-
it 'forwards #write' do
|
15
|
-
expect(backend).to receive(:write).with("it's a message")
|
16
|
-
producer.write "it's a message"
|
17
|
-
end
|
18
|
-
|
19
|
-
it 'forwards #terminate' do
|
20
|
-
expect(backend).to receive(:terminate)
|
21
|
-
producer.terminate
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
describe 'using the default connector' do
|
26
|
-
module TestStrategy
|
27
|
-
module Producer
|
28
|
-
@@never_terminated = true
|
29
|
-
|
30
|
-
module_function
|
31
|
-
|
32
|
-
def new(*_)
|
33
|
-
self
|
34
|
-
end
|
35
|
-
|
36
|
-
def connected?
|
37
|
-
true
|
38
|
-
end
|
39
|
-
|
40
|
-
def terminate
|
41
|
-
raise 'Already terminated once' unless @@never_terminated
|
42
|
-
@@never_terminated = false
|
43
|
-
end
|
44
|
-
|
45
|
-
def was_terminated
|
46
|
-
!@@never_terminated
|
47
|
-
end
|
48
|
-
end
|
49
|
-
end
|
50
|
-
|
51
|
-
before do
|
52
|
-
allow(FastlyNsq).to receive(:strategy).and_return(TestStrategy)
|
53
|
-
end
|
54
|
-
|
55
|
-
it 'instantiates a producer via Strategy' do
|
56
|
-
producer.terminate
|
57
|
-
expect(TestStrategy::Producer.was_terminated).to be_truthy
|
58
|
-
end
|
59
|
-
end
|
60
|
-
end
|