jira-ruby 1.1.3 → 2.3.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 +5 -5
- data/.gitignore +3 -0
- data/.travis.yml +5 -3
- data/Gemfile +7 -1
- data/Guardfile +1 -1
- data/README.md +452 -0
- data/Rakefile +6 -7
- data/example.rb +38 -6
- data/http-basic-example.rb +14 -13
- data/jira-ruby.gemspec +13 -13
- data/lib/jira/base.rb +58 -53
- data/lib/jira/base_factory.rb +3 -6
- data/lib/jira/client.rb +127 -30
- data/lib/jira/has_many_proxy.rb +0 -1
- data/lib/jira/http_client.rb +54 -16
- data/lib/jira/http_error.rb +3 -5
- data/lib/jira/jwt_client.rb +67 -0
- data/lib/jira/oauth_client.rb +47 -17
- data/lib/jira/request_client.rb +16 -5
- data/lib/jira/resource/agile.rb +34 -9
- data/lib/jira/resource/applinks.rb +5 -8
- data/lib/jira/resource/attachment.rb +41 -3
- data/lib/jira/resource/board.rb +91 -0
- data/lib/jira/resource/board_configuration.rb +9 -0
- data/lib/jira/resource/comment.rb +0 -2
- data/lib/jira/resource/component.rb +1 -3
- data/lib/jira/resource/createmeta.rb +14 -22
- data/lib/jira/resource/field.rb +22 -22
- data/lib/jira/resource/filter.rb +2 -2
- data/lib/jira/resource/issue.rb +69 -38
- data/lib/jira/resource/issue_picker_suggestions.rb +24 -0
- data/lib/jira/resource/issue_picker_suggestions_issue.rb +10 -0
- data/lib/jira/resource/issuelink.rb +3 -5
- data/lib/jira/resource/issuelinktype.rb +0 -1
- data/lib/jira/resource/issuetype.rb +1 -3
- data/lib/jira/resource/priority.rb +1 -3
- data/lib/jira/resource/project.rb +8 -8
- data/lib/jira/resource/rapidview.rb +28 -7
- data/lib/jira/resource/remotelink.rb +1 -4
- data/lib/jira/resource/resolution.rb +2 -4
- data/lib/jira/resource/serverinfo.rb +1 -2
- data/lib/jira/resource/sprint.rb +86 -17
- data/lib/jira/resource/sprint_report.rb +8 -0
- data/lib/jira/resource/status.rb +1 -3
- data/lib/jira/resource/suggested_issue.rb +9 -0
- data/lib/jira/resource/transition.rb +2 -6
- data/lib/jira/resource/user.rb +12 -2
- data/lib/jira/resource/version.rb +1 -3
- data/lib/jira/resource/watcher.rb +35 -0
- data/lib/jira/resource/webhook.rb +3 -6
- data/lib/jira/resource/worklog.rb +3 -5
- data/lib/jira/version.rb +1 -1
- data/lib/jira-ruby.rb +12 -2
- data/lib/tasks/generate.rake +4 -4
- data/spec/integration/attachment_spec.rb +17 -8
- data/spec/integration/comment_spec.rb +31 -34
- data/spec/integration/component_spec.rb +21 -24
- data/spec/integration/field_spec.rb +15 -18
- data/spec/integration/issue_spec.rb +45 -46
- data/spec/integration/issuelinktype_spec.rb +8 -11
- data/spec/integration/issuetype_spec.rb +5 -7
- data/spec/integration/priority_spec.rb +5 -8
- data/spec/integration/project_spec.rb +13 -20
- data/spec/integration/rapidview_spec.rb +17 -10
- data/spec/integration/resolution_spec.rb +7 -10
- data/spec/integration/status_spec.rb +5 -8
- data/spec/integration/transition_spec.rb +17 -20
- data/spec/integration/user_spec.rb +24 -8
- data/spec/integration/version_spec.rb +21 -25
- data/spec/integration/watcher_spec.rb +62 -0
- data/spec/integration/webhook.rb +8 -17
- data/spec/integration/worklog_spec.rb +30 -34
- data/spec/jira/base_factory_spec.rb +11 -12
- data/spec/jira/base_spec.rb +216 -229
- data/spec/jira/client_spec.rb +227 -159
- data/spec/jira/has_many_proxy_spec.rb +11 -12
- data/spec/jira/http_client_spec.rb +254 -31
- data/spec/jira/http_error_spec.rb +7 -9
- data/spec/jira/jwt_uri_builder_spec.rb +59 -0
- data/spec/jira/oauth_client_spec.rb +110 -39
- data/spec/jira/request_client_spec.rb +36 -9
- data/spec/jira/resource/agile_spec.rb +135 -0
- data/spec/jira/resource/attachment_spec.rb +127 -9
- data/spec/jira/resource/board_spec.rb +224 -0
- data/spec/jira/resource/createmeta_spec.rb +39 -34
- data/spec/jira/resource/field_spec.rb +42 -48
- data/spec/jira/resource/filter_spec.rb +40 -40
- data/spec/jira/resource/issue_picker_suggestions_spec.rb +79 -0
- data/spec/jira/resource/issue_spec.rb +88 -85
- data/spec/jira/resource/issuelink_spec.rb +1 -1
- data/spec/jira/resource/jira_picker_suggestions_issue_spec.rb +18 -0
- data/spec/jira/resource/project_factory_spec.rb +2 -4
- data/spec/jira/resource/project_spec.rb +86 -33
- data/spec/jira/resource/sprint_spec.rb +90 -0
- data/spec/jira/resource/user_factory_spec.rb +6 -8
- data/spec/jira/resource/worklog_spec.rb +9 -11
- data/spec/mock_responses/board/1.json +33 -0
- data/spec/mock_responses/board/1_issues.json +62 -0
- data/spec/mock_responses/empty_issues.json +8 -0
- data/spec/mock_responses/issue/10002/watchers.json +13 -0
- data/spec/mock_responses/issue.json +1 -1
- data/spec/mock_responses/sprint/1_issues.json +125 -0
- data/spec/spec_helper.rb +8 -9
- data/spec/support/clients_helper.rb +4 -4
- data/spec/support/shared_examples/integration.rb +60 -77
- metadata +115 -54
- data/README.rdoc +0 -329
- /data/spec/mock_responses/{attachment → issue/10002/attachments}/10000.json +0 -0
data/spec/jira/base_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe JIRA::Base do
|
4
|
-
|
5
4
|
class JIRADelegation < SimpleDelegator # :nodoc:
|
6
5
|
end
|
7
6
|
|
@@ -10,57 +9,56 @@ describe JIRA::Base do
|
|
10
9
|
|
11
10
|
class JIRA::Resource::HasOneExample < JIRA::Base # :nodoc:
|
12
11
|
has_one :deadbeef
|
13
|
-
has_one :muffin, :
|
14
|
-
has_one :brunchmuffin, :
|
15
|
-
:
|
12
|
+
has_one :muffin, class: JIRA::Resource::Deadbeef
|
13
|
+
has_one :brunchmuffin, class: JIRA::Resource::Deadbeef,
|
14
|
+
nested_under: 'nested'
|
16
15
|
has_one :breakfastscone,
|
17
|
-
:
|
18
|
-
:
|
16
|
+
class: JIRA::Resource::Deadbeef,
|
17
|
+
nested_under: %w[nested breakfastscone]
|
19
18
|
has_one :irregularly_named_thing,
|
20
|
-
:
|
21
|
-
:
|
19
|
+
class: JIRA::Resource::Deadbeef,
|
20
|
+
attribute_key: 'irregularlyNamedThing'
|
22
21
|
end
|
23
22
|
|
24
23
|
class JIRA::Resource::HasManyExample < JIRA::Base # :nodoc:
|
25
24
|
has_many :deadbeefs
|
26
|
-
has_many :brunchmuffins, :
|
27
|
-
|
25
|
+
has_many :brunchmuffins, class: JIRA::Resource::Deadbeef,
|
26
|
+
nested_under: 'nested'
|
28
27
|
has_many :breakfastscones,
|
29
|
-
:
|
30
|
-
:
|
28
|
+
class: JIRA::Resource::Deadbeef,
|
29
|
+
nested_under: %w[nested breakfastscone]
|
31
30
|
has_many :irregularly_named_things,
|
32
|
-
:
|
33
|
-
:
|
34
|
-
|
31
|
+
class: JIRA::Resource::Deadbeef,
|
32
|
+
attribute_key: 'irregularlyNamedThings'
|
35
33
|
end
|
36
34
|
|
37
|
-
let(:client) { double(
|
38
|
-
let(:attrs) {
|
35
|
+
let(:client) { double('client') }
|
36
|
+
let(:attrs) { {} }
|
39
37
|
|
40
|
-
subject { JIRA::Resource::Deadbeef.new(client, :
|
38
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs: attrs) }
|
41
39
|
|
42
40
|
let(:decorated) { JIRADelegation.new(subject) }
|
43
41
|
|
44
|
-
describe
|
45
|
-
describe
|
46
|
-
it
|
42
|
+
describe '#respond_to?' do
|
43
|
+
describe 'when decorated using SimpleDelegator' do
|
44
|
+
it 'responds to client' do
|
47
45
|
expect(decorated.respond_to?(:client)).to eq(true)
|
48
46
|
end
|
49
|
-
it
|
50
|
-
expect
|
47
|
+
it 'does not raise an error' do
|
48
|
+
expect do
|
51
49
|
decorated.respond_to?(:client)
|
52
|
-
|
50
|
+
end.not_to raise_error
|
53
51
|
end
|
54
52
|
end
|
55
53
|
end
|
56
54
|
|
57
|
-
it
|
55
|
+
it 'assigns the client and attrs' do
|
58
56
|
expect(subject.client).to eq(client)
|
59
57
|
expect(subject.attrs).to eq(attrs)
|
60
58
|
end
|
61
59
|
|
62
|
-
it
|
63
|
-
response = double
|
60
|
+
it 'returns all the deadbeefs' do
|
61
|
+
response = double
|
64
62
|
expect(response).to receive(:body).and_return('[{"self":"http://deadbeef/","id":"98765"}]')
|
65
63
|
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef').and_return(response)
|
66
64
|
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
@@ -73,8 +71,8 @@ describe JIRA::Base do
|
|
73
71
|
expect(first.expanded?).to be_falsey
|
74
72
|
end
|
75
73
|
|
76
|
-
it
|
77
|
-
response = instance_double(
|
74
|
+
it 'finds a deadbeef by id' do
|
75
|
+
response = instance_double('Response', body: '{"self":"http://deadbeef/","id":"98765"}')
|
78
76
|
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765').and_return(response)
|
79
77
|
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
80
78
|
deadbeef = JIRA::Resource::Deadbeef.find(client, '98765')
|
@@ -84,16 +82,16 @@ describe JIRA::Base do
|
|
84
82
|
expect(deadbeef.expanded?).to be_truthy
|
85
83
|
end
|
86
84
|
|
87
|
-
it
|
85
|
+
it 'finds a deadbeef containing changelog by id' do
|
88
86
|
response = instance_double(
|
89
|
-
|
87
|
+
'Response',
|
90
88
|
body: '{"self":"http://deadbeef/","id":"98765","changelog":{"histories":[]}}'
|
91
89
|
)
|
92
90
|
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765?expand=changelog').and_return(response)
|
93
91
|
|
94
92
|
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
95
93
|
|
96
|
-
deadbeef = JIRA::Resource::Deadbeef.find(client, '98765',
|
94
|
+
deadbeef = JIRA::Resource::Deadbeef.find(client, '98765', expand: 'changelog')
|
97
95
|
expect(deadbeef.client).to eq(client)
|
98
96
|
expect(deadbeef.attrs['self']).to eq('http://deadbeef/')
|
99
97
|
expect(deadbeef.attrs['id']).to eq('98765')
|
@@ -101,60 +99,58 @@ describe JIRA::Base do
|
|
101
99
|
expect(deadbeef.attrs['changelog']['histories']).to eq([])
|
102
100
|
end
|
103
101
|
|
104
|
-
it
|
105
|
-
deadbeef = JIRA::Resource::Deadbeef.build(client, 'id' =>
|
102
|
+
it 'builds a deadbeef' do
|
103
|
+
deadbeef = JIRA::Resource::Deadbeef.build(client, 'id' => '98765')
|
106
104
|
expect(deadbeef.expanded?).to be_falsey
|
107
105
|
|
108
106
|
expect(deadbeef.client).to eq(client)
|
109
107
|
expect(deadbeef.attrs['id']).to eq('98765')
|
110
108
|
end
|
111
109
|
|
112
|
-
it
|
110
|
+
it 'returns the endpoint name' do
|
113
111
|
expect(subject.class.endpoint_name).to eq('deadbeef')
|
114
112
|
end
|
115
113
|
|
116
|
-
it
|
114
|
+
it 'returns the path_component' do
|
117
115
|
attrs['id'] = '123'
|
118
116
|
expect(subject.path_component).to eq('/deadbeef/123')
|
119
117
|
end
|
120
118
|
|
121
|
-
it
|
119
|
+
it 'returns the path component for unsaved instances' do
|
122
120
|
expect(subject.path_component).to eq('/deadbeef')
|
123
121
|
end
|
124
122
|
|
125
|
-
it
|
123
|
+
it 'converts to a symbol' do
|
126
124
|
expect(subject.to_sym).to eq(:deadbeef)
|
127
125
|
end
|
128
126
|
|
129
|
-
describe
|
130
|
-
|
127
|
+
describe 'collection_path' do
|
131
128
|
before(:each) do
|
132
|
-
expect(client).to receive(:options).and_return(:
|
129
|
+
expect(client).to receive(:options).and_return(rest_base_path: '/deadbeef/bar')
|
133
130
|
end
|
134
131
|
|
135
|
-
it
|
132
|
+
it 'returns the collection_path' do
|
136
133
|
expect(subject.collection_path).to eq('/deadbeef/bar/deadbeef')
|
137
134
|
end
|
138
135
|
|
139
|
-
it
|
136
|
+
it 'returns the collection_path with a prefix' do
|
140
137
|
expect(subject.collection_path('/baz/')).to eq('/deadbeef/bar/baz/deadbeef')
|
141
138
|
end
|
142
139
|
|
143
|
-
it
|
140
|
+
it 'has a class method that returns the collection_path' do
|
144
141
|
expect(subject.class.collection_path(client)).to eq('/deadbeef/bar/deadbeef')
|
145
142
|
end
|
146
143
|
end
|
147
144
|
|
148
|
-
it
|
149
|
-
expect(described_class.parse_json('{"foo":"bar"}')).to eq(
|
145
|
+
it 'parses json' do
|
146
|
+
expect(described_class.parse_json('{"foo":"bar"}')).to eq('foo' => 'bar')
|
150
147
|
end
|
151
148
|
|
152
|
-
describe
|
149
|
+
describe 'dynamic instance methods' do
|
150
|
+
let(:attrs) { { 'foo' => 'bar', 'flum' => 'goo', 'object_id' => 'dummy' } }
|
151
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs: attrs) }
|
153
152
|
|
154
|
-
|
155
|
-
subject { JIRA::Resource::Deadbeef.new(client, :attrs => attrs) }
|
156
|
-
|
157
|
-
it "responds to each of the top level attribute names" do
|
153
|
+
it 'responds to each of the top level attribute names' do
|
158
154
|
expect(subject).to respond_to(:foo)
|
159
155
|
expect(subject).to respond_to('flum')
|
160
156
|
expect(subject).to respond_to(:object_id)
|
@@ -169,39 +165,36 @@ describe JIRA::Base do
|
|
169
165
|
end
|
170
166
|
end
|
171
167
|
|
172
|
-
describe
|
173
|
-
|
174
|
-
subject { JIRA::Resource::Deadbeef.new(client, :attrs => {'id' => '98765'}) }
|
175
|
-
|
176
|
-
describe "not cached" do
|
168
|
+
describe 'fetch' do
|
169
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs: { 'id' => '98765' }) }
|
177
170
|
|
171
|
+
describe 'not cached' do
|
178
172
|
before(:each) do
|
179
|
-
response = instance_double(
|
173
|
+
response = instance_double('Response', body: '{"self":"http://deadbeef/","id":"98765"}')
|
180
174
|
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765').and_return(response)
|
181
175
|
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
182
176
|
end
|
183
177
|
|
184
|
-
it
|
178
|
+
it 'sets expanded to true after fetch' do
|
185
179
|
expect(subject.expanded?).to be_falsey
|
186
180
|
subject.fetch
|
187
181
|
expect(subject.expanded?).to be_truthy
|
188
182
|
end
|
189
183
|
|
190
|
-
it
|
184
|
+
it 'performs a fetch' do
|
191
185
|
expect(subject.expanded?).to be_falsey
|
192
186
|
subject.fetch
|
193
|
-
expect(subject.self).to eq(
|
194
|
-
expect(subject.id).to eq(
|
187
|
+
expect(subject.self).to eq('http://deadbeef/')
|
188
|
+
expect(subject.id).to eq('98765')
|
195
189
|
end
|
196
190
|
|
197
|
-
it
|
191
|
+
it 'performs a fetch if already fetched and force flag is true' do
|
198
192
|
subject.expanded = true
|
199
193
|
subject.fetch(true)
|
200
194
|
end
|
201
|
-
|
202
195
|
end
|
203
196
|
|
204
|
-
describe
|
197
|
+
describe 'cached' do
|
205
198
|
it "doesn't perform a fetch if already fetched" do
|
206
199
|
subject.expanded = true
|
207
200
|
expect(client).not_to receive(:get)
|
@@ -212,25 +205,24 @@ describe JIRA::Base do
|
|
212
205
|
context "with expand parameter 'changelog'" do
|
213
206
|
it "fetchs changelogs '" do
|
214
207
|
response = instance_double(
|
215
|
-
|
208
|
+
'Response',
|
216
209
|
body: '{"self":"http://deadbeef/","id":"98765","changelog":{"histories":[]}}'
|
217
210
|
)
|
218
211
|
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765?expand=changelog').and_return(response)
|
219
212
|
|
220
213
|
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
221
214
|
|
222
|
-
subject.fetch(false,
|
215
|
+
subject.fetch(false, expand: 'changelog')
|
223
216
|
|
224
|
-
expect(subject.self).to eq(
|
225
|
-
expect(subject.id).to eq(
|
217
|
+
expect(subject.self).to eq('http://deadbeef/')
|
218
|
+
expect(subject.id).to eq('98765')
|
226
219
|
expect(subject.changelog['histories']).to eq([])
|
227
220
|
end
|
228
221
|
end
|
229
222
|
end
|
230
223
|
|
231
|
-
describe
|
232
|
-
|
233
|
-
let(:response) { double() }
|
224
|
+
describe 'save' do
|
225
|
+
let(:response) { double }
|
234
226
|
|
235
227
|
subject { JIRA::Resource::Deadbeef.new(client) }
|
236
228
|
|
@@ -238,49 +230,49 @@ describe JIRA::Base do
|
|
238
230
|
expect(subject).to receive(:url).and_return('/foo/bar')
|
239
231
|
end
|
240
232
|
|
241
|
-
it
|
242
|
-
response = instance_double(
|
233
|
+
it 'POSTs a new record' do
|
234
|
+
response = instance_double('Response', body: '{"id":"123"}')
|
243
235
|
allow(subject).to receive(:new_record?) { true }
|
244
|
-
expect(client).to receive(:post).with('/foo/bar','{"foo":"bar"}').and_return(response)
|
245
|
-
expect(subject.save(
|
246
|
-
expect(subject.id).to eq(
|
236
|
+
expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
237
|
+
expect(subject.save('foo' => 'bar')).to be_truthy
|
238
|
+
expect(subject.id).to eq('123')
|
247
239
|
expect(subject.expanded).to be_falsey
|
248
240
|
end
|
249
241
|
|
250
|
-
it
|
251
|
-
response = instance_double(
|
242
|
+
it 'PUTs an existing record' do
|
243
|
+
response = instance_double('Response', body: nil)
|
252
244
|
allow(subject).to receive(:new_record?) { false }
|
253
|
-
expect(client).to receive(:put).with('/foo/bar','{"foo":"bar"}').and_return(response)
|
254
|
-
expect(subject.save(
|
245
|
+
expect(client).to receive(:put).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
246
|
+
expect(subject.save('foo' => 'bar')).to be_truthy
|
255
247
|
expect(subject.expanded).to be_falsey
|
256
248
|
end
|
257
249
|
|
258
|
-
it
|
259
|
-
response = instance_double(
|
260
|
-
expect(client).to receive(:post).with('/foo/bar','{"foo":{"fum":"dum"}}').and_return(response)
|
261
|
-
subject.attrs = {
|
262
|
-
subject.save(
|
263
|
-
expect(subject.foo).to eq(
|
250
|
+
it 'merges attrs on save' do
|
251
|
+
response = instance_double('Response', body: nil)
|
252
|
+
expect(client).to receive(:post).with('/foo/bar', '{"foo":{"fum":"dum"}}').and_return(response)
|
253
|
+
subject.attrs = { 'foo' => { 'bar' => 'baz' } }
|
254
|
+
subject.save('foo' => { 'fum' => 'dum' })
|
255
|
+
expect(subject.foo).to eq('bar' => 'baz', 'fum' => 'dum')
|
264
256
|
end
|
265
257
|
|
266
|
-
it
|
267
|
-
response = instance_double(
|
258
|
+
it 'returns false when an invalid field is set' do # The JIRA REST API apparently ignores fields that you aren't allowed to set manually
|
259
|
+
response = instance_double('Response', body: '{"errorMessages":["blah"]}', status: 400)
|
268
260
|
allow(subject).to receive(:new_record?) { false }
|
269
|
-
expect(client).to receive(:put).with('/foo/bar','{"invalid_field":"foobar"}').and_raise(JIRA::HTTPError.new(response))
|
270
|
-
expect(subject.save(
|
261
|
+
expect(client).to receive(:put).with('/foo/bar', '{"invalid_field":"foobar"}').and_raise(JIRA::HTTPError.new(response))
|
262
|
+
expect(subject.save('invalid_field' => 'foobar')).to be_falsey
|
271
263
|
end
|
272
264
|
|
273
|
-
it
|
274
|
-
response = double(
|
275
|
-
expect(client).to receive(:post).with('/foo/bar','{"foo":"bar"}').and_raise(JIRA::HTTPError.new(response))
|
276
|
-
expect(subject.save(
|
277
|
-
expect(subject.attrs[
|
278
|
-
expect(subject.attrs[
|
265
|
+
it 'returns false with exception details when non json response body (unauthorized)' do # Unauthorized requests return a non-json body. This makes sure we can handle non-json bodies on HTTPError
|
266
|
+
response = double('Response', body: 'totally invalid json', code: 401, message: 'Unauthorized')
|
267
|
+
expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_raise(JIRA::HTTPError.new(response))
|
268
|
+
expect(subject.save('foo' => 'bar')).to be_falsey
|
269
|
+
expect(subject.attrs['exception']['code']).to eq(401)
|
270
|
+
expect(subject.attrs['exception']['message']).to eq('Unauthorized')
|
279
271
|
end
|
280
272
|
end
|
281
273
|
|
282
|
-
describe
|
283
|
-
let(:response) { double
|
274
|
+
describe 'save!' do
|
275
|
+
let(:response) { double }
|
284
276
|
|
285
277
|
subject { JIRA::Resource::Deadbeef.new(client) }
|
286
278
|
|
@@ -288,145 +280,149 @@ describe JIRA::Base do
|
|
288
280
|
expect(subject).to receive(:url).and_return('/foo/bar')
|
289
281
|
end
|
290
282
|
|
291
|
-
it
|
292
|
-
response = instance_double(
|
283
|
+
it 'POSTs a new record' do
|
284
|
+
response = instance_double('Response', body: '{"id":"123"}')
|
293
285
|
allow(subject).to receive(:new_record?) { true }
|
294
|
-
expect(client).to receive(:post).with('/foo/bar','{"foo":"bar"}').and_return(response)
|
295
|
-
expect(subject.save!(
|
296
|
-
expect(subject.id).to eq(
|
286
|
+
expect(client).to receive(:post).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
287
|
+
expect(subject.save!('foo' => 'bar')).to be_truthy
|
288
|
+
expect(subject.id).to eq('123')
|
297
289
|
expect(subject.expanded).to be_falsey
|
298
290
|
end
|
299
291
|
|
300
|
-
it
|
301
|
-
response = instance_double(
|
292
|
+
it 'PUTs an existing record' do
|
293
|
+
response = instance_double('Response', body: nil)
|
302
294
|
allow(subject).to receive(:new_record?) { false }
|
303
|
-
expect(client).to receive(:put).with('/foo/bar','{"foo":"bar"}').and_return(response)
|
304
|
-
expect(subject.save!(
|
295
|
+
expect(client).to receive(:put).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
296
|
+
expect(subject.save!('foo' => 'bar')).to be_truthy
|
305
297
|
expect(subject.expanded).to be_falsey
|
306
298
|
end
|
307
299
|
|
308
|
-
it
|
309
|
-
response = instance_double(
|
300
|
+
it 'throws an exception when an invalid field is set' do
|
301
|
+
response = instance_double('Response', body: '{"errorMessages":["blah"]}', status: 400)
|
310
302
|
allow(subject).to receive(:new_record?) { false }
|
311
|
-
expect(client).to receive(:put).with('/foo/bar','{"invalid_field":"foobar"}').and_raise(JIRA::HTTPError.new(response))
|
312
|
-
expect(
|
303
|
+
expect(client).to receive(:put).with('/foo/bar', '{"invalid_field":"foobar"}').and_raise(JIRA::HTTPError.new(response))
|
304
|
+
expect(-> { subject.save!('invalid_field' => 'foobar') }).to raise_error(JIRA::HTTPError)
|
313
305
|
end
|
314
306
|
end
|
315
307
|
|
316
|
-
describe
|
317
|
-
it
|
318
|
-
subject.attrs = {
|
319
|
-
subject.set_attrs(
|
320
|
-
expect(subject.foo).to eq(
|
308
|
+
describe 'set_attrs' do
|
309
|
+
it 'merges hashes correctly when clobber is true (default)' do
|
310
|
+
subject.attrs = { 'foo' => { 'bar' => 'baz' } }
|
311
|
+
subject.set_attrs('foo' => { 'fum' => 'dum' })
|
312
|
+
expect(subject.foo).to eq('fum' => 'dum')
|
321
313
|
end
|
322
314
|
|
323
|
-
it
|
324
|
-
subject.attrs = {
|
325
|
-
subject.set_attrs({
|
326
|
-
expect(subject.foo).to eq(
|
315
|
+
it 'merges hashes correctly when clobber is false' do
|
316
|
+
subject.attrs = { 'foo' => { 'bar' => 'baz' } }
|
317
|
+
subject.set_attrs({ 'foo' => { 'fum' => 'dum' } }, false)
|
318
|
+
expect(subject.foo).to eq('bar' => 'baz', 'fum' => 'dum')
|
327
319
|
end
|
328
320
|
end
|
329
321
|
|
330
|
-
describe
|
331
|
-
|
322
|
+
describe 'delete' do
|
332
323
|
before(:each) do
|
333
324
|
expect(client).to receive(:delete).with('/foo/bar')
|
334
325
|
allow(subject).to receive(:url) { '/foo/bar' }
|
335
326
|
end
|
336
327
|
|
337
|
-
it
|
328
|
+
it 'flags itself as deleted' do
|
338
329
|
expect(subject.deleted?).to be_falsey
|
339
330
|
subject.delete
|
340
331
|
expect(subject.deleted?).to be_truthy
|
341
332
|
end
|
342
333
|
|
343
|
-
it
|
334
|
+
it 'sends a DELETE request' do
|
344
335
|
subject.delete
|
345
336
|
end
|
346
|
-
|
347
337
|
end
|
348
338
|
|
349
|
-
describe
|
350
|
-
|
351
|
-
it "returns true for new_record? when new object" do
|
339
|
+
describe 'new_record?' do
|
340
|
+
it 'returns true for new_record? when new object' do
|
352
341
|
subject.attrs['id'] = nil
|
353
342
|
expect(subject.new_record?).to be_truthy
|
354
343
|
end
|
355
344
|
|
356
|
-
it
|
345
|
+
it 'returns false for new_record? when id is set' do
|
357
346
|
subject.attrs['id'] = '123'
|
358
347
|
expect(subject.new_record?).to be_falsey
|
359
348
|
end
|
360
|
-
|
361
349
|
end
|
362
350
|
|
363
|
-
describe
|
364
|
-
|
365
|
-
|
366
|
-
attrs["errors"] = {"invalid" => "Field invalid"}
|
351
|
+
describe 'has_errors?' do
|
352
|
+
it 'returns true when the response contains errors' do
|
353
|
+
attrs['errors'] = { 'invalid' => 'Field invalid' }
|
367
354
|
expect(subject.has_errors?).to be_truthy
|
368
355
|
end
|
369
356
|
|
370
|
-
it
|
357
|
+
it 'returns false when the response does not contain any errors' do
|
371
358
|
expect(subject.has_errors?).to be_falsey
|
372
359
|
end
|
373
|
-
|
374
360
|
end
|
375
361
|
|
376
362
|
describe 'url' do
|
377
|
-
|
378
363
|
before(:each) do
|
379
|
-
allow(client).to receive(:options) { {:
|
364
|
+
allow(client).to receive(:options) { { rest_base_path: '/foo/bar' } }
|
380
365
|
end
|
381
366
|
|
382
|
-
it
|
383
|
-
pending("Identified bug on real jira instance")
|
367
|
+
it 'returns self as the URL if set' do
|
384
368
|
attrs['self'] = 'http://foo/bar'
|
385
|
-
expect(subject.url).to eq(
|
369
|
+
expect(subject.url).to eq('http://foo/bar')
|
386
370
|
end
|
387
371
|
|
388
|
-
it
|
372
|
+
it 'returns path as the URL if set and site options is specified' do
|
373
|
+
allow(client).to receive(:options) { { site: 'http://foo' } }
|
374
|
+
attrs['self'] = 'http://foo/bar'
|
375
|
+
expect(subject.url).to eq('/bar')
|
376
|
+
end
|
377
|
+
|
378
|
+
it 'returns path as the URL if set and site options is specified and ends with a slash' do
|
379
|
+
allow(client).to receive(:options) { { site: 'http://foo/' } }
|
380
|
+
attrs['self'] = 'http://foo/bar'
|
381
|
+
expect(subject.url).to eq('/bar')
|
382
|
+
end
|
383
|
+
|
384
|
+
it 'generates the URL from id if self not set' do
|
389
385
|
attrs['self'] = nil
|
390
386
|
attrs['id'] = '98765'
|
391
|
-
expect(subject.url).to eq(
|
387
|
+
expect(subject.url).to eq('/foo/bar/deadbeef/98765')
|
392
388
|
end
|
393
389
|
|
394
|
-
it
|
390
|
+
it 'generates the URL from collection_path if self and id not set' do
|
395
391
|
attrs['self'] = nil
|
396
|
-
attrs['id']
|
397
|
-
expect(subject.url).to eq(
|
392
|
+
attrs['id'] = nil
|
393
|
+
expect(subject.url).to eq('/foo/bar/deadbeef')
|
398
394
|
end
|
399
395
|
|
400
|
-
it
|
401
|
-
expect(JIRA::Resource::Deadbeef.collection_path(client)).to eq(
|
402
|
-
#Should accept an optional prefix (flum in this case)
|
403
|
-
expect(JIRA::Resource::Deadbeef.collection_path(client, '/flum/')).to eq(
|
396
|
+
it 'has a class method for the collection path' do
|
397
|
+
expect(JIRA::Resource::Deadbeef.collection_path(client)).to eq('/foo/bar/deadbeef')
|
398
|
+
# Should accept an optional prefix (flum in this case)
|
399
|
+
expect(JIRA::Resource::Deadbeef.collection_path(client, '/flum/')).to eq('/foo/bar/flum/deadbeef')
|
404
400
|
end
|
405
401
|
|
406
|
-
it
|
407
|
-
expect(JIRA::Resource::Deadbeef.singular_path(client, 'abc123')).to eq(
|
408
|
-
#Should accept an optional prefix (flum in this case)
|
409
|
-
expect(JIRA::Resource::Deadbeef.singular_path(client, 'abc123', '/flum/')).to eq(
|
402
|
+
it 'has a class method for the singular path' do
|
403
|
+
expect(JIRA::Resource::Deadbeef.singular_path(client, 'abc123')).to eq('/foo/bar/deadbeef/abc123')
|
404
|
+
# Should accept an optional prefix (flum in this case)
|
405
|
+
expect(JIRA::Resource::Deadbeef.singular_path(client, 'abc123', '/flum/')).to eq('/foo/bar/flum/deadbeef/abc123')
|
410
406
|
end
|
411
407
|
end
|
412
408
|
|
413
|
-
it
|
409
|
+
it 'returns the formatted attrs from to_s' do
|
414
410
|
subject.attrs['foo'] = 'bar'
|
415
411
|
subject.attrs['dead'] = 'beef'
|
416
412
|
|
417
413
|
expect(subject.to_s).to match(/#<JIRA::Resource::Deadbeef:\d+ @attrs=#{Regexp.quote(attrs.inspect)}>/)
|
418
414
|
end
|
419
415
|
|
420
|
-
it
|
416
|
+
it 'returns the key attribute' do
|
421
417
|
expect(subject.class.key_attribute).to eq(:id)
|
422
418
|
end
|
423
419
|
|
424
|
-
it
|
420
|
+
it 'returns the key value' do
|
425
421
|
subject.attrs['id'] = '123'
|
426
422
|
expect(subject.key_value).to eq('123')
|
427
423
|
end
|
428
424
|
|
429
|
-
it
|
425
|
+
it 'converts to json' do
|
430
426
|
subject.attrs = { 'foo' => 'bar', 'dead' => 'beef' }
|
431
427
|
expect(subject.to_json).to eq(subject.attrs.to_json)
|
432
428
|
|
@@ -435,53 +431,49 @@ describe JIRA::Base do
|
|
435
431
|
expect(h.to_json).to eq(h_attrs.to_json)
|
436
432
|
end
|
437
433
|
|
438
|
-
describe
|
434
|
+
describe 'extract attrs from response' do
|
435
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs: {}) }
|
439
436
|
|
440
|
-
|
437
|
+
it 'sets the attrs from a response' do
|
438
|
+
response = instance_double('Response', body: '{"foo":"bar"}')
|
441
439
|
|
442
|
-
|
443
|
-
|
444
|
-
|
445
|
-
expect(subject.set_attrs_from_response(response)).to eq({'foo' => 'bar'})
|
446
|
-
expect(subject.foo).to eq("bar")
|
440
|
+
expect(subject.set_attrs_from_response(response)).to eq('foo' => 'bar')
|
441
|
+
expect(subject.foo).to eq('bar')
|
447
442
|
end
|
448
443
|
|
449
444
|
it "doesn't clobber existing attrs not in response" do
|
450
|
-
response = instance_double(
|
445
|
+
response = instance_double('Response', body: '{"foo":"bar"}')
|
451
446
|
|
452
|
-
subject.attrs = {'flum' => 'flar'}
|
453
|
-
expect(subject.set_attrs_from_response(response)).to eq(
|
454
|
-
expect(subject.foo).to eq(
|
455
|
-
expect(subject.flum).to eq(
|
447
|
+
subject.attrs = { 'flum' => 'flar' }
|
448
|
+
expect(subject.set_attrs_from_response(response)).to eq('foo' => 'bar')
|
449
|
+
expect(subject.foo).to eq('bar')
|
450
|
+
expect(subject.flum).to eq('flar')
|
456
451
|
end
|
457
452
|
|
458
|
-
it
|
459
|
-
response = instance_double(
|
453
|
+
it 'handles nil response body' do
|
454
|
+
response = instance_double('Response', body: nil)
|
460
455
|
|
461
|
-
subject.attrs = {'flum' => 'flar'}
|
456
|
+
subject.attrs = { 'flum' => 'flar' }
|
462
457
|
expect(subject.set_attrs_from_response(response)).to be_nil
|
463
458
|
expect(subject.flum).to eq('flar')
|
464
459
|
end
|
465
460
|
end
|
466
461
|
|
467
|
-
describe
|
468
|
-
|
469
|
-
it "defaults collection_attributes_are_nested to false" do
|
462
|
+
describe 'nesting' do
|
463
|
+
it 'defaults collection_attributes_are_nested to false' do
|
470
464
|
expect(JIRA::Resource::Deadbeef.collection_attributes_are_nested).to be_falsey
|
471
465
|
end
|
472
466
|
|
473
|
-
it
|
467
|
+
it 'allows collection_attributes_are_nested to be set' do
|
474
468
|
JIRA::Resource::Deadbeef.nested_collections true
|
475
469
|
expect(JIRA::Resource::Deadbeef.collection_attributes_are_nested).to be_truthy
|
476
470
|
end
|
477
|
-
|
478
471
|
end
|
479
472
|
|
480
|
-
describe
|
473
|
+
describe 'has_many' do
|
474
|
+
subject { JIRA::Resource::HasManyExample.new(client, attrs: { 'deadbeefs' => [{ 'id' => '123' }] }) }
|
481
475
|
|
482
|
-
|
483
|
-
|
484
|
-
it "returns a collection of instances for has_many relationships" do
|
476
|
+
it 'returns a collection of instances for has_many relationships' do
|
485
477
|
expect(subject.deadbeefs.class).to eq(JIRA::HasManyProxy)
|
486
478
|
expect(subject.deadbeefs.length).to eq(1)
|
487
479
|
subject.deadbeefs.each do |deadbeef|
|
@@ -489,123 +481,118 @@ describe JIRA::Base do
|
|
489
481
|
end
|
490
482
|
end
|
491
483
|
|
492
|
-
it
|
484
|
+
it 'returns an empty collection for empty has_many relationships' do
|
493
485
|
subject = JIRA::Resource::HasManyExample.new(client)
|
494
486
|
expect(subject.deadbeefs.length).to eq(0)
|
495
487
|
end
|
496
488
|
|
497
|
-
it
|
498
|
-
subject = JIRA::Resource::HasManyExample.new(client, :
|
489
|
+
it 'allows the has_many attributes to be nested inside another attribute' do
|
490
|
+
subject = JIRA::Resource::HasManyExample.new(client, attrs: { 'nested' => { 'brunchmuffins' => [{ 'id' => '123' }, { 'id' => '456' }] } })
|
499
491
|
expect(subject.brunchmuffins.length).to eq(2)
|
500
492
|
subject.brunchmuffins.each do |brunchmuffin|
|
501
493
|
expect(brunchmuffin.class).to eq(JIRA::Resource::Deadbeef)
|
502
494
|
end
|
503
495
|
end
|
504
496
|
|
505
|
-
it
|
506
|
-
subject = JIRA::Resource::HasManyExample.new(client, :
|
507
|
-
|
508
|
-
|
497
|
+
it 'allows it to be deeply nested' do
|
498
|
+
subject = JIRA::Resource::HasManyExample.new(client, attrs: { 'nested' => {
|
499
|
+
'breakfastscone' => { 'breakfastscones' => [{ 'id' => '123' }, { 'id' => '456' }] }
|
500
|
+
} })
|
509
501
|
expect(subject.breakfastscones.length).to eq(2)
|
510
502
|
subject.breakfastscones.each do |breakfastscone|
|
511
503
|
expect(breakfastscone.class).to eq(JIRA::Resource::Deadbeef)
|
512
504
|
end
|
513
505
|
end
|
514
506
|
|
515
|
-
it
|
516
|
-
subject = JIRA::Resource::HasManyExample.new(client, :
|
517
|
-
|
518
|
-
|
507
|
+
it 'short circuits missing deeply nested attrs' do
|
508
|
+
subject = JIRA::Resource::HasManyExample.new(client, attrs: {
|
509
|
+
'nested' => {}
|
510
|
+
})
|
519
511
|
expect(subject.breakfastscones.length).to eq(0)
|
520
512
|
end
|
521
513
|
|
522
|
-
it
|
523
|
-
subject = JIRA::Resource::HasManyExample.new(client, :
|
514
|
+
it 'allows the attribute key to be specified' do
|
515
|
+
subject = JIRA::Resource::HasManyExample.new(client, attrs: { 'irregularlyNamedThings' => [{ 'id' => '123' }, { 'id' => '456' }] })
|
524
516
|
expect(subject.irregularly_named_things.length).to eq(2)
|
525
517
|
subject.irregularly_named_things.each do |thing|
|
526
518
|
expect(thing.class).to eq(JIRA::Resource::Deadbeef)
|
527
519
|
end
|
528
520
|
end
|
529
521
|
|
530
|
-
it
|
522
|
+
it 'can build child instances' do
|
531
523
|
deadbeef = subject.deadbeefs.build
|
532
524
|
expect(deadbeef.class).to eq(JIRA::Resource::Deadbeef)
|
533
525
|
end
|
534
|
-
|
535
526
|
end
|
536
527
|
|
537
|
-
describe
|
528
|
+
describe 'has_one' do
|
529
|
+
subject { JIRA::Resource::HasOneExample.new(client, attrs: { 'deadbeef' => { 'id' => '123' } }) }
|
538
530
|
|
539
|
-
|
540
|
-
|
541
|
-
it "returns an instance for a has one relationship" do
|
531
|
+
it 'returns an instance for a has one relationship' do
|
542
532
|
expect(subject.deadbeef.class).to eq(JIRA::Resource::Deadbeef)
|
543
533
|
expect(subject.deadbeef.id).to eq('123')
|
544
534
|
end
|
545
535
|
|
546
|
-
it
|
536
|
+
it 'returns nil when resource attribute is nonexistent' do
|
547
537
|
subject = JIRA::Resource::HasOneExample.new(client)
|
548
538
|
expect(subject.deadbeef).to be_nil
|
549
539
|
end
|
550
540
|
|
551
|
-
it
|
552
|
-
subject = JIRA::Resource::HasOneExample.new(client, :
|
541
|
+
it 'returns an instance with a different class name to the attribute name' do
|
542
|
+
subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'muffin' => { 'id' => '123' } })
|
553
543
|
expect(subject.muffin.class).to eq(JIRA::Resource::Deadbeef)
|
554
544
|
expect(subject.muffin.id).to eq('123')
|
555
545
|
end
|
556
546
|
|
557
|
-
it
|
558
|
-
subject = JIRA::Resource::HasOneExample.new(client, :
|
547
|
+
it 'allows the has_one attributes to be nested inside another attribute' do
|
548
|
+
subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'nested' => { 'brunchmuffin' => { 'id' => '123' } } })
|
559
549
|
expect(subject.brunchmuffin.class).to eq(JIRA::Resource::Deadbeef)
|
560
550
|
expect(subject.brunchmuffin.id).to eq('123')
|
561
551
|
end
|
562
552
|
|
563
|
-
it
|
564
|
-
subject = JIRA::Resource::HasOneExample.new(client, :
|
565
|
-
|
566
|
-
|
553
|
+
it 'allows it to be deeply nested' do
|
554
|
+
subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'nested' => {
|
555
|
+
'breakfastscone' => { 'breakfastscone' => { 'id' => '123' } }
|
556
|
+
} })
|
567
557
|
expect(subject.breakfastscone.class).to eq(JIRA::Resource::Deadbeef)
|
568
558
|
expect(subject.breakfastscone.id).to eq('123')
|
569
559
|
end
|
570
560
|
|
571
|
-
it
|
572
|
-
subject = JIRA::Resource::HasOneExample.new(client, :
|
561
|
+
it 'allows the attribute key to be specified' do
|
562
|
+
subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'irregularlyNamedThing' => { 'id' => '123' } })
|
573
563
|
expect(subject.irregularly_named_thing.class).to eq(JIRA::Resource::Deadbeef)
|
574
564
|
expect(subject.irregularly_named_thing.id).to eq('123')
|
575
565
|
end
|
576
|
-
|
577
566
|
end
|
578
567
|
|
579
|
-
describe
|
580
|
-
|
568
|
+
describe 'belongs_to' do
|
581
569
|
class JIRA::Resource::BelongsToExample < JIRA::Base
|
582
570
|
belongs_to :deadbeef
|
583
571
|
end
|
584
572
|
|
585
|
-
let(:deadbeef) { JIRA::Resource::Deadbeef.new(client, :
|
573
|
+
let(:deadbeef) { JIRA::Resource::Deadbeef.new(client, attrs: { 'id' => '999' }) }
|
586
574
|
|
587
|
-
subject { JIRA::Resource::BelongsToExample.new(client, :
|
575
|
+
subject { JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' }, deadbeef: deadbeef) }
|
588
576
|
|
589
|
-
it
|
577
|
+
it 'sets up an accessor for the belongs to relationship' do
|
590
578
|
expect(subject.deadbeef).to eq(deadbeef)
|
591
579
|
end
|
592
580
|
|
593
|
-
it
|
581
|
+
it 'raises an exception when initialized without a belongs_to instance' do
|
594
582
|
expect(lambda {
|
595
|
-
JIRA::Resource::BelongsToExample.new(client, :
|
596
|
-
}).to raise_exception(ArgumentError,
|
583
|
+
JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' })
|
584
|
+
}).to raise_exception(ArgumentError, 'Required option :deadbeef missing')
|
597
585
|
end
|
598
586
|
|
599
|
-
it
|
600
|
-
allow(client).to receive(:options) { { :
|
601
|
-
expect(subject.url).to eq(
|
587
|
+
it 'returns the right url' do
|
588
|
+
allow(client).to receive(:options) { { rest_base_path: '/foo' } }
|
589
|
+
expect(subject.url).to eq('/foo/deadbeef/999/belongstoexample/123')
|
602
590
|
end
|
603
591
|
|
604
|
-
it
|
605
|
-
allow(client).to receive(:options) { { :
|
606
|
-
subject = JIRA::Resource::BelongsToExample.new(client, :
|
607
|
-
expect(subject.url).to eq(
|
592
|
+
it 'can be initialized with an instance or a key value' do
|
593
|
+
allow(client).to receive(:options) { { rest_base_path: '/foo' } }
|
594
|
+
subject = JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' }, deadbeef_id: '987')
|
595
|
+
expect(subject.url).to eq('/foo/deadbeef/987/belongstoexample/123')
|
608
596
|
end
|
609
|
-
|
610
597
|
end
|
611
598
|
end
|