sclemmer-jira-ruby 0.1.12

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