navyrb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,195 @@
1
+ require 'spec_helper'
2
+
3
+ describe Navy::Container do
4
+ let(:specification) do
5
+ {
6
+ :container_name => 'the_container_name',
7
+ :name => 'the_app_name',
8
+ :type => "application"
9
+ }
10
+ end
11
+
12
+ let(:dependencies) do
13
+ ['dep1', 'dep2']
14
+ end
15
+
16
+ let(:etcd) do
17
+ MockEtcd.new
18
+ end
19
+
20
+ subject do
21
+ described_class.new :specification => specification,
22
+ :dependencies => dependencies
23
+ end
24
+
25
+ describe "#name" do
26
+ it "is the container_name in the specification" do
27
+ expect(subject.name).to eq 'the_container_name'
28
+ end
29
+ end
30
+
31
+ describe "#app" do
32
+ it "is the app name in the specification" do
33
+ expect(subject.app).to eq 'the_app_name'
34
+ end
35
+ end
36
+
37
+ describe "#daemon?" do
38
+ context "when the type is application" do
39
+ before :each do
40
+ specification[:type] = "application"
41
+ end
42
+
43
+ it "is a daemon" do
44
+ expect(subject.daemon?).to be true
45
+ end
46
+ end
47
+
48
+ context "when the type is task" do
49
+ before :each do
50
+ specification[:type] = "task"
51
+ end
52
+
53
+ it "is a daemon" do
54
+ expect(subject.daemon?).to be false
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "#can_be_started?" do
60
+ context "when there are no dependencies" do
61
+ let(:dependencies) { [] }
62
+ it "can be started" do
63
+ expect(subject.can_be_started?(etcd)).to be true
64
+ end
65
+ end
66
+
67
+ context "when the dependencies are not in desired state" do
68
+ before :each do
69
+ etcd.setJSON('/navy/containers/dep1/desired', {:state => :desired})
70
+ etcd.setJSON('/navy/containers/dep1/actual', {:state => :not_desired})
71
+ end
72
+
73
+ it "cannot be started" do
74
+ expect(subject.can_be_started?(etcd)).to be false
75
+ end
76
+ end
77
+
78
+ context "when the dependencies are in desired state" do
79
+ before :each do
80
+ etcd.setJSON('/navy/containers/dep1/desired', {:state => :desired})
81
+ etcd.setJSON('/navy/containers/dep1/actual', {:state => :desired})
82
+ etcd.setJSON('/navy/containers/dep2/desired', {:state => :desired})
83
+ etcd.setJSON('/navy/containers/dep2/actual', {:state => :desired})
84
+ end
85
+
86
+ it "can be started" do
87
+ expect(subject.can_be_started?(etcd)).to be true
88
+ end
89
+ end
90
+ end
91
+
92
+ describe "#can_never_be_started?" do
93
+ context "when there are no dependencies" do
94
+ let(:dependencies) { [] }
95
+ it "can *always* be started" do
96
+ expect(subject.can_never_be_started?(etcd)).to be false
97
+ end
98
+ end
99
+
100
+ context "when the dependencies exist" do
101
+ context "when one of the dependencies is errored" do
102
+ before :each do
103
+ etcd.setJSON('/navy/containers/dep1/desired', {:state => :desired})
104
+ etcd.setJSON('/navy/containers/dep1/actual', {:state => :error})
105
+ end
106
+
107
+ it "can *never* be started" do
108
+ expect(subject.can_never_be_started?(etcd)).to be true
109
+ end
110
+ end
111
+
112
+ context "when the dependencies are in non errored states" do
113
+ before :each do
114
+ etcd.setJSON('/navy/containers/dep1/desired', {:state => :desired})
115
+ etcd.setJSON('/navy/containers/dep1/actual', {:state => :not_error})
116
+ end
117
+
118
+ it "can be *potentially* started" do
119
+ expect(subject.can_never_be_started?(etcd)).to be false
120
+ end
121
+
122
+ end
123
+ end
124
+ end
125
+
126
+ describe "#start" do
127
+ let(:cmd) { Navy::CommandBuilder.new(subject).build }
128
+ let(:launched) { [] }
129
+ let(:success) { true }
130
+
131
+ before :each do
132
+ allow(Navy::Runner).to receive(:launch) do |cmd|
133
+ launched << cmd
134
+ success
135
+ end
136
+ end
137
+
138
+ it "generates a command for the container and executes it" do
139
+ subject.start
140
+ expect(launched).to eq [cmd]
141
+ end
142
+
143
+ context "when the command succeeds" do
144
+ it "returns true" do
145
+ expect(subject.start).to be true
146
+ end
147
+ end
148
+
149
+ context "when the command fails" do
150
+ let(:success) { false }
151
+ it "returns false" do
152
+ expect(subject.start).to be false
153
+ end
154
+ end
155
+
156
+ context "when it is a task" do
157
+ before :each do
158
+ specification[:type] = "task"
159
+ specification[:cmds] = ["cmd1", "cmd2"]
160
+ end
161
+
162
+ it "runs each command" do
163
+ subject.start
164
+ expect(launched[0].last).to eq "cmd1"
165
+ expect(launched[1].last).to eq "cmd2"
166
+ end
167
+
168
+ context "when one of the items fails" do
169
+ let(:success) { false }
170
+
171
+ it "returns false and does not run other tasks" do
172
+ expect(subject.start).to be false
173
+ expect(launched.length).to eq 1
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ describe "#stop" do
180
+ let(:cmd) { ["docker rm -f", "the_container_name"] }
181
+ let(:launched) { [] }
182
+
183
+ before :each do
184
+ allow(Navy::Runner).to receive(:launch) do |cmd|
185
+ launched << cmd
186
+ true
187
+ end
188
+ end
189
+
190
+ it "generates a rm command for the container and executes it" do
191
+ subject.stop
192
+ expect(launched).to eq [cmd]
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,202 @@
1
+ require 'spec_helper'
2
+ require 'webmock/rspec'
3
+
4
+ describe Navy::Etcd do
5
+ let(:options) do
6
+ {:host => 'etcdhost', :port => '1234'}
7
+ end
8
+
9
+ let(:example_record) do
10
+ <<-JSON
11
+ {
12
+ "action":"The Action",
13
+ "node": {
14
+ "createdIndex": 1,
15
+ "key": "/akey",
16
+ "modifiedIndex": 2,
17
+ "value": "New Value"
18
+ },
19
+ "prevNode": {
20
+ "createdIndex": 1,
21
+ "key": "/akey",
22
+ "value": "Prev Value",
23
+ "modifiedIndex": 3
24
+ }
25
+ }
26
+ JSON
27
+ end
28
+
29
+ let(:example_json_record) do
30
+ <<-JSON
31
+ {
32
+ "action":"The Action",
33
+ "node": {
34
+ "createdIndex": 1,
35
+ "key": "/akey",
36
+ "modifiedIndex": 2,
37
+ "value": "{\\"key\\":\\"value\\"}"
38
+ },
39
+ "prevNode": {
40
+ "createdIndex": 1,
41
+ "key": "/akey",
42
+ "value": "Prev Value",
43
+ "modifiedIndex": 3
44
+ }
45
+ }
46
+ JSON
47
+ end
48
+
49
+ let(:example_headers) do
50
+ {
51
+ "X-Etcd-Index" => "999"
52
+ }
53
+ end
54
+
55
+ subject { described_class.client options }
56
+
57
+ describe "Retrieval" do
58
+ describe "#get" do
59
+ before :each do
60
+ @request = stub_request(:get, 'http://etcdhost:1234/v2/keys/some/key').
61
+ to_return(:body => example_record, :headers => example_headers)
62
+ end
63
+
64
+ it "fetches a response from the given key" do
65
+ record = subject.get('/some/key')
66
+
67
+ #expect(@request).to have_been_made
68
+
69
+ expect(record.key).to eq '/akey'
70
+ expect(record.etcd_index).to be 999
71
+ expect(record.action).to eq "The Action"
72
+ expect(record.node.createdIndex).to eq 1
73
+ expect(record.node.modifiedIndex).to eq 2
74
+ expect(record.node.value).to eq "New Value"
75
+ expect(record.prevNode.value).to eq "Prev Value"
76
+ end
77
+ end
78
+
79
+ describe "#getJSON" do
80
+ before :each do
81
+ @request = stub_request(:get, 'http://etcdhost:1234/v2/keys/some/key').
82
+ to_return(:body => example_json_record, :headers => example_headers)
83
+ end
84
+
85
+ it "fetches and parses as JSON the given jey" do
86
+ hash = subject.getJSON('/some/key')
87
+ #expect(@request).to have_been_made
88
+
89
+ expect(hash['key']).to eq 'value'
90
+ end
91
+ end
92
+
93
+ describe "#watch" do
94
+ before :each do
95
+ @request = stub_request(:get, 'http://etcdhost:1234/v2/keys/some/key?wait=true').
96
+ to_return(:body => example_record, :headers => example_headers)
97
+ end
98
+
99
+ it "fetches with a wait" do
100
+ record = subject.watch('/some/key')
101
+ #expect(@request).to have_been_made
102
+ end
103
+ end
104
+
105
+ describe "#ls" do
106
+ let(:exampledir) do
107
+ {
108
+ :node => {
109
+ :nodes => [
110
+ {:key => '/some/dir/item1'},
111
+ {:key => '/some/dir/item2'}
112
+ ]
113
+ }
114
+ }.to_json
115
+ end
116
+
117
+ before :each do
118
+ @request = stub_request(:get, 'http://etcdhost:1234/v2/keys/some/dir').
119
+ to_return(:body => exampledir)
120
+ end
121
+
122
+ it "returns the keys in the given directory" do
123
+ keys = subject.ls('/some/dir')
124
+ #expect(@request).to have_been_made
125
+ expect(keys).to include '/some/dir/item1'
126
+ expect(keys).to include '/some/dir/item2'
127
+ expect(keys.length).to eq 2
128
+ end
129
+ end
130
+
131
+ end
132
+
133
+ describe "Storage" do
134
+ describe "#setJSON" do
135
+ let(:data) do
136
+ data = {:some => :json, :data => [:here]}
137
+ end
138
+
139
+ before :each do
140
+ @request = stub_request(:put, 'http://etcdhost:1234/v2/keys/some/json/key').
141
+ with(:body => {:value => data.to_json})
142
+ end
143
+
144
+ it "stores JSON encoded value at given key" do
145
+ subject.setJSON('/some/json/key', data)
146
+ #expect(@request).to have_been_made
147
+ end
148
+ end
149
+
150
+ describe "#delete" do
151
+ before :each do
152
+ @request = stub_request(:delete, 'http://etcdhost:1234/v2/keys/some/key?with=param').to_return(:status => 202)
153
+ end
154
+
155
+ it "deletes the specified key" do
156
+ result = subject.delete('/some/key', :with => :param)
157
+ expect(@request).to have_been_made
158
+ expect(result).to be true
159
+ end
160
+
161
+ context "when the key is missing" do
162
+ before :each do
163
+ @request = stub_request(:delete, 'http://etcdhost:1234/v2/keys/some/key?with=param').to_return(:status => 404)
164
+ end
165
+
166
+ it "returns false" do
167
+ result = subject.delete('/some/key', :with => :param)
168
+ expect(result).to be false
169
+ end
170
+ end
171
+ end
172
+
173
+ describe "#set" do
174
+ let(:data) { "some data " }
175
+
176
+ before :each do
177
+ @request = stub_request(:put, 'http://etcdhost:1234/v2/keys/some/data/value').
178
+ with(:body => {:value => data })
179
+ end
180
+
181
+ it "stores the string at the given value" do
182
+ subject.set('/some/data/value', data)
183
+ #expect(@request).to have_been_made
184
+ end
185
+ end
186
+
187
+ describe "#queueJSON" do
188
+ let(:data) { {:some => :json } }
189
+
190
+ before :each do
191
+ @request = stub_request(:post, 'http://etcdhost:1234/v2/keys/some/queue').
192
+ with(:body => {:value => data.to_json })
193
+ end
194
+
195
+ it "stores the string at the given value" do
196
+ subject.queueJSON('/some/queue', data)
197
+ expect(@request).to have_been_made
198
+ end
199
+ end
200
+ end
201
+
202
+ end
@@ -0,0 +1,69 @@
1
+ require 'spec_helper'
2
+
3
+ describe Navy::Router do
4
+ class ExampleHandler
5
+ def self.handled
6
+ @handled ||= {}
7
+ end
8
+
9
+ def handle_set(params, request)
10
+ self.class.handled[:set] = params, request
11
+ end
12
+
13
+ def handle_delete(params, request)
14
+ self.class.handled[:delete] = params, request
15
+ end
16
+
17
+ end
18
+
19
+ let(:handler1) { Class.new(ExampleHandler) }
20
+ let(:handler2) { Class.new(ExampleHandler) }
21
+
22
+ subject do
23
+ described_class.new do |r|
24
+ r.route '^/a/route$', handler1
25
+ r.route '^/a/:pattern/route$', handler2
26
+ end
27
+ end
28
+
29
+ it "routes through to defined handlers" do
30
+ request = double :key => '/a/route',
31
+ :action => :set
32
+
33
+ subject.route(request)
34
+ expect(handler1.handled[:set]).to eq [{}, request]
35
+ end
36
+
37
+ it "matches placeholder patters" do
38
+ request = double :key => '/a/matching/route',
39
+ :action => :delete
40
+
41
+ subject.route(request)
42
+ expect(handler2.handled[:delete]).to eq [{'pattern' => 'matching'}, request]
43
+ end
44
+
45
+ it "gracefully handles unknown paths" do
46
+ request = double :key => '/unkonwn/path',
47
+ :action => :delete
48
+ expect { subject.route(request) }.to_not raise_error
49
+ end
50
+
51
+ it "gracefully handle unmapped action" do
52
+ request = double :key => '/a/route',
53
+ :action => :unmapped
54
+ expect { subject.route(request) }.to_not raise_error
55
+ end
56
+
57
+ it "passes down global options into the params" do
58
+ options = {:foo => :bar}
59
+ router = described_class.new(options) do |r|
60
+ r.route 'example', handler1
61
+ end
62
+
63
+ request = double :key => 'example', :action => :set
64
+
65
+ router.route(request)
66
+
67
+ expect(handler1.handled[:set][0]).to eq({:foo => :bar})
68
+ end
69
+ end