mamiya 0.0.1.alpha21 → 0.0.1.alpha22

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/example/.gitignore +5 -0
  3. data/example/Procfile +1 -1
  4. data/example/README.md +83 -0
  5. data/example/config.agent.rb +40 -0
  6. data/example/config.rb +20 -6
  7. data/example/deploy.rb +27 -11
  8. data/example/source/README.md +1 -0
  9. data/lib/mamiya/agent/actions.rb +8 -3
  10. data/lib/mamiya/agent/task_queue.rb +9 -0
  11. data/lib/mamiya/agent/tasks/abstract.rb +13 -0
  12. data/lib/mamiya/agent/tasks/clean.rb +36 -4
  13. data/lib/mamiya/agent/tasks/fetch.rb +1 -0
  14. data/lib/mamiya/agent/tasks/notifyable.rb +0 -1
  15. data/lib/mamiya/agent/tasks/prepare.rb +103 -0
  16. data/lib/mamiya/agent.rb +46 -12
  17. data/lib/mamiya/cli/client.rb +35 -7
  18. data/lib/mamiya/cli.rb +44 -5
  19. data/lib/mamiya/configuration.rb +12 -0
  20. data/lib/mamiya/dsl.rb +6 -2
  21. data/lib/mamiya/helpers/git.rb +24 -0
  22. data/lib/mamiya/master/agent_monitor.rb +22 -2
  23. data/lib/mamiya/master/agent_monitor_handlers.rb +17 -0
  24. data/lib/mamiya/master/web.rb +42 -3
  25. data/lib/mamiya/master.rb +4 -0
  26. data/lib/mamiya/script.rb +28 -8
  27. data/lib/mamiya/steps/abstract.rb +1 -0
  28. data/lib/mamiya/steps/build.rb +107 -19
  29. data/lib/mamiya/steps/extract.rb +1 -0
  30. data/lib/mamiya/steps/prepare.rb +60 -0
  31. data/lib/mamiya/steps/switch.rb +76 -0
  32. data/lib/mamiya/storages/filesystem.rb +92 -0
  33. data/lib/mamiya/storages/mock.rb +1 -0
  34. data/lib/mamiya/util/label_matcher.rb +7 -3
  35. data/lib/mamiya/version.rb +1 -1
  36. data/mamiya.gemspec +1 -1
  37. data/spec/agent/actions_spec.rb +25 -0
  38. data/spec/agent/task_queue_spec.rb +42 -6
  39. data/spec/agent/tasks/abstract_spec.rb +35 -0
  40. data/spec/agent/tasks/clean_spec.rb +94 -45
  41. data/spec/agent/tasks/fetch_spec.rb +1 -0
  42. data/spec/agent/tasks/prepare_spec.rb +127 -0
  43. data/spec/agent_spec.rb +75 -27
  44. data/spec/dsl_spec.rb +6 -8
  45. data/spec/master/agent_monitor_spec.rb +142 -4
  46. data/spec/master/web_spec.rb +43 -1
  47. data/spec/steps/build_spec.rb +101 -0
  48. data/spec/steps/prepare_spec.rb +125 -0
  49. data/spec/steps/switch_spec.rb +146 -0
  50. data/spec/storages/filesystem_spec.rb +305 -0
  51. data/spec/util/label_matcher_spec.rb +32 -0
  52. metadata +20 -6
  53. data/config.example.yml +0 -11
  54. data/example.rb +0 -74
@@ -8,11 +8,18 @@ describe Mamiya::Agent::Tasks::Clean do
8
8
  let!(:tmpdir) { Dir.mktmpdir('mamiya-agent-tasks-clean-spec') }
9
9
  after { FileUtils.remove_entry_secure(tmpdir) if File.exist?(tmpdir) }
10
10
 
11
- let(:config) { {packages_dir: tmpdir, keep_packages: 2} }
11
+ let(:packages_dir) { Pathname.new(tmpdir).join('packages').tap(&:mkdir) }
12
+ let(:prereleases_dir) { Pathname.new(tmpdir).join('prereleases').tap(&:mkdir) }
13
+
14
+ let(:config) do
15
+ {packages_dir: packages_dir, keep_packages: 2,
16
+ prereleases_dir: prereleases_dir, keep_prereleases: 2,}
17
+ end
12
18
 
13
19
  let(:agent) { double('agent', config: config) }
14
20
  let(:task_queue) { double('task_queue') }
15
21
 
22
+
16
23
  subject(:task) { described_class.new(task_queue, {}, agent: agent, raise_error: true) }
17
24
 
18
25
  it 'inherits abstract task' do
@@ -21,52 +28,94 @@ describe Mamiya::Agent::Tasks::Clean do
21
28
 
22
29
 
23
30
  describe "#execute" do
24
- before do
25
- path = Pathname.new(tmpdir)
26
-
27
- path.join('a').mkdir
28
- File.write path.join('a', "a.tar.gz"), "\n"
29
- File.write path.join('a', "a.json"), "\n"
30
- File.write path.join('a', "b.json"), "\n"
31
- File.write path.join('a', "b.tar.gz"), "\n"
32
- File.write path.join('a', "c.json"), "\n"
33
- File.write path.join('a', "c.tar.gz"), "\n"
34
- path.join('b').mkdir
35
- File.write path.join('b', "a.tar.gz"), "\n"
36
- File.write path.join('b', "a.json"), "\n"
37
-
38
- path.join('c').mkdir
39
- File.write path.join('c', "a.tar.gz"), "\n"
40
- File.write path.join('c', "b.json"), "\n"
31
+ describe "packages" do
32
+ before do
33
+ path = packages_dir
34
+
35
+ path.join('a').mkdir
36
+ File.write path.join('a', "a.tar.gz"), "\n"
37
+ File.write path.join('a', "a.json"), "\n"
38
+ File.write path.join('a', "b.json"), "\n"
39
+ File.write path.join('a', "b.tar.gz"), "\n"
40
+ File.write path.join('a', "c.json"), "\n"
41
+ File.write path.join('a', "c.tar.gz"), "\n"
42
+ path.join('b').mkdir
43
+ File.write path.join('b', "a.tar.gz"), "\n"
44
+ File.write path.join('b', "a.json"), "\n"
45
+
46
+ path.join('c').mkdir
47
+ File.write path.join('c', "a.tar.gz"), "\n"
48
+ File.write path.join('c', "b.json"), "\n"
49
+ end
50
+
51
+ it "cleans up" do
52
+ expect(agent).to receive(:trigger).with('pkg', action: 'remove', application: 'a', package: 'a', coalesce: false)
53
+
54
+ task.execute
55
+
56
+ existences = Hash[
57
+ [
58
+ packages_dir.join('a', 'a.tar.gz'),
59
+ packages_dir.join('a', 'a.json'),
60
+ packages_dir.join('a', 'b.tar.gz'),
61
+ packages_dir.join('a', 'b.json'),
62
+ packages_dir.join('a', 'c.tar.gz'),
63
+ packages_dir.join('a', 'c.json'),
64
+ ].map { |file|
65
+ [file, file.exist?]
66
+ }
67
+ ]
68
+
69
+ expect(existences).to eq(
70
+ packages_dir.join('a', 'a.tar.gz') => false,
71
+ packages_dir.join('a', 'a.json') => false,
72
+ packages_dir.join('a', 'b.tar.gz') => true,
73
+ packages_dir.join('a', 'b.json') => true,
74
+ packages_dir.join('a', 'c.tar.gz') => true,
75
+ packages_dir.join('a', 'c.json') => true,
76
+ )
77
+ end
41
78
  end
42
79
 
43
- it "cleans up" do
44
- expect(agent).to receive(:trigger).with('pkg', action: 'remove', application: 'a', package: 'a', coalesce: false)
45
-
46
- task.execute
47
-
48
- path = Pathname.new(tmpdir)
49
- existences = Hash[
50
- [
51
- path.join('a', 'a.tar.gz'),
52
- path.join('a', 'a.json'),
53
- path.join('a', 'b.tar.gz'),
54
- path.join('a', 'b.json'),
55
- path.join('a', 'c.tar.gz'),
56
- path.join('a', 'c.json'),
57
- ].map { |file|
58
- [file, file.exist?]
59
- }
60
- ]
61
-
62
- expect(existences).to eq(
63
- path.join('a', 'a.tar.gz') => false,
64
- path.join('a', 'a.json') => false,
65
- path.join('a', 'b.tar.gz') => true,
66
- path.join('a', 'b.json') => true,
67
- path.join('a', 'c.tar.gz') => true,
68
- path.join('a', 'c.json') => true,
69
- )
80
+ describe "prereleases_dir" do
81
+ before do
82
+ path = prereleases_dir
83
+
84
+ # TODO: XXX: this may remove ongoing preparing somewhat
85
+ path.join('a').mkdir
86
+ path.join('a', '1').mkdir
87
+ path.join('a', '2').mkdir
88
+ path.join('a', '3').mkdir
89
+ path.join('b').mkdir
90
+ path.join('b', '1').mkdir
91
+ path.join('b', '2').mkdir
92
+ end
93
+
94
+ it "cleans up" do
95
+ expect(agent).to receive(:trigger).with('prerelease', action: 'remove', app: 'a', pkg: '1', coalesce: false)
96
+
97
+ task.execute
98
+
99
+ existences = Hash[
100
+ [
101
+ prereleases_dir.join('a', '1'),
102
+ prereleases_dir.join('a', '2'),
103
+ prereleases_dir.join('a', '3'),
104
+ prereleases_dir.join('b', '1'),
105
+ prereleases_dir.join('b', '2'),
106
+ ].map { |file|
107
+ [file, file.exist?]
108
+ }
109
+ ]
110
+
111
+ expect(existences).to eq(
112
+ prereleases_dir.join('a', '1') => false,
113
+ prereleases_dir.join('a', '2') => true,
114
+ prereleases_dir.join('a', '3') => true,
115
+ prereleases_dir.join('b', '1') => true,
116
+ prereleases_dir.join('b', '2') => true,
117
+ )
118
+ end
70
119
  end
71
120
  end
72
121
  end
@@ -36,6 +36,7 @@ describe Mamiya::Agent::Tasks::Fetch do
36
36
  package: 'mypkg',
37
37
  destination: app_destination,
38
38
  config: config,
39
+ logger: task.logger,
39
40
  ).and_return(step)
40
41
 
41
42
  allow(agent).to receive(:trigger)
@@ -0,0 +1,127 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+ require 'pathname'
4
+
5
+ require 'mamiya/agent/tasks/notifyable'
6
+ require 'mamiya/agent/tasks/prepare'
7
+
8
+ require 'mamiya/steps/extract'
9
+ require 'mamiya/steps/prepare'
10
+
11
+ describe Mamiya::Agent::Tasks::Prepare do
12
+ let!(:tmpdir) { Pathname.new Dir.mktmpdir("mamiya-agent-tasks-prepare-spec") }
13
+ after { FileUtils.remove_entry_secure tmpdir }
14
+
15
+ let(:packages_dir) { tmpdir.join('packages').tap(&:mkdir) }
16
+ let(:prereleases_dir) { tmpdir.join('prereleases').tap(&:mkdir) }
17
+
18
+ let(:config) do
19
+ _pkg, _pre = packages_dir, prereleases_dir
20
+ Mamiya::Configuration.new.evaluate! do
21
+ set :packages_dir, _pkg
22
+ set :prereleases_dir, _pre
23
+ end
24
+ end
25
+
26
+ let(:agent) { double('agent', config: config, trigger: nil, labels: [:foo, :bar]) }
27
+ let(:task_queue) { double('task_queue', enqueue: nil) }
28
+
29
+ let(:extract_step) { double('extract step', run!: nil) }
30
+ let(:prepare_step) { double('prepare step', run!: nil) }
31
+
32
+ let(:job) { {'app' => 'myapp', 'pkg' => 'mypkg'} }
33
+
34
+ subject(:task) { described_class.new(task_queue, job, agent: agent, raise_error: true) }
35
+
36
+ it 'inherits notifyable task' do
37
+ expect(described_class.ancestors).to include(Mamiya::Agent::Tasks::Notifyable)
38
+ end
39
+
40
+ describe "#execute" do
41
+ context "when package not fetched" do
42
+ before do
43
+ expect(Mamiya::Steps::Extract).not_to receive(:new)
44
+ expect(Mamiya::Steps::Prepare).not_to receive(:new)
45
+ end
46
+
47
+ it "enqueues fetch task and finish" do
48
+ expect(task_queue).to receive(:enqueue).with(
49
+ :fetch, job.merge('task' => 'prepare', '_chain' => ['prepare'])
50
+ )
51
+
52
+ task.execute
53
+ end
54
+
55
+ context "with _chain-ed job" do
56
+ let(:job) { {'app' => 'myapp', 'pkg' => 'mypkg', '_chain' => ['next']} }
57
+
58
+ it "enqueues fetch task and finish" do
59
+ expect(task_queue).to receive(:enqueue).with(
60
+ :fetch, job.merge('task' => 'prepare', '_chain' => ['prepare', 'next'])
61
+ )
62
+
63
+ task.execute
64
+ end
65
+ end
66
+ end
67
+
68
+ context "when package fetched" do
69
+ before do
70
+ packages_dir.join('myapp').mkdir
71
+ File.write packages_dir.join('myapp', 'mypkg.tar.gz'), "\n"
72
+
73
+ allow(Mamiya::Steps::Extract).to receive(:new).with(
74
+ package: packages_dir.join('myapp', 'mypkg.tar.gz'),
75
+ destination: prereleases_dir.join('myapp', 'mypkg'),
76
+ config: config,
77
+ logger: task.logger,
78
+ ).and_return(extract_step)
79
+
80
+ allow(Mamiya::Steps::Prepare).to receive(:new).with(
81
+ target: prereleases_dir.join('myapp', 'mypkg'),
82
+ labels: [:foo, :bar],
83
+ config: config,
84
+ script: nil,
85
+ logger: task.logger,
86
+ ).and_return(prepare_step)
87
+ end
88
+
89
+ it "prepares extracted release" do
90
+ expect(extract_step).to receive(:run!).ordered
91
+ expect(prepare_step).to receive(:run!).ordered
92
+
93
+ task.execute
94
+ end
95
+
96
+ context "when prepared package exists" do
97
+ before do
98
+ prereleases_dir.join('myapp', 'mypkg').mkpath
99
+ File.write prereleases_dir.join('myapp', 'mypkg', '.mamiya.prepared'),
100
+ "#{Time.now.to_i}\n"
101
+ end
102
+
103
+ it "does nothing" do
104
+ expect(extract_step).not_to receive(:run!)
105
+ expect(prepare_step).not_to receive(:run!)
106
+
107
+ task.execute
108
+ end
109
+ end
110
+
111
+ context "when extracted but non-prepared release exists" do
112
+ before do
113
+ prereleases_dir.join('myapp', 'mypkg').mkpath
114
+ end
115
+
116
+ it "removes existing release then prepare" do
117
+ expect(extract_step).to receive(:run!).ordered do
118
+ expect(prereleases_dir.join('myapp', 'mypkg')).not_to be_exist
119
+ end
120
+ expect(prepare_step).to receive(:run!).ordered
121
+
122
+ task.execute
123
+ end
124
+ end
125
+ end
126
+ end
127
+ end
data/spec/agent_spec.rb CHANGED
@@ -10,6 +10,8 @@ require 'mamiya/agent'
10
10
  require 'mamiya/agent/task_queue'
11
11
  require 'mamiya/agent/actions'
12
12
 
13
+ require 'mamiya/configuration'
14
+
13
15
  require_relative './support/dummy_serf.rb'
14
16
 
15
17
  describe Mamiya::Agent do
@@ -21,7 +23,9 @@ describe Mamiya::Agent do
21
23
  end
22
24
 
23
25
  let(:config) do
24
- {serf: {agent: {rpc_addr: '127.0.0.1:17373', bind: '127.0.0.1:17946'}}}
26
+ Mamiya::Configuration.new.evaluate! do
27
+ set :serf, {agent: {rpc_addr: '127.0.0.1:17373', bind: '127.0.0.1:17946'}}
28
+ end
25
29
  end
26
30
 
27
31
  before do
@@ -84,33 +88,11 @@ describe Mamiya::Agent do
84
88
  end
85
89
  end
86
90
 
87
- describe "#update_tags!" do
88
- describe "(status)" do
89
- context "when it is not busy" do
90
- it "shows ready" do
91
- agent.update_tags!
92
-
93
- expect(serf.tags['mamiya']).to eq ',ready,'
94
- end
95
- end
96
-
97
- context "when it is running multiple jobs" do
98
- pending
99
- end
100
- end
101
-
102
- describe "(prepared)" do
103
- pending
104
- end
105
-
106
- describe "(current)" do
107
- pending
108
- end
109
- end
110
-
111
91
  describe "#status" do
112
92
  before do
113
93
  allow(agent).to receive(:existing_packages).and_return("app" => ["pkg"])
94
+ allow(agent).to receive(:existing_prereleases).and_return("app" => ["pkg"])
95
+ allow(agent).to receive(:labels).and_return([:foo,:bar])
114
96
 
115
97
  allow(task_queue).to receive(:status).and_return({a: {working: nil, queue: []}})
116
98
  end
@@ -133,12 +115,24 @@ describe Mamiya::Agent do
133
115
  expect(status[:packages]).to eq agent.existing_packages
134
116
  end
135
117
 
118
+ it "includes prereleases" do
119
+ expect(status[:prereleases]).to eq agent.existing_prereleases
120
+ end
121
+
122
+ it "includes status" do
123
+ expect(status[:labels]).to eq agent.labels
124
+ end
125
+
136
126
  context "with packages=false" do
137
127
  subject(:status) { agent.status(packages: false) }
138
128
 
139
129
  it "doesn't include existing packages" do
140
130
  expect(status.has_key?(:packages)).to be_false
141
131
  end
132
+
133
+ it "doesn't include existing packages" do
134
+ expect(status.has_key?(:prereleases)).to be_false
135
+ end
142
136
  end
143
137
 
144
138
  describe "(task queue)" do
@@ -176,6 +170,51 @@ describe Mamiya::Agent do
176
170
  end
177
171
  end
178
172
 
173
+ describe "#existing_prereleases" do
174
+ let!(:prereleases_dir) { Pathname.new Dir.mktmpdir('mamiya-agent-spec') }
175
+ after { FileUtils.remove_entry_secure(prereleases_dir) }
176
+
177
+ let(:config) { {prereleases_dir: prereleases_dir} }
178
+
179
+ subject(:existing_prereleases) { agent.existing_prereleases }
180
+
181
+ before do
182
+ prereleases_dir.join('a').mkdir
183
+ prereleases_dir.join('a', '1').mkdir
184
+ File.write prereleases_dir.join('a', '1', '.mamiya.prepared'), "#{Time.now.to_s}\n"
185
+ prereleases_dir.join('a', '2').mkdir
186
+ File.write prereleases_dir.join('a', '2', '.mamiya.prepared'), "#{Time.now.to_s}\n"
187
+ prereleases_dir.join('a', '3').mkdir
188
+ end
189
+
190
+ it "returns prepared prereleases" do
191
+ expect(existing_prereleases).to eq('a' => ['1', '2'])
192
+ end
193
+ end
194
+
195
+
196
+ describe "#labels" do
197
+ subject(:labels) { agent.labels }
198
+
199
+ context "with config.labels" do
200
+ before do
201
+ config.evaluate! do
202
+ labels { [:foo, :bar, :baz] }
203
+ end
204
+ end
205
+
206
+ it "retrieves label from configuration" do
207
+ expect(labels).to eq [:foo, :bar, :baz]
208
+ end
209
+ end
210
+
211
+ context "without config.labels" do
212
+ it "returns []" do
213
+ expect(labels).to eq []
214
+ end
215
+ end
216
+ end
217
+
179
218
  describe "query responder" do
180
219
  it "responds to 'mamiya:status'" do
181
220
  allow(agent).to receive(:status).with(packages: false).and_return("my" => "status")
@@ -185,10 +224,19 @@ describe Mamiya::Agent do
185
224
  end
186
225
 
187
226
  it "responds to 'mamiya:packages'" do
188
- allow(agent).to receive(:existing_packages).and_return(%w(pkg1 pkg2))
227
+ allow(agent).to receive(:existing_packages).and_return("app" => %w(pkg1 pkg2))
228
+ allow(agent).to receive(:existing_prereleases).and_return("app" => %w(pkg2))
189
229
 
190
230
  response = serf.trigger_query('mamiya:packages', '')
191
- expect(JSON.parse(response)).to eq(%w(pkg1 pkg2))
231
+
232
+ expect(JSON.parse(response)).to eq(
233
+ "packages" => {
234
+ "app" => %w(pkg1 pkg2)
235
+ },
236
+ "prereleases" => {
237
+ "app" => %w(pkg2)
238
+ },
239
+ )
192
240
  end
193
241
  end
194
242
 
data/spec/dsl_spec.rb CHANGED
@@ -50,6 +50,12 @@ describe Mamiya::DSL do
50
50
  dsl.load! "#{__dir__}/fixtures/dsl_test_load.rb"
51
51
  }.to change { dsl.foo }.from(42).to(72)
52
52
  end
53
+
54
+ it "sets _file" do
55
+ expect {
56
+ dsl.load! "#{__dir__}/fixtures/dsl_test_load.rb"
57
+ }.to change { dsl._file }.from(nil).to(Pathname.new("#{__dir__}/fixtures/dsl_test_load.rb"))
58
+ end
53
59
  end
54
60
 
55
61
  describe "#use" do
@@ -284,12 +290,4 @@ describe Mamiya::DSL do
284
290
  end
285
291
  end
286
292
  end
287
-
288
- describe "#servers" do
289
- pending "Hey!"
290
- end
291
-
292
- describe "#use_servers" do
293
- pending "Hey!"
294
- end
295
293
  end
@@ -3,6 +3,8 @@ require 'json'
3
3
 
4
4
  require 'villein/event'
5
5
 
6
+ require 'mamiya/util/label_matcher'
7
+
6
8
  require 'mamiya/master/agent_monitor'
7
9
 
8
10
  describe Mamiya::Master::AgentMonitor do
@@ -30,7 +32,7 @@ describe Mamiya::Master::AgentMonitor do
30
32
  {
31
33
  "Acks" => ['a'],
32
34
  "Responses" => {
33
- 'a' => {"foo" => "bar", 'packages' => ['pkg1']}.to_json,
35
+ 'a' => {"foo" => "bar", 'packages' => {"app" => ['pkg1']}, 'prereleases' => {"app" => ['pkg2']}}.to_json,
34
36
  },
35
37
  }
36
38
  end
@@ -39,11 +41,50 @@ describe Mamiya::Master::AgentMonitor do
39
41
  {
40
42
  "Acks" => ['a'],
41
43
  "Responses" => {
42
- 'a' => ['pkg1','pkg2'].to_json,
44
+ 'a' => {"packages" => {"app" => ['pkg1','pkg2']}, "prereleases" => {"app" => ['pkg2']}}.to_json,
43
45
  },
44
46
  }
45
47
  end
46
48
 
49
+ describe "#statuses" do
50
+ let(:members) do
51
+ [
52
+ {
53
+ "name"=>"a", "status"=>"alive",
54
+ "addr"=>"x.x.x.x:7676", "port"=>7676,
55
+ "protocol"=>{"max"=>4, "min"=>2, "version"=>4},
56
+ "tags"=>{},
57
+ },
58
+ ]
59
+ end
60
+
61
+ let(:status_query_response) do
62
+ {
63
+ "Acks" => ['a','b'],
64
+ "Responses" => {
65
+ 'a' => {'packages' => {}, 'prereleases' => {}, "labels" => ['foo','bar']}.to_json,
66
+ 'b' => {'packages' => {}, 'prereleases' => {}, "labels" => ['baz']}.to_json,
67
+ },
68
+ }
69
+ end
70
+
71
+ before do
72
+ stub_serf_queries()
73
+ allow(serf).to receive(:members).and_return(members)
74
+
75
+ agent_monitor.refresh
76
+ end
77
+
78
+ context "with labels" do
79
+ it "can filter agents by label" do
80
+ # FIXME: stub label matcher
81
+ expect(agent_monitor.statuses.keys.sort).to eq ['a', 'b']
82
+ expect(agent_monitor.statuses(labels: ['foo']).keys.sort).to eq ['a']
83
+ expect(agent_monitor.statuses(labels: ['baz']).keys.sort).to eq ['b']
84
+ end
85
+ end
86
+ end
87
+
47
88
  describe "#refresh" do
48
89
  let(:members) do
49
90
  [
@@ -69,12 +110,32 @@ describe Mamiya::Master::AgentMonitor do
69
110
  }.to('bar')
70
111
  end
71
112
 
113
+ it "updates #last_refresh_at" do
114
+ agent_monitor.refresh
115
+ expect(agent_monitor.last_refresh_at).to be_a_kind_of(Time)
116
+
117
+ expect {
118
+ agent_monitor.refresh
119
+ }.to change {
120
+ agent_monitor.last_refresh_at
121
+ }
122
+ end
123
+
124
+
72
125
  it "updates status .packages by packages query" do
73
126
  expect {
74
127
  agent_monitor.refresh
75
128
  }.to change {
76
129
  agent_monitor.statuses["a"] && agent_monitor.statuses["a"]['packages']
77
- }.to(%w(pkg1 pkg2))
130
+ }.to("app" => %w(pkg1 pkg2))
131
+ end
132
+
133
+ it "updates status .prereleases by packages query" do
134
+ expect {
135
+ agent_monitor.refresh
136
+ }.to change {
137
+ agent_monitor.statuses["a"] && agent_monitor.statuses["a"]['prereleases']
138
+ }.to("app" => %w(pkg2))
78
139
  end
79
140
 
80
141
  context "when packages query unavailable, but available in status query" do
@@ -91,7 +152,15 @@ describe Mamiya::Master::AgentMonitor do
91
152
  agent_monitor.refresh
92
153
  }.to change {
93
154
  agent_monitor.statuses["a"] && agent_monitor.statuses["a"]['packages']
94
- }.to(%w(pkg1))
155
+ }.to("app" => %w(pkg1))
156
+ end
157
+
158
+ it "updates status .prereleases from status" do
159
+ expect {
160
+ agent_monitor.refresh
161
+ }.to change {
162
+ agent_monitor.statuses["a"] && agent_monitor.statuses["a"]['prereleases']
163
+ }.to("app" => %w(pkg2))
95
164
  end
96
165
  end
97
166
 
@@ -393,6 +462,47 @@ describe Mamiya::Master::AgentMonitor do
393
462
  end
394
463
  end
395
464
 
465
+ describe "prerelease" do
466
+ describe ":remove" do
467
+ let(:status) do
468
+ {prereleases: {'myapp' => ['pkg1']}}
469
+ end
470
+
471
+ it "removes removed release from prereleases" do
472
+ commit('mamiya:prerelease:remove',
473
+ app: 'myapp', pkg: 'pkg1')
474
+
475
+ expect(new_status["prereleases"]['myapp']).to eq []
476
+ end
477
+
478
+ context "with existing packages" do
479
+ let(:status) do
480
+ {prereleases: {'myapp' => ['pkg1', 'pkg2']}}
481
+ end
482
+
483
+ it "removes removed release from prereleases" do
484
+ commit('mamiya:prerelease:remove',
485
+ app: 'myapp', pkg: 'pkg1')
486
+
487
+ expect(new_status["prereleases"]['myapp']).to eq ['pkg2']
488
+ end
489
+ end
490
+
491
+ context "with inexist package" do
492
+ let(:status) do
493
+ {prereleases: {'myapp' => ['pkg1', 'pkg3']}}
494
+ end
495
+
496
+ it "removes removed release from packages" do
497
+ commit('mamiya:prerelease:remove',
498
+ app: 'myapp', pkg: 'pkg2')
499
+
500
+ expect(new_status["prereleases"]['myapp']).to eq ['pkg1', 'pkg3']
501
+ end
502
+ end
503
+ end
504
+ end
505
+
396
506
  describe "fetch" do
397
507
  describe "success" do
398
508
  let(:status) do
@@ -420,6 +530,34 @@ describe Mamiya::Master::AgentMonitor do
420
530
  end
421
531
  end
422
532
  end
533
+
534
+ describe "prepare" do
535
+ describe "success" do
536
+ let(:status) do
537
+ {prereleases: {}}
538
+ end
539
+
540
+ it "updates prereleases" do
541
+ commit('mamiya:task:finish',
542
+ task: {task: 'prepare', app: 'myapp', pkg: 'pkg'})
543
+
544
+ expect(new_status["prereleases"]['myapp']).to eq ["pkg"]
545
+ end
546
+
547
+ context "with existing prerelease" do
548
+ let(:status) do
549
+ {prereleases: {'myapp' => ['pkg1']}}
550
+ end
551
+
552
+ it "updates prereleases" do
553
+ commit('mamiya:task:finish',
554
+ task: {task: 'prepare', app: 'myapp', pkg: 'pkg2'})
555
+
556
+ expect(new_status["prereleases"]['myapp']).to eq %w(pkg1 pkg2)
557
+ end
558
+ end
559
+ end
560
+ end
423
561
  end
424
562
  end
425
563
  end