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