mamiya 0.0.1.alpha2

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 (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +17 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +16 -0
  5. data/Gemfile +8 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +43 -0
  8. data/Rakefile +6 -0
  9. data/bin/mamiya +17 -0
  10. data/config.example.yml +11 -0
  11. data/docs/sequences/deploy.png +0 -0
  12. data/docs/sequences/deploy.uml +58 -0
  13. data/example.rb +74 -0
  14. data/lib/mamiya.rb +5 -0
  15. data/lib/mamiya/agent.rb +181 -0
  16. data/lib/mamiya/agent/actions.rb +12 -0
  17. data/lib/mamiya/agent/fetcher.rb +137 -0
  18. data/lib/mamiya/agent/handlers/abstract.rb +20 -0
  19. data/lib/mamiya/agent/handlers/fetch.rb +68 -0
  20. data/lib/mamiya/cli.rb +322 -0
  21. data/lib/mamiya/cli/client.rb +172 -0
  22. data/lib/mamiya/config.rb +57 -0
  23. data/lib/mamiya/dsl.rb +192 -0
  24. data/lib/mamiya/helpers/git.rb +75 -0
  25. data/lib/mamiya/logger.rb +190 -0
  26. data/lib/mamiya/master.rb +118 -0
  27. data/lib/mamiya/master/agent_monitor.rb +146 -0
  28. data/lib/mamiya/master/agent_monitor_handlers.rb +44 -0
  29. data/lib/mamiya/master/web.rb +148 -0
  30. data/lib/mamiya/package.rb +122 -0
  31. data/lib/mamiya/script.rb +117 -0
  32. data/lib/mamiya/steps/abstract.rb +19 -0
  33. data/lib/mamiya/steps/build.rb +72 -0
  34. data/lib/mamiya/steps/extract.rb +26 -0
  35. data/lib/mamiya/steps/fetch.rb +24 -0
  36. data/lib/mamiya/steps/push.rb +34 -0
  37. data/lib/mamiya/storages.rb +17 -0
  38. data/lib/mamiya/storages/abstract.rb +48 -0
  39. data/lib/mamiya/storages/mock.rb +61 -0
  40. data/lib/mamiya/storages/s3.rb +127 -0
  41. data/lib/mamiya/util/label_matcher.rb +38 -0
  42. data/lib/mamiya/version.rb +3 -0
  43. data/mamiya.gemspec +35 -0
  44. data/misc/logger_test.rb +12 -0
  45. data/spec/agent/actions_spec.rb +37 -0
  46. data/spec/agent/fetcher_spec.rb +199 -0
  47. data/spec/agent/handlers/fetch_spec.rb +121 -0
  48. data/spec/agent_spec.rb +255 -0
  49. data/spec/config_spec.rb +50 -0
  50. data/spec/dsl_spec.rb +291 -0
  51. data/spec/fixtures/dsl_test_load.rb +1 -0
  52. data/spec/fixtures/dsl_test_use.rb +1 -0
  53. data/spec/fixtures/helpers/foo.rb +1 -0
  54. data/spec/fixtures/test-package-source/.mamiya.meta.json +1 -0
  55. data/spec/fixtures/test-package-source/greeting +1 -0
  56. data/spec/fixtures/test-package.tar.gz +0 -0
  57. data/spec/fixtures/test.yml +4 -0
  58. data/spec/logger_spec.rb +68 -0
  59. data/spec/master/agent_monitor_spec.rb +269 -0
  60. data/spec/master/web_spec.rb +121 -0
  61. data/spec/master_spec.rb +94 -0
  62. data/spec/package_spec.rb +394 -0
  63. data/spec/script_spec.rb +78 -0
  64. data/spec/spec_helper.rb +38 -0
  65. data/spec/steps/build_spec.rb +261 -0
  66. data/spec/steps/extract_spec.rb +68 -0
  67. data/spec/steps/fetch_spec.rb +96 -0
  68. data/spec/steps/push_spec.rb +73 -0
  69. data/spec/storages/abstract_spec.rb +22 -0
  70. data/spec/storages/s3_spec.rb +342 -0
  71. data/spec/storages_spec.rb +33 -0
  72. data/spec/support/dummy_serf.rb +70 -0
  73. data/spec/util/label_matcher_spec.rb +85 -0
  74. metadata +272 -0
@@ -0,0 +1,3 @@
1
+ module Mamiya
2
+ VERSION = "0.0.1.alpha2"
3
+ end
data/mamiya.gemspec ADDED
@@ -0,0 +1,35 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'mamiya/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "mamiya"
8
+ spec.version = Mamiya::VERSION
9
+ spec.authors = ["Shota Fukumori (sora_h)"]
10
+ spec.email = ["her@sorah.jp"]
11
+ spec.summary = %q{Fast deploy tool using tarballs and serf}
12
+ spec.description = %q{Deploy tool using tarballs and serf for lot of servers}
13
+ spec.homepage = "https://github.com/sorah/mamiya"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_runtime_dependency "thor", "~> 0.18.1"
22
+ spec.add_runtime_dependency "aws-sdk-core", "2.0.0.rc6"
23
+ spec.add_runtime_dependency "term-ansicolor", ">= 1.3.0"
24
+ unless ENV["MAMIYA_VILLEIN_PATH"]
25
+ spec.add_runtime_dependency "villein", ">= 0.3.1"
26
+ end
27
+
28
+ spec.add_runtime_dependency "sinatra", ">= 1.4.5"
29
+
30
+ spec.add_development_dependency "rspec", "2.14.1"
31
+ spec.add_development_dependency 'rack-test', '~> 0.6.2'
32
+
33
+ spec.add_development_dependency "bundler", "~> 1.5"
34
+ spec.add_development_dependency "rake"
35
+ end
@@ -0,0 +1,12 @@
1
+ require 'bundler/setup'
2
+ $:.unshift File.join(__dir__, '..' ,'lib')
3
+ require 'mamiya/logger'
4
+
5
+ [true,false].each {|color|
6
+ l = Mamiya::Logger.new(level: Logger::DEBUG, color: color)
7
+ [nil, "app"].each {|n|
8
+ (0..5).each {|s|
9
+ l.log s, "hi", n
10
+ }
11
+ }
12
+ }
@@ -0,0 +1,37 @@
1
+ require 'spec_helper'
2
+
3
+ require 'json'
4
+ require 'villein/event'
5
+
6
+ require 'mamiya/agent'
7
+ require 'mamiya/agent/actions'
8
+
9
+ require_relative '../support/dummy_serf.rb'
10
+
11
+ describe Mamiya::Agent::Actions do
12
+ let(:serf) { DummySerf.new }
13
+ let(:fetcher) { double('fetcher', start!: nil) }
14
+
15
+ let(:config) do
16
+ {serf: {agent: {rpc_addr: '127.0.0.1:17373', bind: '127.0.0.1:17946'}}}
17
+ end
18
+
19
+ before do
20
+ allow(Villein::Agent).to receive(:new).and_return(serf)
21
+ allow(Mamiya::Agent::Fetcher).to receive(:new).and_return(fetcher)
22
+ end
23
+
24
+ subject(:agent) { Mamiya::Agent.new(config) }
25
+
26
+
27
+ describe "#distribute" do
28
+ it "sends fetch request" do
29
+ expect(serf).to receive(:event).with(
30
+ 'mamiya:fetch',
31
+ {application: 'app', package: 'pkg'}.to_json
32
+ )
33
+
34
+ agent.distribute('app', 'pkg')
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,199 @@
1
+ require 'spec_helper'
2
+ require 'pathname'
3
+ require 'tmpdir'
4
+ require 'fileutils'
5
+
6
+ require 'mamiya/agent/fetcher'
7
+ require 'mamiya/steps/fetch'
8
+
9
+ describe Mamiya::Agent::Fetcher do
10
+ let!(:tmpdir) { Dir.mktmpdir('mamiya-agent-fetcher-spec') }
11
+ after { FileUtils.remove_entry_secure(tmpdir) if File.exist?(tmpdir) }
12
+
13
+ let(:config) do
14
+ {packages_dir: tmpdir, keep_packages: 2}
15
+ end
16
+
17
+ subject(:fetcher) { described_class.new(config) }
18
+
19
+ describe "lifecycle" do
20
+ it "can start and stop" do
21
+ expect(fetcher.thread).to be_nil
22
+ expect(fetcher).not_to be_running
23
+
24
+ fetcher.start!
25
+
26
+ expect(fetcher).to be_running
27
+ expect(fetcher.thread).to be_a(Thread)
28
+ expect(fetcher.thread).to be_alive
29
+ th = fetcher.thread
30
+
31
+ fetcher.stop!
32
+
33
+ 10.times { break unless th.alive?; sleep 0.1 }
34
+ expect(th).not_to be_alive
35
+
36
+ expect(fetcher.thread).to be_nil
37
+ expect(fetcher).not_to be_running
38
+ end
39
+
40
+ it "can graceful stop"
41
+ end
42
+
43
+ describe "#cleanup" do
44
+ before do
45
+ path = Pathname.new(tmpdir)
46
+
47
+ path.join('a').mkdir
48
+ File.write path.join('a', "a.tar.gz"), "\n"
49
+ File.write path.join('a', "a.json"), "\n"
50
+ File.write path.join('a', "b.json"), "\n"
51
+ File.write path.join('a', "b.tar.gz"), "\n"
52
+ File.write path.join('a', "c.json"), "\n"
53
+ File.write path.join('a', "c.tar.gz"), "\n"
54
+ path.join('b').mkdir
55
+ File.write path.join('b', "a.tar.gz"), "\n"
56
+ File.write path.join('b', "a.json"), "\n"
57
+
58
+ path.join('c').mkdir
59
+ File.write path.join('c', "a.tar.gz"), "\n"
60
+ File.write path.join('c', "b.json"), "\n"
61
+ end
62
+
63
+ it "cleans up" do
64
+ called = []
65
+ fetcher.cleanup_hook = proc { |a,b| called << [a,b] }
66
+
67
+ fetcher.cleanup
68
+
69
+ path = Pathname.new(tmpdir)
70
+ existences = Hash[
71
+ [
72
+ path.join('a', 'a.tar.gz'),
73
+ path.join('a', 'a.json'),
74
+ path.join('a', 'b.tar.gz'),
75
+ path.join('a', 'b.json'),
76
+ path.join('a', 'c.tar.gz'),
77
+ path.join('a', 'c.json'),
78
+ ].map { |file|
79
+ [file, file.exist?]
80
+ }
81
+ ]
82
+
83
+ expect(called).to eq([['a', 'a']])
84
+ expect(existences).to eq(
85
+ path.join('a', 'a.tar.gz') => false,
86
+ path.join('a', 'a.json') => false,
87
+ path.join('a', 'b.tar.gz') => true,
88
+ path.join('a', 'b.json') => true,
89
+ path.join('a', 'c.tar.gz') => true,
90
+ path.join('a', 'c.json') => true,
91
+ )
92
+ end
93
+ end
94
+
95
+ describe "mainloop" do
96
+ before do
97
+ allow(step).to receive(:run!)
98
+ allow(Mamiya::Steps::Fetch).to receive(:new).with(
99
+ application: 'myapp',
100
+ package: 'package',
101
+ destination: File.join(tmpdir, 'myapp'),
102
+ config: config,
103
+ ).and_return(step)
104
+
105
+ fetcher.start!
106
+ end
107
+
108
+ let(:step) { double('fetch-step') }
109
+
110
+ it "starts fetch step for each order" do
111
+ flag = false
112
+
113
+ expect(step).to receive(:run!) do
114
+ flag = true
115
+ end
116
+
117
+ fetcher.enqueue('myapp', 'package')
118
+ fetcher.stop!(:graceful)
119
+ end
120
+
121
+ it "calls callback" do
122
+ received = true
123
+
124
+ fetcher.enqueue('myapp', 'package') do |succeeded|
125
+ received = succeeded
126
+ end
127
+
128
+ fetcher.stop!(:graceful)
129
+
130
+ expect(received).to be_nil
131
+ end
132
+
133
+ it "calls cleanup" do
134
+ expect(fetcher).to receive(:cleanup)
135
+ fetcher.enqueue('myapp', 'package')
136
+ fetcher.stop!(:graceful)
137
+ end
138
+
139
+ it "claims itself as working" do
140
+ expect(fetcher.working?).to be_false
141
+ expect(fetcher.current_job).to be_nil
142
+
143
+ received = false
144
+ fetcher.enqueue('myapp', 'package') do |error|
145
+ received = true
146
+ expect(fetcher.working?).to be_true
147
+ expect(fetcher.current_job).to eq %w(myapp package)
148
+ end
149
+
150
+ fetcher.stop!(:graceful)
151
+ expect(received).to be_true
152
+ expect(fetcher.working?).to be_false
153
+ expect(fetcher.current_job).to be_nil
154
+ end
155
+
156
+ context "with before hook" do
157
+ it "calls callback" do
158
+ run = false
159
+ received = false
160
+
161
+ allow(step).to receive(:run!) do
162
+ run = true
163
+ end
164
+
165
+ fetcher.enqueue('myapp', 'package', before: proc {
166
+ received = true
167
+ expect(run).to be_false
168
+ })
169
+ fetcher.stop!(:graceful)
170
+
171
+ expect(received).to be_true
172
+ end
173
+ end
174
+
175
+ context "when fetch step raised error" do
176
+ let(:exception) { Exception.new("he he...") }
177
+
178
+ before do
179
+ allow(step).to receive(:run!).and_raise(exception)
180
+ end
181
+
182
+ it "calls callback with error" do
183
+ received = nil
184
+
185
+ fetcher.enqueue('myapp', 'package') do |error|
186
+ received = error
187
+ end
188
+
189
+ fetcher.stop!(:graceful)
190
+
191
+ expect(received).to eq exception
192
+ end
193
+ end
194
+
195
+ after do
196
+ fetcher.stop!
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,121 @@
1
+ require 'spec_helper'
2
+
3
+ require 'json'
4
+ require 'villein/event'
5
+
6
+ require 'mamiya/agent/handlers/fetch'
7
+
8
+ describe Mamiya::Agent::Handlers::Fetch do
9
+ let(:event) do
10
+ Villein::Event.new(
11
+ {
12
+ 'SERF_EVENT' => 'user',
13
+ 'SERF_USER_EVENT' => 'mamiya:fetch',
14
+ },
15
+ payload: {
16
+ application: 'app',
17
+ package: 'package',
18
+ }.to_json,
19
+ )
20
+ end
21
+
22
+ let(:fetcher) { double('fetcher', start!: nil) }
23
+ let(:serf) { double('serf', name: 'myname', event: nil) }
24
+
25
+ let(:agent) do
26
+ double('agent', fetcher: fetcher, serf: serf, update_tags!: nil)
27
+ end
28
+
29
+ subject(:handler) { described_class.new(agent, event) }
30
+
31
+ before do
32
+ allow(fetcher).to receive(:queue_size).and_return(0)
33
+ allow(fetcher).to receive(:enqueue) do |&block|
34
+ block.call
35
+ end
36
+ end
37
+
38
+ it "enqueue a request" do
39
+ expect(fetcher).to receive(:enqueue).with('app', 'package', kind_of(Hash))
40
+
41
+ handler.run!
42
+ end
43
+
44
+ it "responds ack" do
45
+ allow(fetcher).to receive(:enqueue).with('app', 'package')
46
+ expect(serf).to receive(:event).with('mamiya:fetch-result:ack',
47
+ {name: serf.name, application: 'app', package: 'package', pending: 1}.to_json)
48
+
49
+ handler.run!
50
+ end
51
+
52
+ it "kicks update_tag! on start" do
53
+ allow(fetcher).to receive(:enqueue) do |app, package, options, &block|
54
+ options[:before].call
55
+ end
56
+ expect(agent).to receive(:update_tags!)
57
+ handler.run!
58
+ end
59
+
60
+ it "reports start" do
61
+ allow(fetcher).to receive(:enqueue) do |app, package, options, &block|
62
+ options[:before].call
63
+ end
64
+ expect(serf).to receive(:event).with('mamiya:fetch-result:start',
65
+ {name: serf.name, application: 'app', package: 'package', pending: 1}.to_json)
66
+
67
+ handler.run!
68
+ end
69
+
70
+ it "responds success" do
71
+ callback = nil
72
+ allow(fetcher).to receive(:enqueue).with('app', 'package', kind_of(Hash)) do |&block|
73
+ callback = block
74
+ end
75
+
76
+ handler.run!
77
+
78
+ expect(serf).to receive(:event).with(
79
+ 'mamiya:fetch-result:success',
80
+ {name: serf.name, application: 'app', package: 'package', pending: 0}.to_json
81
+ )
82
+
83
+ callback.call
84
+ end
85
+
86
+ it "updates tag" do
87
+ expect(agent).to receive(:update_tags!)
88
+ handler.run!
89
+ end
90
+
91
+ context "when failed" do
92
+ it "notifies error" do
93
+ callback = nil
94
+ allow(fetcher).to receive(:enqueue).with('app', 'package', kind_of(Hash)) do |&block|
95
+ callback = block
96
+ end
97
+
98
+ handler.run!
99
+
100
+ error = RuntimeError.new('test')
101
+ expect(serf).to receive(:event).with(
102
+ 'mamiya:fetch-result:error',
103
+ {
104
+ name: serf.name, application: 'app', package: 'package',
105
+ error: error.inspect, pending: 0,
106
+ }.to_json,
107
+ )
108
+
109
+ callback.call(error)
110
+ end
111
+
112
+ it "updates tag" do
113
+ allow(fetcher).to receive(:enqueue) do |&block|
114
+ block.call(Exception.new)
115
+ end
116
+
117
+ expect(agent).to receive(:update_tags!)
118
+ handler.run!
119
+ end
120
+ end
121
+ end
@@ -0,0 +1,255 @@
1
+ require 'spec_helper'
2
+
3
+ require 'tmpdir'
4
+ require 'fileutils'
5
+ require 'json'
6
+ require 'villein/event'
7
+
8
+ require 'mamiya/agent'
9
+ require 'mamiya/agent/fetcher'
10
+ require 'mamiya/agent/actions'
11
+
12
+ require_relative './support/dummy_serf.rb'
13
+
14
+ describe Mamiya::Agent do
15
+
16
+ let(:serf) { DummySerf.new }
17
+ let(:fetcher) do
18
+ double('fetcher', start!: nil, working?: false).tap do |f|
19
+ cleanup_hook = nil
20
+ allow(f).to receive(:cleanup_hook=) { |_| cleanup_hook = _ }
21
+ allow(f).to receive(:cleanup_hook) { cleanup_hook }
22
+ end
23
+ end
24
+
25
+ let(:config) do
26
+ {serf: {agent: {rpc_addr: '127.0.0.1:17373', bind: '127.0.0.1:17946'}}}
27
+ end
28
+
29
+ before do
30
+ allow(Villein::Agent).to receive(:new).and_return(serf)
31
+ allow(Mamiya::Agent::Fetcher).to receive(:new).and_return(fetcher)
32
+ end
33
+
34
+ subject(:agent) { described_class.new(config) }
35
+
36
+ it "includes actions" do
37
+ expect(described_class.ancestors).to include(Mamiya::Agent::Actions)
38
+ end
39
+
40
+ describe "fetcher" do
41
+ it "sends events on cleanup hook" do
42
+ expect(serf).to receive(:event).with(
43
+ 'mamiya:fetch-result:remove',
44
+ {
45
+ name: serf.name, application: 'foo', package: 'bar',
46
+ }.to_json,
47
+ )
48
+
49
+ agent.fetcher.cleanup_hook.call('foo', 'bar')
50
+ end
51
+ end
52
+
53
+ describe "#run!" do
54
+ it "starts serf and fetcher" do
55
+ begin
56
+ flag = false
57
+
58
+ expect(fetcher).to receive(:start!)
59
+ expect(serf).to receive(:start!)
60
+ expect(serf).to receive(:auto_stop) do
61
+ flag = true
62
+ end
63
+
64
+ th = Thread.new { agent.run! }
65
+
66
+ 10.times { break if flag; sleep 0.1 }
67
+ ensure
68
+ th.kill if th && th.alive?
69
+ end
70
+ end
71
+ end
72
+
73
+ describe "#update_tags!" do
74
+ describe "(status)" do
75
+ context "when it is not busy" do
76
+ it "shows ready" do
77
+ agent.update_tags!
78
+
79
+ expect(serf.tags['mamiya']).to eq ',ready,'
80
+ end
81
+ end
82
+
83
+ context "when it is fetching" do
84
+ before do
85
+ allow(fetcher).to receive(:working?).and_return(true)
86
+ end
87
+
88
+ it "shows fetching" do
89
+ agent.update_tags!
90
+
91
+ expect(serf.tags['mamiya']).to eq ',fetching,'
92
+ end
93
+ end
94
+
95
+ context "when it is running multiple jobs" do
96
+ pending
97
+ end
98
+ end
99
+
100
+ describe "(prepared)" do
101
+ pending
102
+ end
103
+
104
+ describe "(current)" do
105
+ pending
106
+ end
107
+ end
108
+
109
+ describe "#status" do
110
+ before do
111
+ allow(agent).to receive(:existing_packages).and_return("app" => ["pkg"])
112
+ allow(fetcher).to receive(:queue_size).and_return(42)
113
+ allow(fetcher).to receive(:working?).and_return(false)
114
+ allow(fetcher).to receive(:current_job).and_return(nil)
115
+ end
116
+
117
+ subject(:status) { agent.status }
118
+
119
+ it "includes agent name" do
120
+ expect(status[:name]).to eq serf.name
121
+ end
122
+
123
+ it "includes packages" do
124
+ expect(status[:packages]).to eq agent.existing_packages
125
+ end
126
+
127
+ describe "(fetcher)" do
128
+ it "includes queue_size as pending" do
129
+ expect(status[:fetcher][:pending]).to eq 42
130
+ end
131
+
132
+ it "shows fetching status" do
133
+ expect(status[:fetcher][:fetching]).to be_nil
134
+ end
135
+
136
+ context "when it's fetching" do
137
+ before do
138
+ allow(fetcher).to receive(:working?).and_return(true)
139
+ allow(fetcher).to receive(:current_job).and_return(%w(foo bar))
140
+ end
141
+
142
+ it "shows fetching true" do
143
+ expect(status[:fetcher][:fetching]).to eq ['foo', 'bar']
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ describe "#existing_packages" do
150
+ let!(:packages_dir) { Dir.mktmpdir('mamiya-agent-spec') }
151
+ after { FileUtils.remove_entry_secure(packages_dir) }
152
+
153
+ let(:config) { {packages_dir: packages_dir} }
154
+
155
+ subject(:existing_packages) { agent.existing_packages }
156
+
157
+ before do
158
+ dir = Pathname.new(packages_dir)
159
+ %w(a b).each do |app|
160
+ dir.join(app).mkdir
161
+ %w(valid.tar.gz valid.json
162
+ valid-2.tar.gz valid-2.json
163
+ invalid-1.tar.gz invalid-2.json invalid-3.txt).each do |name|
164
+ File.write dir.join(app, name), "\n"
165
+ end
166
+ end
167
+ end
168
+
169
+ it "returns valid packages" do
170
+ expect(existing_packages).to eq(
171
+ "a" => ["valid", "valid-2"],
172
+ "b" => ["valid", "valid-2"],
173
+ )
174
+ end
175
+ end
176
+
177
+ describe "query responder" do
178
+ it "responds to 'mamiya:status'" do
179
+ allow(agent).to receive(:status).and_return("my" => "status")
180
+
181
+ response = serf.trigger_query('mamiya:status', '')
182
+ expect(JSON.parse(response)).to eq("my" => "status")
183
+ end
184
+ end
185
+
186
+ describe "event handler" do
187
+ let(:handler_class) do
188
+ Class.new(Mamiya::Agent::Handlers::Abstract) do
189
+ end
190
+ end
191
+
192
+ def trigger(name, payload={})
193
+ serf.trigger('user_event', Villein::Event.new(
194
+ {
195
+ 'SERF_EVENT' => 'user',
196
+ 'SERF_USER_EVENT' => "mamiya:#{name}",
197
+ },
198
+ payload: payload.to_json,
199
+ ))
200
+ end
201
+
202
+ before do
203
+ stub_const("Mamiya::Agent::Handlers::Test", handler_class)
204
+ agent # to create
205
+ end
206
+
207
+ it "finds handler class then call #run!" do
208
+ expect_any_instance_of(handler_class).to receive(:run!)
209
+
210
+ trigger('test')
211
+ end
212
+
213
+ it "passes proper argument to handler"
214
+
215
+ context "when handler not found" do
216
+ it "ignores event"
217
+ end
218
+
219
+ context "with events_only" do
220
+ subject(:agent) { described_class.new(config, events_only: [/foo/]) }
221
+
222
+ let(:handler_foo) do
223
+ Class.new(Mamiya::Agent::Handlers::Abstract) do
224
+ end
225
+ end
226
+
227
+ let(:handler_bar) do
228
+ Class.new(Mamiya::Agent::Handlers::Abstract) do
229
+ def run!
230
+ raise 'oops?'
231
+ end
232
+ end
233
+ end
234
+
235
+ before do
236
+ stub_const("Mamiya::Agent::Handlers::Foo", handler_foo)
237
+ stub_const("Mamiya::Agent::Handlers::Bar", handler_bar)
238
+ end
239
+
240
+ it "handles events only matches any of them" do
241
+ expect_any_instance_of(handler_foo).to receive(:run!)
242
+
243
+ trigger('foo')
244
+ trigger('bar')
245
+ end
246
+ end
247
+
248
+ context "with action" do
249
+ it "calls another method instead of run!" do
250
+ expect_any_instance_of(handler_class).to receive(:hello)
251
+ trigger('test:hello')
252
+ end
253
+ end
254
+ end
255
+ end