mamiya 0.0.1.alpha2

Sign up to get free protection for your applications and to get access to all the features.
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,50 @@
1
+ require 'spec_helper'
2
+ require 'mamiya/storages'
3
+
4
+ require 'mamiya/config'
5
+
6
+ describe Mamiya::Config do
7
+ let(:source) do
8
+ {
9
+ "test" => {
10
+ "a" => ["b" => {"c" => {"d" => "e"}}]
11
+ },
12
+ :test2 => :hello
13
+ }
14
+ end
15
+ subject(:config) { described_class.new(source) }
16
+
17
+ describe ".load" do
18
+ let(:fixture_path) { File.join(__dir__, 'fixtures', 'test.yml') }
19
+
20
+ subject(:config) { described_class.load(fixture_path) }
21
+
22
+ it { should be_a(described_class) }
23
+
24
+ it "loads configuration from file" do
25
+ expect(config[:a][:e]).to eq "f"
26
+ end
27
+ end
28
+
29
+ it "symbolizes keys" do
30
+ expect(config[:test][:a][0][:b][:c][:d]).to eq "e"
31
+ expect(config[:test2]).to eq :hello
32
+ end
33
+
34
+ describe "#storage_class" do
35
+ let(:source) do
36
+ {storage: {type: :foobar, conf: :iguration}}
37
+ end
38
+ let(:klass) { Class.new }
39
+
40
+ before do
41
+ allow(Mamiya::Storages).to receive(:find).with(:foobar).and_return(klass)
42
+ end
43
+
44
+ subject(:storage_class) { config.storage_class }
45
+
46
+ it "finds class using Storages.find" do
47
+ expect(storage_class).to eq klass
48
+ end
49
+ end
50
+ end
data/spec/dsl_spec.rb ADDED
@@ -0,0 +1,291 @@
1
+ require 'spec_helper'
2
+ require 'mamiya/dsl'
3
+ require 'mamiya/util/label_matcher'
4
+
5
+ describe Mamiya::DSL do
6
+ let(:klass) { Class.new(Mamiya::DSL) }
7
+ subject(:dsl) { klass.new }
8
+
9
+ describe ".set_default" do
10
+ it "sets default" do
11
+ expect { dsl.testvar }.to raise_error
12
+ klass.set_default :testvar, 1
13
+ expect(dsl.testvar).to eq 1
14
+ end
15
+ end
16
+
17
+ describe ".add_hook" do
18
+ it "adds hook" do
19
+ expect { dsl.testhook }.to raise_error
20
+ klass.add_hook :testhook
21
+ expect(dsl.testhook).to be_a(Proc)
22
+ end
23
+ end
24
+
25
+ describe "#evaluate!" do
26
+ context "with block" do
27
+ it "evaluates block within its object" do
28
+ flag = false
29
+ expect {
30
+ dsl.evaluate! { flag = true }
31
+ }.to change { flag }.from(false).to(true)
32
+ end
33
+ end
34
+
35
+ context "with string" do
36
+ it "evaluates string within its object" do
37
+ expect(dsl).to receive(:flag!)
38
+ dsl.evaluate! "flag!"
39
+ end
40
+ end
41
+ end
42
+
43
+ describe "#load!" do
44
+ before do
45
+ dsl.set :foo, 42
46
+ end
47
+
48
+ it "loads file" do
49
+ expect {
50
+ dsl.load! "#{__dir__}/fixtures/dsl_test_load.rb"
51
+ }.to change { dsl.foo }.from(42).to(72)
52
+ end
53
+ end
54
+
55
+ describe "#use" do
56
+ before do
57
+ dsl.set :foo, 42
58
+ end
59
+
60
+ it "loads file" do
61
+ dsl.set :load_path, ["#{__dir__}/fixtures/helpers"]
62
+ expect {
63
+ dsl.use :foo
64
+ }.to change { dsl.foo }.from(42).to(72)
65
+ end
66
+
67
+ context "when file not exists" do
68
+ it "raises error" do
69
+ expect {
70
+ dsl.use :blahblahblah
71
+ }.to raise_error(Mamiya::DSL::HelperNotFound)
72
+ end
73
+ end
74
+
75
+ context "with options" do
76
+ it "passes to file" do
77
+ dsl.set :load_path, ["#{__dir__}/fixtures/helpers"]
78
+ expect {
79
+ dsl.use :foo, value: 100
80
+ }.to change { dsl.foo }.from(42).to(100)
81
+ end
82
+ end
83
+
84
+ context "when loading file" do
85
+ it "loads file with suitable load path" do
86
+ expect {
87
+ dsl.load! "#{__dir__}/fixtures/dsl_test_use.rb"
88
+ }.to change { dsl.foo }.from(42).to(72)
89
+ end
90
+ end
91
+ end
92
+
93
+ describe "#set" do
94
+ it "sets variable" do
95
+ expect{ dsl.foo }.to raise_error
96
+ dsl.set :foo, 100
97
+ expect(dsl.foo).to eq 100
98
+ dsl.set :foo, 200
99
+ expect(dsl.foo).to eq 200
100
+ end
101
+ end
102
+
103
+ describe "#set_default" do
104
+ it "sets variable" do
105
+ dsl.set_default :foo, 100
106
+ expect(dsl.foo).to eq 100
107
+ end
108
+
109
+ context "when already assigned" do
110
+ it "doesn't nothing" do
111
+ dsl.set :foo, 100
112
+ dsl.set_default :foo, 200
113
+ expect(dsl.foo).to eq 100
114
+ end
115
+ end
116
+ end
117
+
118
+ describe "tasks" do
119
+ it "defines callable tasks" do
120
+ flag = false
121
+ dsl.task :test do
122
+ flag = true
123
+ end
124
+
125
+ expect {
126
+ dsl.invoke :test
127
+ }.to change { flag }.from(false).to(true)
128
+ end
129
+
130
+ it "can be called from another tasks" do
131
+ flag = false
132
+
133
+ dsl.task :a do
134
+ invoke :b
135
+ end
136
+
137
+ dsl.task :b do
138
+ flag = true
139
+ end
140
+
141
+ expect {
142
+ dsl.invoke :a
143
+ }.to change { flag }.from(false).to(true)
144
+ end
145
+
146
+ context "when task doesn't exist" do
147
+ it "raises error" do
148
+ expect {
149
+ dsl.invoke :nul
150
+ }.to raise_error
151
+ end
152
+ end
153
+ end
154
+
155
+ describe "hook method" do
156
+ before do
157
+ klass.add_hook(:testhook)
158
+ end
159
+
160
+ context "without block" do
161
+ it "returns Proc to run hooks" do
162
+ expect(dsl.testhook).to be_a_kind_of(Proc)
163
+ end
164
+ end
165
+
166
+ context "with block" do
167
+ it "appends given block to hooks" do
168
+ flag = false
169
+ dsl.testhook { flag = true }
170
+ expect { dsl.testhook.call }.to \
171
+ change { flag }.from(false).to(true)
172
+ end
173
+
174
+ it "appends given block to hooks (multiple)" do
175
+ flags = []
176
+ dsl.testhook { flags << :a }
177
+ dsl.testhook { flags << :b }
178
+
179
+ expect { dsl.testhook.call }.to \
180
+ change { flags }.from([]).to([:a,:b])
181
+ end
182
+
183
+ it "can call hooks with argument" do
184
+ flag = nil
185
+ dsl.testhook { |a| flag = a }
186
+
187
+ expect { dsl.testhook.call(42) }.to \
188
+ change { flag }.from(nil).to(42)
189
+ end
190
+
191
+ context "with :prepend" do
192
+ it "prepends given block to hooks" do
193
+ flag = nil
194
+ dsl.testhook { |a| flag = 1 }
195
+ dsl.testhook(:prepend) { flag = 0 }
196
+
197
+ expect { dsl.testhook.call }.to \
198
+ change { flag }.from(nil).to(1)
199
+ end
200
+ end
201
+
202
+ context "with :overwrite" do
203
+ it "overwrites all existing hooks with given block" do
204
+ flags = []
205
+ dsl.testhook { |a| flags << :a }
206
+ dsl.testhook { |a| flags << :b }
207
+ dsl.testhook(:overwrite) { |a| flags << :c }
208
+
209
+ expect { dsl.testhook.call }.to \
210
+ change { flags }.from([]).to([:c])
211
+ end
212
+ end
213
+ end
214
+
215
+ describe "limiting by label" do
216
+ context "using kwarg :only" do
217
+ it "runs only for specified label" do
218
+ matcher = double('matcher')
219
+ expect(Mamiya::Util::LabelMatcher::Simple).to receive(:new).with([:a,:b,:d]).and_return(matcher)
220
+ expect(matcher).to receive(:match?).with(:a).and_return(true)
221
+ expect(matcher).to receive(:match?).with([:a]).and_return(true)
222
+ expect(matcher).to receive(:match?).with([:b, :c]).and_return(false)
223
+
224
+ flags = []
225
+ dsl.testhook(only: [:a]) { flags << 1 }
226
+ dsl.testhook { flags << 2 }
227
+ dsl.testhook(only: [[:b, :c]]) { flags << 3 }
228
+ dsl.testhook(:prepend, only: [[:a]]) { flags << 4 }
229
+
230
+ expect { dsl.testhook(:a, :b, :d).call }.to \
231
+ change { flags }.from([]).to([4,1,2])
232
+ end
233
+ end
234
+
235
+ context "using kwarg :except" do
236
+ it "doesn't run for specified label" do
237
+ matcher = double('matcher')
238
+ expect(Mamiya::Util::LabelMatcher::Simple).to receive(:new).with([:a,:b,:d]).and_return(matcher)
239
+ expect(matcher).to receive(:match?).with(:a).and_return(true)
240
+ expect(matcher).to receive(:match?).with([:a]).and_return(true)
241
+ expect(matcher).to receive(:match?).with([:b, :c]).and_return(false)
242
+
243
+ flags = []
244
+ dsl.testhook(except: [:a]) { flags << 1 }
245
+ dsl.testhook { flags << 2 }
246
+ dsl.testhook(except: [[:b, :c]]) { flags << 3 }
247
+ dsl.testhook(:prepend, except: [[:a]]) { flags << 4 }
248
+
249
+ expect { dsl.testhook(:a, :b, :d).call }.to \
250
+ change { flags }.from([]).to([2,3])
251
+ end
252
+ end
253
+ end
254
+
255
+ describe "chain hooks" do
256
+ before do
257
+ klass.add_hook(:testchain, chain: true)
258
+ end
259
+
260
+ it "injects the result of blocks" do
261
+ dsl.testchain { |result, arg| result += arg * 3 }
262
+ dsl.testchain { |result, arg| result -= arg }
263
+
264
+ expect(dsl.testchain[2, 5]).to eq 12
265
+ end
266
+ end
267
+
268
+ describe "naming" do
269
+ it "ables to name defined hooks" do
270
+ dsl.testhook('the-name') { }
271
+ expect(dsl.hooks[:testhook].first[:name]).to eq 'the-name'
272
+ end
273
+
274
+ context "with other option" do
275
+ it "ables to name defined hooks" do
276
+ dsl.testhook('the-name2', :prepend) { }
277
+ dsl.testhook('the-name', :prepend) { }
278
+ expect(dsl.hooks[:testhook].first[:name]).to eq 'the-name'
279
+ end
280
+ end
281
+ end
282
+ end
283
+
284
+ describe "#servers" do
285
+ pending "Hey!"
286
+ end
287
+
288
+ describe "#use_servers" do
289
+ pending "Hey!"
290
+ end
291
+ end
@@ -0,0 +1 @@
1
+ set :foo, 72
@@ -0,0 +1 @@
1
+ use :foo
@@ -0,0 +1 @@
1
+ set :foo, options[:value] || 72
@@ -0,0 +1 @@
1
+ hello
Binary file
@@ -0,0 +1,4 @@
1
+ a:
2
+ b:
3
+ - c: d
4
+ e: f
@@ -0,0 +1,68 @@
1
+ require 'spec_helper'
2
+ require 'stringio'
3
+ require 'tmpdir'
4
+ require 'fileutils'
5
+
6
+ require 'mamiya/logger'
7
+
8
+ describe Mamiya::Logger do
9
+ it "can log" do
10
+ sio = StringIO.new('', 'w')
11
+ logger = described_class.new(outputs: [sio])
12
+ logger.info 'hello'
13
+ logger.close
14
+
15
+ expect(sio.string).to match(/hello/)
16
+ end
17
+
18
+ context "with multiple outputs" do
19
+ let!(:tmpdir) { Dir.mktmpdir('mamiya-logger-spec') }
20
+ after { FileUtils.remove_entry_secure tmpdir }
21
+
22
+ it "can log" do
23
+ sio = StringIO.new('', 'w')
24
+ i,o = IO.pipe
25
+ path = File.join(tmpdir, 'test.log')
26
+
27
+ logger = described_class.new(outputs: [sio, o, path])
28
+ logger.info 'hello'
29
+ logger.close
30
+
31
+ io_out = i.read; i.close
32
+
33
+ expect(sio.string).to match(/hello/)
34
+ expect(io_out).to match(/hello/)
35
+ expect(File.read(path)).to match(/hello/)
36
+ end
37
+ end
38
+
39
+ describe "#[]" do
40
+ it "can log" do
41
+ sio = StringIO.new('', 'w')
42
+ logger = described_class.new(outputs: [sio])
43
+ test_logger = logger['test']
44
+ logger.info 'hello'
45
+ test_logger.info ''
46
+
47
+ expect(test_logger.progname).to eq 'test'
48
+ expect(logger.progname).to be_nil
49
+
50
+ logger.close
51
+
52
+ expect(sio.string).to match(/^.*hello.*$/)
53
+ expect(sio.string).to match(/^.*test.*$/)
54
+ end
55
+ end
56
+
57
+ describe "#reopen" do
58
+ it "reopens file IOs" do
59
+ io = double('io', path: 'foo', tty?: false, write: 0, sync: true)
60
+ expect(io).to receive(:reopen).with('foo', 'a')
61
+ expect(io).to receive(:sync=).with(true)
62
+
63
+ logger = described_class.new(outputs: [io])
64
+
65
+ logger.reopen
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,269 @@
1
+ require 'spec_helper'
2
+ require 'json'
3
+
4
+ require 'villein/event'
5
+
6
+ require 'mamiya/master/agent_monitor'
7
+
8
+ describe Mamiya::Master::AgentMonitor do
9
+ let(:serf) { double('serf') }
10
+ let(:config) { {} }
11
+ let(:master) do
12
+ double('master', logger: Mamiya::Logger.new, serf: serf, config: config)
13
+ end
14
+
15
+ subject(:agent_monitor) do
16
+ described_class.new(master)
17
+ end
18
+
19
+ describe "#refresh" do
20
+ let(:query_response) do
21
+ {
22
+ "Acks" => ['a'],
23
+ "Responses" => {
24
+ 'a' => {"foo" => "bar"}.to_json,
25
+ },
26
+ }
27
+ end
28
+
29
+ let(:members) do
30
+ [
31
+ {
32
+ "name"=>"a", "status"=>"alive",
33
+ "addr"=>"x.x.x.x:7676", "port"=>7676,
34
+ "protocol"=>{"max"=>4, "min"=>2, "version"=>4},
35
+ "tags"=>{},
36
+ },
37
+ ]
38
+ end
39
+
40
+ before do
41
+ allow(serf).to receive(:query).with('mamiya:status', '', {}).and_return(query_response)
42
+ allow(serf).to receive(:members).and_return(members)
43
+ end
44
+
45
+ it "updates #statuses" do
46
+ expect {
47
+ agent_monitor.refresh
48
+ }.to change {
49
+ agent_monitor.statuses["a"]
50
+ }.to("foo" => "bar")
51
+ end
52
+
53
+ it "updates #agents" do
54
+ expect {
55
+ agent_monitor.refresh
56
+ }.to change {
57
+ agent_monitor.agents
58
+ }.from({}).to("a" => members[0])
59
+ end
60
+
61
+ context "when some member is failing" do
62
+ let(:members) do
63
+ [
64
+ {
65
+ "name"=>"a", "status"=>"failed",
66
+ "addr"=>"x.x.x.x:7676", "port"=>7676,
67
+ "protocol"=>{"max"=>4, "min"=>2, "version"=>4},
68
+ "tags"=>{},
69
+ },
70
+ ]
71
+ end
72
+
73
+ it "appends to failed_agents" do
74
+ expect {
75
+ agent_monitor.refresh
76
+ }.to change {
77
+ agent_monitor.failed_agents
78
+ }.from([]).to(['a'])
79
+ end
80
+ end
81
+
82
+ context "when some agent returned invalid status" do
83
+ let(:query_response) do
84
+ {
85
+ "Acks" => ['a'],
86
+ "Responses" => {
87
+ 'a' => '{',
88
+ },
89
+ }
90
+ end
91
+
92
+ it "appends to failed_agents" do
93
+ expect {
94
+ agent_monitor.refresh
95
+ }.to change {
96
+ agent_monitor.failed_agents
97
+ }.from([]).to(['a'])
98
+ end
99
+ end
100
+
101
+ context "with argument" do
102
+ it "passes args to serf query" do
103
+ expect(serf).to receive(:query).with('mamiya:status', '', node: 'foo').and_return(query_response)
104
+ agent_monitor.refresh(node: 'foo')
105
+ end
106
+ end
107
+ end
108
+
109
+ describe "(commiting events)" do
110
+ let(:query_response) do
111
+ {
112
+ "Acks" => ['a'],
113
+ "Responses" => {
114
+ 'a' => status.to_json,
115
+ },
116
+ }
117
+ end
118
+
119
+ let(:members) do
120
+ [
121
+ {
122
+ "name"=>"a", "status"=>"alive",
123
+ "addr"=>"x.x.x.x:7676", "port"=>7676,
124
+ "protocol"=>{"max"=>4, "min"=>2, "version"=>4},
125
+ "tags"=>{},
126
+ },
127
+ ]
128
+ end
129
+
130
+ let(:status) do
131
+ {}
132
+ end
133
+
134
+ def commit(event, payload)
135
+ agent_monitor.commit_event(Villein::Event.new(
136
+ {
137
+ 'SERF_EVENT' => 'user',
138
+ 'SERF_USER_EVENT' => event,
139
+ },
140
+ payload: {name: "a"}.merge(payload).to_json
141
+ ))
142
+ end
143
+
144
+ before do
145
+ allow(serf).to receive(:query).with('mamiya:status', '', {}).and_return(query_response)
146
+ allow(serf).to receive(:members).and_return(members)
147
+
148
+ agent_monitor.refresh
149
+ end
150
+
151
+ subject(:new_status) { agent_monitor.statuses["a"] }
152
+
153
+ describe "fetch-result" do
154
+ describe ":ack" do
155
+ let(:status) do
156
+ {fetcher: {fetching: nil, pending: 0}}
157
+ end
158
+
159
+ it "updates pending" do
160
+ commit('mamiya:fetch-result:ack', pending: 72)
161
+ expect(new_status["fetcher"]["pending"]).to eq 72
162
+ end
163
+ end
164
+
165
+ describe ":start" do
166
+ let(:status) do
167
+ {fetcher: {fetching: nil, pending: 0}}
168
+ end
169
+
170
+ it "updates fetching" do
171
+ commit('mamiya:fetch-result:start',
172
+ application: 'app', package: 'pkg', pending: 0)
173
+ expect(new_status["fetcher"]["fetching"]).to eq ['app', 'pkg']
174
+ end
175
+ end
176
+
177
+ describe ":error" do
178
+ let(:status) do
179
+ {fetcher: {fetching: ['app', 'pkg'], pending: 0}}
180
+ end
181
+
182
+ it "updates fetching" do
183
+ commit('mamiya:fetch-result:error',
184
+ application: 'app', package: 'pkg', pending: 0)
185
+
186
+ expect(new_status["fetcher"]["fetching"]).to eq nil
187
+ end
188
+
189
+ context "when package doesn't match with present state" do
190
+ it "doesn't updates fetching" do
191
+ commit('mamiya:fetch-result:error',
192
+ application: 'app', package: 'pkg2', pending: 0)
193
+
194
+ expect(new_status["fetcher"]["fetching"]).to \
195
+ eq(['app', 'pkg'])
196
+ end
197
+ end
198
+ end
199
+
200
+ describe ":success" do
201
+ let(:status) do
202
+ {fetcher: {fetching: ['app', 'pkg'], pending: 0},
203
+ packages: {}}
204
+ end
205
+
206
+ it "updates fetching" do
207
+ commit('mamiya:fetch-result:success',
208
+ application: 'app', package: 'pkg', pending: 0)
209
+
210
+ expect(new_status["fetcher"]["fetching"]).to eq nil
211
+ end
212
+
213
+ it "updates packages" do
214
+ commit('mamiya:fetch-result:success',
215
+ application: 'app', package: 'pkg', pending: 0)
216
+
217
+ expect(new_status["packages"]["app"]).to eq ["pkg"]
218
+ end
219
+
220
+ context "with existing packages" do
221
+ let(:status) do
222
+ {fetcher: {fetching: ['app', 'pkg2'], pending: 0},
223
+ packages: {"app" => ['pkg1']}}
224
+ end
225
+
226
+ it "updates packages" do
227
+ commit('mamiya:fetch-result:success',
228
+ application: 'app', package: 'pkg2', pending: 0)
229
+
230
+ expect(new_status["packages"]["app"]).to eq %w(pkg1 pkg2)
231
+ end
232
+ end
233
+
234
+ context "when package doesn't match with present state" do
235
+ it "doesn't updates fetching" do
236
+ commit('mamiya:fetch-result:success',
237
+ application: 'app', package: 'pkg2', pending: 0)
238
+
239
+ expect(agent_monitor.statuses["a"]["fetcher"]["fetching"]).to \
240
+ eq(['app', 'pkg'])
241
+ end
242
+
243
+ it "updates packages" do
244
+ commit('mamiya:fetch-result:success',
245
+ application: 'app', package: 'pkg', pending: 0)
246
+
247
+ expect(new_status["packages"]["app"]).to eq ["pkg"]
248
+ end
249
+ end
250
+ end
251
+
252
+ describe ":remove" do
253
+ context "with existing packages" do
254
+ let(:status) do
255
+ {fetcher: {fetching: ['app', 'pkg2'], pending: 0},
256
+ packages: {"app" => ['pkg1']}}
257
+ end
258
+
259
+ it "updates packages" do
260
+ commit('mamiya:fetch-result:remove',
261
+ application: 'app', package: 'pkg1', pending: 0)
262
+
263
+ expect(new_status["packages"]["app"]).to eq []
264
+ end
265
+ end
266
+ end
267
+ end
268
+ end
269
+ end