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.
- 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/lib/jira.rb +33 -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 +80 -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/tasks/generate.rake +18 -0
- data/sclemmer-jira-ruby.gemspec +28 -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 +583 -0
- data/spec/jira/client_spec.rb +188 -0
- data/spec/jira/has_many_proxy_spec.rb +47 -0
- data/spec/jira/http_client_spec.rb +109 -0
- data/spec/jira/http_error_spec.rb +26 -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 +123 -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.post.json +28 -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/field.json +32 -0
- data/spec/mock_responses/field/1.json +15 -0
- data/spec/mock_responses/issue.json +1108 -0
- data/spec/mock_responses/issue.post.json +5 -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/10002/comment.json +65 -0
- data/spec/mock_responses/issue/10002/comment.post.json +29 -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/transitions.json +49 -0
- data/spec/mock_responses/issue/10002/transitions.post.json +1 -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/worklog/10000.json +31 -0
- data/spec/mock_responses/issue/10002/worklog/10000.put.json +30 -0
- data/spec/mock_responses/issuetype.json +42 -0
- data/spec/mock_responses/issuetype/5.json +8 -0
- data/spec/mock_responses/priority.json +42 -0
- data/spec/mock_responses/priority/1.json +8 -0
- data/spec/mock_responses/project.json +12 -0
- data/spec/mock_responses/project/SAMPLEPROJECT.issues.json +1108 -0
- data/spec/mock_responses/project/SAMPLEPROJECT.json +84 -0
- data/spec/mock_responses/status.json +37 -0
- data/spec/mock_responses/status/1.json +7 -0
- data/spec/mock_responses/user_username=admin.json +17 -0
- data/spec/mock_responses/version.post.json +7 -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/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 +194 -0
- 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
|