chasqui 0.0.1 → 0.1.0
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 +4 -4
- data/.travis.yml +4 -1
- data/Guardfile +14 -0
- data/README.md +6 -6
- data/Rakefile +16 -0
- data/bin/chasqui +6 -0
- data/chasqui.gemspec +6 -0
- data/examples/full.rb +4 -4
- data/lib/chasqui/broker.rb +70 -0
- data/lib/chasqui/cli.rb +78 -0
- data/lib/chasqui/config.rb +78 -0
- data/lib/chasqui/multi_broker.rb +56 -0
- data/lib/chasqui/subscriber.rb +74 -0
- data/lib/chasqui/version.rb +1 -1
- data/lib/chasqui/workers/resque_worker.rb +40 -0
- data/lib/chasqui/workers/sidekiq_worker.rb +25 -0
- data/lib/chasqui.rb +59 -21
- data/spec/integration/resque_spec.rb +113 -0
- data/spec/integration/subscribers.rb +14 -0
- data/spec/lib/chasqui/broker_spec.rb +10 -0
- data/spec/lib/chasqui/cli_spec.rb +74 -0
- data/spec/lib/chasqui/config_spec.rb +0 -0
- data/spec/lib/chasqui/multi_broker_spec.rb +73 -0
- data/spec/lib/chasqui/subscriber_spec.rb +67 -0
- data/spec/lib/chasqui/workers/resque_worker_spec.rb +27 -0
- data/spec/lib/chasqui/workers/sidekiq_worker_spec.rb +30 -0
- data/spec/lib/chasqui_spec.rb +185 -0
- data/spec/spec_helper.rb +5 -18
- data/spec/support/chasqui_spec_helpers.rb +21 -0
- data/spec/support/fake_logger.rb +5 -0
- data/spec/support/fake_subscriber.rb +12 -0
- data/spec/support/sidekiq_compatibility_check.rb +9 -0
- data/tmp/.keep +0 -0
- metadata +113 -6
- data/spec/chasqui_spec.rb +0 -56
@@ -0,0 +1,113 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe "Chasqui resque integration" do
|
4
|
+
|
5
|
+
PUBLISH_EVENTS = [
|
6
|
+
{ event: 'user.signup', data: ['Kelly'] },
|
7
|
+
{ event: 'account.credit', data: [1337, 'Kelly'] },
|
8
|
+
{ event: 'account.debit', data: [10, 'Kelly'] },
|
9
|
+
{ event: 'user.signup', data: ['Travis'] },
|
10
|
+
{ event: 'account.debit', data: [9000, 'Kelly'] },
|
11
|
+
{ event: 'user.cancel', data: ['Kelly'] },
|
12
|
+
{ event: 'account.credit', data: [42, 'Travis'] },
|
13
|
+
]
|
14
|
+
|
15
|
+
EXPECTED_EVENTS = {
|
16
|
+
'app1' => [
|
17
|
+
{ event: 'user.signup', data: ['Kelly'] },
|
18
|
+
{ event: 'user.signup', data: ['Travis'] },
|
19
|
+
],
|
20
|
+
'app2' => [
|
21
|
+
{ event: 'account.credit', data: [1337, 'Kelly'] },
|
22
|
+
{ event: 'account.debit', data: [10, 'Kelly'] },
|
23
|
+
{ event: 'account.debit', data: [9000, 'Kelly'] },
|
24
|
+
{ event: 'user.cancel', data: ['Kelly'] },
|
25
|
+
{ event: 'account.credit', data: [42, 'Travis'] },
|
26
|
+
],
|
27
|
+
}
|
28
|
+
|
29
|
+
before do
|
30
|
+
@subscriber_queues = %w(app1 app2)
|
31
|
+
@redis_url = 'redis://localhost:6379/13'
|
32
|
+
@redis = Redis.new url: @redis_url
|
33
|
+
@redis.keys('*').each { |k| @redis.del k }
|
34
|
+
|
35
|
+
Chasqui.configure do |c|
|
36
|
+
c.channel = 'integration'
|
37
|
+
c.redis = @redis_url
|
38
|
+
end
|
39
|
+
|
40
|
+
@pids = []
|
41
|
+
start_resque_workers
|
42
|
+
|
43
|
+
# Wait for subscribers to register before starting the broker so we don't miss events.
|
44
|
+
sleep 1
|
45
|
+
|
46
|
+
start_chasqui_broker
|
47
|
+
end
|
48
|
+
|
49
|
+
after do
|
50
|
+
terminate_child_processes
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'works' do
|
54
|
+
PUBLISH_EVENTS.each do |event|
|
55
|
+
Chasqui.publish event[:event], *event[:data]
|
56
|
+
end
|
57
|
+
|
58
|
+
begin
|
59
|
+
Timeout::timeout(10) do
|
60
|
+
EXPECTED_EVENTS.each do |subscriber_queue, events|
|
61
|
+
events.each do |expected|
|
62
|
+
_, payload = @redis.blpop "chasqui:#{subscriber_queue}:event_log"
|
63
|
+
actual = JSON.parse payload
|
64
|
+
expect(actual['event']).to eq(expected[:event])
|
65
|
+
expect(actual['data']).to eq(expected[:data])
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
rescue TimeoutError
|
70
|
+
fail "Failed to process all events in a timely manner."
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
def start_chasqui_broker
|
75
|
+
@pids << fork do
|
76
|
+
exec './bin/chasqui',
|
77
|
+
'--logfile', 'tmp/resque-spec.log',
|
78
|
+
'--redis', @redis_url,
|
79
|
+
'--debug'
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
def start_resque_workers
|
84
|
+
@subscriber_queues.each do |queue|
|
85
|
+
@pids << fork do
|
86
|
+
ENV['CHASQUI_ENV'] = 'test'
|
87
|
+
ENV['QUEUE'] = queue
|
88
|
+
ENV['TERM_CHILD'] = '1'
|
89
|
+
ENV['INTERVAL'] = '0.1'
|
90
|
+
ENV['REDIS_NAMESPACE'] = "resque:#{queue}"
|
91
|
+
ENV['REDIS_URL'] = @redis_url
|
92
|
+
exec 'bundle', 'exec', 'rake', 'resque:work'
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def terminate_child_processes
|
98
|
+
Timeout::timeout(10) do
|
99
|
+
@pids.each { |pid| kill 'TERM', pid rescue nil }
|
100
|
+
end
|
101
|
+
rescue TimeoutError
|
102
|
+
@pids.each { |pid| kill 'KILL', pid rescue nil }
|
103
|
+
fail "One or more child processes failed to terminate in a timely manner"
|
104
|
+
end
|
105
|
+
|
106
|
+
def kill(signal, pid)
|
107
|
+
Process.kill signal, pid
|
108
|
+
unless signal == 'KILL'
|
109
|
+
pid, status = Process.waitpid2 pid, 0
|
110
|
+
expect(status.exitstatus).to eq(0)
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
def log_event subscriber, args
|
2
|
+
event = subscriber.current_event
|
3
|
+
payload = { event: event['event'], data: args }.to_json
|
4
|
+
subscriber.redis.rpush "#{subscriber.queue}:event_log", payload
|
5
|
+
end
|
6
|
+
|
7
|
+
Chasqui.subscribe queue: 'app1', channel: 'integration' do
|
8
|
+
on('user.signup') { |*args| log_event self, args }
|
9
|
+
end
|
10
|
+
|
11
|
+
Chasqui.subscribe queue: 'app2', channel: 'integration' do
|
12
|
+
on('account.*') { |*args| log_event self, args }
|
13
|
+
on('user.cancel') { |*args| log_event self, args }
|
14
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'chasqui/cli'
|
3
|
+
|
4
|
+
describe Chasqui::CLI do
|
5
|
+
|
6
|
+
describe 'options' do
|
7
|
+
before { reset_config }
|
8
|
+
|
9
|
+
context do
|
10
|
+
before { Chasqui.config.broker_poll_interval = 1 }
|
11
|
+
after { File.unlink 'tmp/test.log' if File.exists? 'tmp/test.log' }
|
12
|
+
|
13
|
+
it 'changes the logfile' do
|
14
|
+
cli = Chasqui::CLI.new ['chasqui', '-f', 'tmp/test.log']
|
15
|
+
expect(cli.logfile).to eq('tmp/test.log')
|
16
|
+
|
17
|
+
cli.configure
|
18
|
+
Chasqui.logger.warn 'tmp/test.log message'
|
19
|
+
expect(File.read('tmp/test.log')).to match('tmp/test.log message')
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'changes the redis connection' do
|
24
|
+
cli = Chasqui::CLI.new ['chasqui', '-r', 'redis://10.0.0.23:6379/5']
|
25
|
+
expect(cli.redis_url).to eq('redis://10.0.0.23:6379/5')
|
26
|
+
|
27
|
+
cli.configure
|
28
|
+
expect(Chasqui.redis.redis.client.host).to eq('10.0.0.23')
|
29
|
+
end
|
30
|
+
|
31
|
+
it 'changes the inbox queue' do
|
32
|
+
cli = Chasqui::CLI.new ['chasqui', '-q', 'inbox2']
|
33
|
+
expect(cli.inbox_queue).to eq('inbox2')
|
34
|
+
|
35
|
+
cli.configure
|
36
|
+
expect(Chasqui.inbox).to eq('inbox2')
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'enables verbose mode (debug logging)' do
|
40
|
+
cli = Chasqui::CLI.new ['chasqui', '--debug']
|
41
|
+
expect(cli.debug).to be true
|
42
|
+
|
43
|
+
cli.configure
|
44
|
+
expect(Chasqui.logger.level).to eq(Logger::DEBUG)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'displays the version' do
|
48
|
+
cli = Chasqui::CLI.new ['chasqui', '-v']
|
49
|
+
cli.run
|
50
|
+
expect { cli.run }.to output(/chasqui #{Chasqui::VERSION}/).to_stdout
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'displays help' do
|
54
|
+
cli = Chasqui::CLI.new ['chasqui', '-h', '--help']
|
55
|
+
expect { cli.run }.to output(/Usage: chasqui \[options\]/).to_stdout
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '#run' do
|
60
|
+
before { reset_chasqui }
|
61
|
+
|
62
|
+
it 'configures and starts the broker' do
|
63
|
+
expect(Chasqui::Broker).to receive(:start)
|
64
|
+
|
65
|
+
argv = %w(chasqui -f tmp/test.log --debug -q inbox2 -r redis://127.0.0.1:6379/0)
|
66
|
+
Chasqui::CLI.new(argv).run
|
67
|
+
|
68
|
+
expect(Chasqui.logger.level).to eq(Logger::DEBUG)
|
69
|
+
expect(Chasqui.inbox_queue).to eq('inbox2')
|
70
|
+
expect(Chasqui.redis.client.host).to eq('127.0.0.1')
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
end
|
File without changes
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chasqui::MultiBroker do
|
4
|
+
let(:broker) { Chasqui::MultiBroker.new }
|
5
|
+
before { reset_chasqui }
|
6
|
+
|
7
|
+
describe '#forward_event' do
|
8
|
+
before do
|
9
|
+
Chasqui.config.channel = 'app1'
|
10
|
+
Chasqui.subscribe queue: 'queue1', channel: 'app1'
|
11
|
+
Chasqui.subscribe queue: 'queue2', channel: 'app2'
|
12
|
+
Chasqui.subscribe queue: 'queue3', channel: 'app1'
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'places the event on all subscriber queues' do
|
16
|
+
Chasqui.publish 'foo.bar', 'A'
|
17
|
+
broker.forward_event
|
18
|
+
|
19
|
+
expect(redis.llen('inbox')).to eq(0)
|
20
|
+
|
21
|
+
expect(redis.llen('queue:queue1')).to eq(1)
|
22
|
+
expect(redis.llen('queue:queue2')).to eq(0)
|
23
|
+
expect(redis.llen('queue:queue3')).to eq(1)
|
24
|
+
|
25
|
+
event = { 'event' => 'foo.bar', 'channel' => 'app1', 'data' => ['A'] }
|
26
|
+
|
27
|
+
job1 = JSON.parse redis.rpop('queue:queue1')
|
28
|
+
expect(job1['args']).to include(event)
|
29
|
+
|
30
|
+
job3 = JSON.parse redis.rpop('queue:queue3')
|
31
|
+
expect(job3['args']).to include(event)
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'blocks on empty queues' do
|
35
|
+
thread = Thread.new { broker.forward_event }
|
36
|
+
|
37
|
+
begin
|
38
|
+
Timeout::timeout(1) do
|
39
|
+
Chasqui.config.redis = Redis.new
|
40
|
+
Chasqui.config.channel = 'app2'
|
41
|
+
Chasqui.publish 'foo.bar', 'A'
|
42
|
+
|
43
|
+
job = JSON.parse redis.brpop('queue:queue2')[1]
|
44
|
+
expect(job).to include('class' => 'Chasqui::Subscriber__queue2')
|
45
|
+
expect(job).to include('args' =>
|
46
|
+
[{ 'event' => 'foo.bar', 'channel' => 'app2', 'data' => ['A'] }])
|
47
|
+
end
|
48
|
+
ensure
|
49
|
+
thread.kill
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
it "doesn't lose events if the broker fails" do
|
54
|
+
Chasqui.config.channel = 'app2'
|
55
|
+
Chasqui.publish 'foo', 'process'
|
56
|
+
Chasqui.publish 'foo', 'keep in queue'
|
57
|
+
allow(broker.redis).to receive(:smembers).and_raise(Redis::ConnectionError)
|
58
|
+
|
59
|
+
expect(-> { broker.forward_event }).to raise_error(Redis::ConnectionError)
|
60
|
+
expect(redis.llen('queue:queue2')).to eq(0)
|
61
|
+
expect(redis.llen(broker.in_progress_queue)).to eq(1)
|
62
|
+
|
63
|
+
allow(broker.redis).to receive(:smembers).and_call_original
|
64
|
+
broker.forward_event
|
65
|
+
expect(redis.llen('queue:queue2')).to eq(1)
|
66
|
+
|
67
|
+
job = JSON.parse redis.rpop('queue:queue2')
|
68
|
+
expect(job['args']).to include(
|
69
|
+
'event' => 'foo', 'channel' => 'app2', 'data' => ['process'])
|
70
|
+
expect(redis.llen(broker.in_progress_queue)).to eq(0)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chasqui::Subscriber do
|
4
|
+
let(:subscriber) { Chasqui::Subscriber.new 'my-queue', 'my.channel' }
|
5
|
+
|
6
|
+
it { expect(subscriber.queue).to eq('my-queue') }
|
7
|
+
it { expect(subscriber.channel).to eq('my.channel') }
|
8
|
+
|
9
|
+
describe '#on' do
|
10
|
+
it 'registers the event handlers' do
|
11
|
+
subscriber.on('foo') { |foo| foo }
|
12
|
+
subscriber.on('zig.zag') { |*args| args }
|
13
|
+
|
14
|
+
pattern = subscriber.matching_handler_patterns_for('foo').first
|
15
|
+
expect(subscriber.call_handler(pattern, 'bar')).to eq('bar')
|
16
|
+
|
17
|
+
pattern = subscriber.matching_handler_patterns_for('zig.zag').first
|
18
|
+
expect(subscriber.call_handler(pattern, 1, 2, 3, 4)).to eq([1, 2, 3, 4])
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'raises when registering duplicate handlers' do
|
22
|
+
subscriber.on('foo') { |foo| foo }
|
23
|
+
expect(-> {
|
24
|
+
subscriber.on('foo') { |a, b| a + b }
|
25
|
+
}).to raise_error(Chasqui::HandlerAlreadyRegistered)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#matching_handler_patterns_for' do
|
30
|
+
it 'always returns an array' do
|
31
|
+
expect(subscriber.matching_handler_patterns_for('unknown')).to eq([])
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'matches single event' do
|
35
|
+
p = Proc.new { }
|
36
|
+
subscriber.on('foo', &p)
|
37
|
+
expect(subscriber.matching_handler_patterns_for('foo')).to eq([/\Afoo\z/])
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'matches wildcards' do
|
41
|
+
p = 6.times.map { Proc.new { } }
|
42
|
+
subscriber.on('foo*', &p[0])
|
43
|
+
subscriber.on('bar', &p[1])
|
44
|
+
subscriber.on('*bar', &p[2])
|
45
|
+
subscriber.on('*', &p[3])
|
46
|
+
subscriber.on('*a*', &p[4])
|
47
|
+
subscriber.on('*z*', &p[5])
|
48
|
+
expect(subscriber.matching_handler_patterns_for('foo.bar')).to eq([
|
49
|
+
/\Afoo.*\z/,
|
50
|
+
/\A.*bar\z/,
|
51
|
+
/\A.*\z/,
|
52
|
+
/\A.*a.*\z/
|
53
|
+
])
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe '#perform' do
|
58
|
+
it 'calls the matching event handlers' do
|
59
|
+
calls = []
|
60
|
+
subscriber.on('foo.bar') { |a, b| calls << a + b }
|
61
|
+
subscriber.on('foo.*') { |a, b| calls << a ** b }
|
62
|
+
subscriber.perform(redis, { 'event' => 'foo.bar', 'data' => [3, 4] })
|
63
|
+
expect(calls.sort).to eq([7, 81])
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'resque'
|
3
|
+
|
4
|
+
describe Chasqui::ResqueWorker do
|
5
|
+
let(:subscriber) { FakeSubscriber.new 'my-queue', 'my.channel'}
|
6
|
+
|
7
|
+
describe '.create' do
|
8
|
+
it 'configures a new worker' do
|
9
|
+
worker_class = Chasqui::ResqueWorker.create(subscriber)
|
10
|
+
expect(worker_class.name).to eq('Chasqui::Subscriber__my_queue')
|
11
|
+
expect(worker_class.ancestors).to include(Chasqui::ResqueWorker)
|
12
|
+
expect(worker_class.instance_variable_get(:@queue)).to eq('my-queue')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
describe '.perform' do
|
17
|
+
let(:worker) { Chasqui::ResqueWorker.create(subscriber) }
|
18
|
+
|
19
|
+
it 'delegates to the subscriber' do
|
20
|
+
event = { 'event' => 'foo', 'data' => ['bar'] }
|
21
|
+
worker.perform event
|
22
|
+
received_event = subscriber.events.shift
|
23
|
+
expect(received_event).to eq(event)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
if sidekiq_supported_ruby_version?
|
4
|
+
|
5
|
+
describe Chasqui::SidekiqWorker do
|
6
|
+
let(:subscriber) { FakeSubscriber.new 'my-queue', 'my.channel'}
|
7
|
+
|
8
|
+
describe '.create' do
|
9
|
+
it 'configures a new worker' do
|
10
|
+
worker_class = Chasqui::SidekiqWorker.create(subscriber)
|
11
|
+
expect(worker_class.included_modules).to include(Sidekiq::Worker)
|
12
|
+
expect(worker_class.sidekiq_options).to include('queue' => 'my-queue')
|
13
|
+
expect(worker_class.new).to be_kind_of(Chasqui::SidekiqWorker)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
describe '#perform' do
|
18
|
+
let(:worker_class) { Chasqui::SidekiqWorker.create(subscriber) }
|
19
|
+
|
20
|
+
it 'delegates to the subscriber' do
|
21
|
+
event = { 'event' => 'foo', 'data' => ['bar'] }
|
22
|
+
worker = worker_class.new
|
23
|
+
worker.perform event
|
24
|
+
received_event = subscriber.events.shift
|
25
|
+
expect(received_event).to eq(event)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,185 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Chasqui do
|
4
|
+
it 'has a version number' do
|
5
|
+
expect(Chasqui::VERSION).not_to be nil
|
6
|
+
end
|
7
|
+
|
8
|
+
describe '.configure' do
|
9
|
+
before { reset_config }
|
10
|
+
|
11
|
+
context 'defaults' do
|
12
|
+
it { expect(Chasqui.channel).to eq('__default') }
|
13
|
+
it { expect(Chasqui.inbox_queue).to eq('inbox') }
|
14
|
+
it { expect(Chasqui.redis.client.db).to eq(0) }
|
15
|
+
it { expect(Chasqui.config.broker_poll_interval).to eq(3) }
|
16
|
+
it { expect(Chasqui.config.worker_backend).to eq(nil) }
|
17
|
+
|
18
|
+
it do
|
19
|
+
# remove chasqui's test environment logger
|
20
|
+
Chasqui.config[:logger] = nil
|
21
|
+
expect(Chasqui.logger).to be_kind_of(Logger)
|
22
|
+
end
|
23
|
+
|
24
|
+
it { expect(Chasqui.logger.level).to eq(Logger::INFO) }
|
25
|
+
it { expect(Chasqui.logger.progname).to eq('chasqui') }
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'configures the channel' do
|
29
|
+
Chasqui.config.channel = 'com.example.test'
|
30
|
+
expect(Chasqui.channel).to eq('com.example.test')
|
31
|
+
end
|
32
|
+
|
33
|
+
it 'accepts a block' do
|
34
|
+
Chasqui.configure { |config| config.channel = 'com.example.test' }
|
35
|
+
expect(Chasqui.channel).to eq('com.example.test')
|
36
|
+
end
|
37
|
+
|
38
|
+
it 'configures the inbox queue' do
|
39
|
+
Chasqui.config.inbox_queue = 'foo'
|
40
|
+
expect(Chasqui.inbox).to eq('foo')
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'configures the broker poll interval' do
|
44
|
+
Chasqui.config.broker_poll_interval = 1
|
45
|
+
expect(Chasqui.config.broker_poll_interval).to eq(1)
|
46
|
+
end
|
47
|
+
|
48
|
+
context 'redis' do
|
49
|
+
it 'accepts config options' do
|
50
|
+
redis_config = { host: '10.0.3.24' }
|
51
|
+
Chasqui.config.redis = redis_config
|
52
|
+
expect(Chasqui.redis.client.host).to eq('10.0.3.24')
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'accepts an initialized client' do
|
56
|
+
redis = Redis.new db: 2
|
57
|
+
Chasqui.config.redis = redis
|
58
|
+
expect(Chasqui.redis.client.db).to eq(2)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'accepts URLs' do
|
62
|
+
Chasqui.config.redis = 'redis://10.0.1.21:12345/0'
|
63
|
+
expect(Chasqui.redis.client.host).to eq('10.0.1.21')
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'uses a namespace' do
|
67
|
+
Chasqui.redis.set 'foo', 'bar'
|
68
|
+
expect(Chasqui.redis.redis.get 'chasqui:foo').to eq('bar')
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe 'logger' do
|
73
|
+
it 'accepts a log device' do
|
74
|
+
logs = StringIO.new
|
75
|
+
Chasqui.config.logger = logs
|
76
|
+
Chasqui.logger.info "status"
|
77
|
+
Chasqui.logger.warn "error"
|
78
|
+
|
79
|
+
logs.rewind
|
80
|
+
output = logs.read
|
81
|
+
|
82
|
+
%w(chasqui INFO status WARN error).each do |text|
|
83
|
+
expect(output).to match(text)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
it 'accepts a logger-like object' do
|
88
|
+
fake_logger = FakeLogger.new
|
89
|
+
Chasqui.config.logger = fake_logger
|
90
|
+
expect(Chasqui.logger).to eq(fake_logger)
|
91
|
+
expect(Chasqui.logger.progname).to eq('chasqui')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe '.publish' do
|
97
|
+
before { reset_chasqui }
|
98
|
+
|
99
|
+
it 'pushes messages to the inbox queue' do
|
100
|
+
payloads = [
|
101
|
+
[1, 2, {'foo'=>'bar'}],
|
102
|
+
[3, 4, {'biz'=>'baz'}]
|
103
|
+
]
|
104
|
+
|
105
|
+
payloads.each do |args|
|
106
|
+
Chasqui.publish 'test.event', *args
|
107
|
+
end
|
108
|
+
|
109
|
+
payloads.each do |data|
|
110
|
+
event = JSON.load Chasqui.redis.rpop('inbox')
|
111
|
+
expect(event['event']).to eq('test.event')
|
112
|
+
expect(event['channel']).to eq('__default')
|
113
|
+
expect(event['data']).to eq(data)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
it 'supports channels' do
|
118
|
+
Chasqui.config.channel = 'my.app'
|
119
|
+
Chasqui.publish 'test.event', :foo
|
120
|
+
event = JSON.load Chasqui.redis.rpop('inbox')
|
121
|
+
expect(event['event']).to eq('test.event')
|
122
|
+
expect(event['channel']).to eq('my.app')
|
123
|
+
expect(event['data']).to eq(['foo'])
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
127
|
+
describe '.subscribe' do
|
128
|
+
before { reset_chasqui }
|
129
|
+
|
130
|
+
it 'saves subscriptions' do
|
131
|
+
sub1 = Chasqui.subscribe queue: 'app1-queue', channel: 'com.example.admin'
|
132
|
+
sub2 = Chasqui.subscribe queue: 'app2-queue', channel: 'com.example.admin'
|
133
|
+
sub3 = Chasqui.subscribe queue: 'app1-queue', channel: 'com.example.video'
|
134
|
+
|
135
|
+
queues = Chasqui.redis.smembers "subscribers:com.example.admin"
|
136
|
+
expect(queues.sort).to eq(['app1-queue', 'app2-queue'])
|
137
|
+
|
138
|
+
queues = Chasqui.redis.smembers "subscribers:com.example.video"
|
139
|
+
expect(queues).to eq(['app1-queue'])
|
140
|
+
|
141
|
+
expect(Chasqui.subscriber('app1-queue')).to eq(sub1)
|
142
|
+
expect(Chasqui.subscriber('app2-queue')).to eq(sub2)
|
143
|
+
expect(sub1).to eq(sub3)
|
144
|
+
end
|
145
|
+
|
146
|
+
it 'returns a subscriber' do
|
147
|
+
subscriber = Chasqui.subscribe queue: 'app1-queue', channel: 'com.example.admin'
|
148
|
+
expect(subscriber).to be_kind_of(Chasqui::Subscriber)
|
149
|
+
end
|
150
|
+
|
151
|
+
it 'yields a subscriber configuration context' do
|
152
|
+
$context = nil
|
153
|
+
Chasqui.subscribe queue: 'foo', channel: 'bar' do
|
154
|
+
$context = self
|
155
|
+
end
|
156
|
+
expect($context).to be_kind_of(Chasqui::Subscriber)
|
157
|
+
end
|
158
|
+
end
|
159
|
+
|
160
|
+
describe '.create_worker' do
|
161
|
+
let(:subscriber) { j }
|
162
|
+
|
163
|
+
it 'raises when no worker backend configured' do
|
164
|
+
expect(-> {
|
165
|
+
Chasqui.create_worker nil
|
166
|
+
}).to raise_error(Chasqui::ConfigurationError)
|
167
|
+
end
|
168
|
+
|
169
|
+
it 'creates a resque worker' do
|
170
|
+
subscriber = FakeSubscriber.new 'resque-queue', 'fake-channel'
|
171
|
+
Chasqui.config.worker_backend = :resque
|
172
|
+
worker = Chasqui.create_worker subscriber
|
173
|
+
expect(worker.new).to be_kind_of(Chasqui::ResqueWorker)
|
174
|
+
end
|
175
|
+
|
176
|
+
if sidekiq_supported_ruby_version?
|
177
|
+
it 'creates a sidekiq worker' do
|
178
|
+
subscriber = FakeSubscriber.new 'sidekiq-queue', 'fake-channel'
|
179
|
+
Chasqui.config.worker_backend = :sidekiq
|
180
|
+
worker = Chasqui.create_worker subscriber
|
181
|
+
expect(worker.new).to be_kind_of(Chasqui::SidekiqWorker)
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,23 +1,10 @@
|
|
1
1
|
$LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
|
2
2
|
require 'chasqui'
|
3
|
+
require 'resque'
|
4
|
+
require 'pp'
|
3
5
|
|
4
|
-
|
5
|
-
def initialize
|
6
|
-
@queues = {}
|
7
|
-
end
|
6
|
+
Dir['spec/support/*.rb'].each { |file| require File.expand_path(file) }
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
end
|
12
|
-
|
13
|
-
def lpop(key)
|
14
|
-
queue(key).shift
|
15
|
-
end
|
16
|
-
|
17
|
-
private
|
18
|
-
|
19
|
-
def queue(key)
|
20
|
-
@queues[key] ||= []
|
21
|
-
@queues[key]
|
22
|
-
end
|
8
|
+
RSpec.configure do |c|
|
9
|
+
c.include ChasquiSpecHelpers
|
23
10
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module ChasquiSpecHelpers
|
2
|
+
|
3
|
+
def reset_chasqui
|
4
|
+
reset_config
|
5
|
+
flush_redis
|
6
|
+
end
|
7
|
+
|
8
|
+
def reset_config
|
9
|
+
Chasqui.instance_variable_set(:@config, nil)
|
10
|
+
Chasqui.config.logger = open('/dev/null', 'w+')
|
11
|
+
end
|
12
|
+
|
13
|
+
def redis
|
14
|
+
Chasqui.redis
|
15
|
+
end
|
16
|
+
|
17
|
+
def flush_redis
|
18
|
+
redis.keys('*').each { |k| redis.del k }
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
@@ -0,0 +1,9 @@
|
|
1
|
+
# Newer versions of sidekiq only support newer versions of ruby
|
2
|
+
# https://github.com/mperham/sidekiq/blob/master/Changes.md#322
|
3
|
+
def sidekiq_supported_ruby_version?
|
4
|
+
Gem::Version.new(RUBY_VERSION.dup) > Gem::Version.new('1.9.3')
|
5
|
+
end
|
6
|
+
|
7
|
+
if sidekiq_supported_ruby_version?
|
8
|
+
require 'sidekiq'
|
9
|
+
end
|
data/tmp/.keep
ADDED
File without changes
|