zk-jira-ruby 2.1.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +13 -0
- data/.travis.yml +9 -0
- data/Gemfile +14 -0
- data/Guardfile +14 -0
- data/LICENSE +19 -0
- data/README.md +427 -0
- data/Rakefile +31 -0
- data/example.rb +232 -0
- data/http-basic-example.rb +113 -0
- data/jira-ruby.gemspec +35 -0
- data/lib/jira/base.rb +525 -0
- data/lib/jira/base_factory.rb +46 -0
- data/lib/jira/client.rb +315 -0
- data/lib/jira/has_many_proxy.rb +42 -0
- data/lib/jira/http_client.rb +112 -0
- data/lib/jira/http_error.rb +14 -0
- data/lib/jira/jwt_client.rb +67 -0
- data/lib/jira/oauth_client.rb +114 -0
- data/lib/jira/railtie.rb +10 -0
- data/lib/jira/request_client.rb +31 -0
- data/lib/jira/resource/agile.rb +79 -0
- data/lib/jira/resource/applinks.rb +39 -0
- data/lib/jira/resource/attachment.rb +50 -0
- data/lib/jira/resource/board.rb +91 -0
- data/lib/jira/resource/board_configuration.rb +9 -0
- data/lib/jira/resource/comment.rb +12 -0
- data/lib/jira/resource/component.rb +8 -0
- data/lib/jira/resource/createmeta.rb +44 -0
- data/lib/jira/resource/field.rb +83 -0
- data/lib/jira/resource/filter.rb +15 -0
- data/lib/jira/resource/issue.rb +141 -0
- data/lib/jira/resource/issuelink.rb +20 -0
- data/lib/jira/resource/issuelinktype.rb +14 -0
- data/lib/jira/resource/issuetype.rb +8 -0
- data/lib/jira/resource/priority.rb +8 -0
- data/lib/jira/resource/project.rb +41 -0
- data/lib/jira/resource/rapidview.rb +67 -0
- data/lib/jira/resource/remotelink.rb +26 -0
- data/lib/jira/resource/resolution.rb +8 -0
- data/lib/jira/resource/serverinfo.rb +18 -0
- data/lib/jira/resource/sprint.rb +105 -0
- data/lib/jira/resource/sprint_report.rb +8 -0
- data/lib/jira/resource/status.rb +8 -0
- data/lib/jira/resource/transition.rb +29 -0
- data/lib/jira/resource/user.rb +30 -0
- data/lib/jira/resource/version.rb +66 -0
- data/lib/jira/resource/watcher.rb +35 -0
- data/lib/jira/resource/webhook.rb +37 -0
- data/lib/jira/resource/worklog.rb +14 -0
- data/lib/jira/tasks.rb +0 -0
- data/lib/jira/version.rb +3 -0
- data/lib/jira-ruby.rb +49 -0
- data/lib/tasks/generate.rake +18 -0
- data/spec/integration/attachment_spec.rb +32 -0
- data/spec/integration/comment_spec.rb +52 -0
- data/spec/integration/component_spec.rb +39 -0
- data/spec/integration/field_spec.rb +32 -0
- data/spec/integration/issue_spec.rb +93 -0
- data/spec/integration/issuelinktype_spec.rb +26 -0
- data/spec/integration/issuetype_spec.rb +24 -0
- data/spec/integration/priority_spec.rb +24 -0
- data/spec/integration/project_spec.rb +49 -0
- data/spec/integration/rapidview_spec.rb +74 -0
- data/spec/integration/resolution_spec.rb +26 -0
- data/spec/integration/status_spec.rb +24 -0
- data/spec/integration/transition_spec.rb +49 -0
- data/spec/integration/user_spec.rb +41 -0
- data/spec/integration/version_spec.rb +39 -0
- data/spec/integration/watcher_spec.rb +62 -0
- data/spec/integration/webhook.rb +25 -0
- data/spec/integration/worklog_spec.rb +51 -0
- data/spec/jira/base_factory_spec.rb +45 -0
- data/spec/jira/base_spec.rb +598 -0
- data/spec/jira/client_spec.rb +291 -0
- data/spec/jira/has_many_proxy_spec.rb +46 -0
- data/spec/jira/http_client_spec.rb +328 -0
- data/spec/jira/http_error_spec.rb +24 -0
- data/spec/jira/jwt_uri_builder_spec.rb +59 -0
- data/spec/jira/oauth_client_spec.rb +162 -0
- data/spec/jira/request_client_spec.rb +41 -0
- data/spec/jira/resource/agile_spec.rb +135 -0
- data/spec/jira/resource/attachment_spec.rb +138 -0
- data/spec/jira/resource/board_spec.rb +224 -0
- data/spec/jira/resource/createmeta_spec.rb +258 -0
- data/spec/jira/resource/field_spec.rb +85 -0
- data/spec/jira/resource/filter_spec.rb +97 -0
- data/spec/jira/resource/issue_spec.rb +227 -0
- data/spec/jira/resource/issuelink_spec.rb +14 -0
- data/spec/jira/resource/project_factory_spec.rb +11 -0
- data/spec/jira/resource/project_spec.rb +123 -0
- data/spec/jira/resource/sprint_spec.rb +90 -0
- data/spec/jira/resource/user_factory_spec.rb +31 -0
- data/spec/jira/resource/worklog_spec.rb +22 -0
- data/spec/mock_responses/board/1.json +33 -0
- data/spec/mock_responses/board/1_issues.json +62 -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/empty_issues.json +8 -0
- data/spec/mock_responses/field/1.json +15 -0
- data/spec/mock_responses/field.json +32 -0
- data/spec/mock_responses/issue/10002/attachments/10000.json +20 -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/watchers.json +13 -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/issueLinkType/10000.json +7 -0
- data/spec/mock_responses/issueLinkType.json +25 -0
- data/spec/mock_responses/issuetype/5.json +8 -0
- data/spec/mock_responses/issuetype.json +42 -0
- data/spec/mock_responses/jira/rest/webhooks/1.0/webhook/2.json +11 -0
- data/spec/mock_responses/jira/rest/webhooks/1.0/webhook.json +11 -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/rapidview/SAMPLEPROJECT.issues.full.json +276 -0
- data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.json +111 -0
- data/spec/mock_responses/rapidview/SAMPLEPROJECT.json +6 -0
- data/spec/mock_responses/rapidview.json +10 -0
- data/spec/mock_responses/resolution/1.json +7 -0
- data/spec/mock_responses/resolution.json +15 -0
- data/spec/mock_responses/sprint/1_issues.json +125 -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/mock_responses/webhook/webhook.json +11 -0
- data/spec/mock_responses/webhook.json +11 -0
- data/spec/spec_helper.rb +21 -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 +177 -0
- metadata +491 -0
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Resource::Resolution do
|
4
|
+
with_each_client do |site_url, client|
|
5
|
+
let(:client) { client }
|
6
|
+
let(:site_url) { site_url }
|
7
|
+
|
8
|
+
let(:key) { '1' }
|
9
|
+
|
10
|
+
let(:expected_attributes) do
|
11
|
+
{
|
12
|
+
'self' => 'http://www.example.com/jira/rest/api/2/resolution/1',
|
13
|
+
'id' => key,
|
14
|
+
'name' => 'Fixed',
|
15
|
+
'description' => 'A fix for this issue is checked into the tree and tested.',
|
16
|
+
'iconUrl' => 'http://www.example.com/jira/images/icons/status_resolved.gif'
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
let(:expected_collection_length) { 2 }
|
21
|
+
|
22
|
+
it_should_behave_like 'a resource'
|
23
|
+
it_should_behave_like 'a resource with a collection GET endpoint'
|
24
|
+
it_should_behave_like 'a resource with a singular GET endpoint'
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Resource::Status do
|
4
|
+
with_each_client do |site_url, client|
|
5
|
+
let(:client) { client }
|
6
|
+
let(:site_url) { site_url }
|
7
|
+
|
8
|
+
let(:key) { '1' }
|
9
|
+
|
10
|
+
let(:expected_attributes) do
|
11
|
+
{
|
12
|
+
'self' => 'http://localhost:2990/jira/rest/api/2/status/1',
|
13
|
+
'id' => key,
|
14
|
+
'name' => 'Open'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:expected_collection_length) { 5 }
|
19
|
+
|
20
|
+
it_should_behave_like 'a resource'
|
21
|
+
it_should_behave_like 'a resource with a collection GET endpoint'
|
22
|
+
it_should_behave_like 'a resource with a singular GET endpoint'
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Resource::Transition do
|
4
|
+
with_each_client do |site_url, client|
|
5
|
+
let(:client) { client }
|
6
|
+
let(:site_url) { site_url }
|
7
|
+
|
8
|
+
let(:key) { '10000' }
|
9
|
+
|
10
|
+
let(:target) { JIRA::Resource::Transition.new(client, attrs: { 'id' => '99999' }, issue_id: '10014') }
|
11
|
+
|
12
|
+
let(:belongs_to) do
|
13
|
+
JIRA::Resource::Issue.new(client, attrs: {
|
14
|
+
'id' => '10002',
|
15
|
+
'self' => "#{site_url}/jira/rest/api/2/issue/10002",
|
16
|
+
'fields' => {
|
17
|
+
'comment' => { 'comments' => [] }
|
18
|
+
}
|
19
|
+
})
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:expected_attributes) do
|
23
|
+
{
|
24
|
+
'self' => "#{site_url}/jira/rest/api/2/issue/10002/transition/10000",
|
25
|
+
'id' => key
|
26
|
+
}
|
27
|
+
end
|
28
|
+
|
29
|
+
let(:attributes_for_post) do
|
30
|
+
{
|
31
|
+
'transition' => {
|
32
|
+
'id' => '42'
|
33
|
+
}
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
it_should_behave_like 'a resource'
|
38
|
+
|
39
|
+
describe 'POST endpoint' do
|
40
|
+
it 'saves a new resource' do
|
41
|
+
stub_request(:post, /#{described_class.collection_path(client, prefix)}$/)
|
42
|
+
.with(body: attributes_for_post.to_json)
|
43
|
+
.to_return(status: 200, body: get_mock_from_path(:post))
|
44
|
+
subject = build_receiver.build
|
45
|
+
expect(subject.save(attributes_for_post)).to be_truthy
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Resource::User do
|
4
|
+
with_each_client do |site_url, client|
|
5
|
+
let(:client) { client }
|
6
|
+
let(:site_url) { site_url }
|
7
|
+
|
8
|
+
let(:key) { 'admin' }
|
9
|
+
|
10
|
+
let(:expected_attributes) do
|
11
|
+
{
|
12
|
+
'self' => 'http://localhost:2990/jira/rest/api/2/user?username=admin',
|
13
|
+
'name' => key,
|
14
|
+
'emailAddress' => 'admin@example.com'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
it_should_behave_like 'a resource'
|
19
|
+
it_should_behave_like 'a resource with a singular GET endpoint'
|
20
|
+
|
21
|
+
describe '#all' do
|
22
|
+
let(:client) do
|
23
|
+
client = double(options: { rest_base_path: '/jira/rest/api/2' })
|
24
|
+
allow(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000').and_return(JIRA::Resource::UserFactory.new(client))
|
25
|
+
client
|
26
|
+
end
|
27
|
+
|
28
|
+
before do
|
29
|
+
allow(client).to receive(:get)
|
30
|
+
.with('/rest/api/2/users/search?username=_&maxResults=1000') { OpenStruct.new(body: '["User1"]') }
|
31
|
+
allow(client).to receive_message_chain(:User, :build).with('users') { [] }
|
32
|
+
end
|
33
|
+
|
34
|
+
it 'gets users with maxResults of 1000' do
|
35
|
+
expect(client).to receive(:get).with('/rest/api/2/users/search?username=_&maxResults=1000')
|
36
|
+
expect(client).to receive_message_chain(:User, :build).with('User1')
|
37
|
+
JIRA::Resource::User.all(client)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Resource::Version do
|
4
|
+
with_each_client do |site_url, client|
|
5
|
+
let(:client) { client }
|
6
|
+
let(:site_url) { site_url }
|
7
|
+
|
8
|
+
let(:key) { '10000' }
|
9
|
+
|
10
|
+
let(:expected_attributes) do
|
11
|
+
{
|
12
|
+
'self' => 'http://localhost:2990/jira/rest/api/2/version/10000',
|
13
|
+
'id' => key,
|
14
|
+
'description' => 'Initial version'
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
let(:attributes_for_post) do
|
19
|
+
{ 'name' => '2.0', 'project' => 'SAMPLEPROJECT' }
|
20
|
+
end
|
21
|
+
let(:expected_attributes_from_post) do
|
22
|
+
{ 'id' => '10001', 'name' => '2.0' }
|
23
|
+
end
|
24
|
+
|
25
|
+
let(:attributes_for_put) do
|
26
|
+
{ 'name' => '2.0.0' }
|
27
|
+
end
|
28
|
+
let(:expected_attributes_from_put) do
|
29
|
+
{ 'id' => '10000', 'name' => '2.0.0' }
|
30
|
+
end
|
31
|
+
|
32
|
+
it_should_behave_like 'a resource'
|
33
|
+
it_should_behave_like 'a resource with a singular GET endpoint'
|
34
|
+
it_should_behave_like 'a resource with a DELETE endpoint'
|
35
|
+
it_should_behave_like 'a resource with a POST endpoint'
|
36
|
+
it_should_behave_like 'a resource with a PUT endpoint'
|
37
|
+
it_should_behave_like 'a resource with a PUT endpoint that rejects invalid fields'
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Resource::Watcher do
|
4
|
+
with_each_client do |site_url, client|
|
5
|
+
let(:client) { client }
|
6
|
+
let(:site_url) { site_url }
|
7
|
+
|
8
|
+
let(:target) { JIRA::Resource::Watcher.new(client, attrs: { 'id' => '99999' }, issue_id: '10002') }
|
9
|
+
|
10
|
+
let(:belongs_to) do
|
11
|
+
JIRA::Resource::Issue.new(client, attrs: {
|
12
|
+
'id' => '10002',
|
13
|
+
'fields' => {
|
14
|
+
'comment' => { 'comments' => [] }
|
15
|
+
}
|
16
|
+
})
|
17
|
+
end
|
18
|
+
|
19
|
+
let(:expected_attributes) do
|
20
|
+
{
|
21
|
+
'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002/watchers',
|
22
|
+
"isWatching": false,
|
23
|
+
"watchCount": 1,
|
24
|
+
"watchers": [
|
25
|
+
{
|
26
|
+
"self": 'http://www.example.com/jira/rest/api/2/user?username=admin',
|
27
|
+
"name": 'admin',
|
28
|
+
"displayName": 'admin',
|
29
|
+
"active": false
|
30
|
+
}
|
31
|
+
]
|
32
|
+
}
|
33
|
+
end
|
34
|
+
|
35
|
+
describe 'watchers' do
|
36
|
+
before(:each) do
|
37
|
+
stub_request(:get, site_url + '/jira/rest/api/2/issue/10002')
|
38
|
+
.to_return(status: 200, body: get_mock_response('issue/10002.json'))
|
39
|
+
|
40
|
+
stub_request(:get, site_url + '/jira/rest/api/2/issue/10002/watchers')
|
41
|
+
.to_return(status: 200, body: get_mock_response('issue/10002/watchers.json'))
|
42
|
+
|
43
|
+
stub_request(:post, site_url + '/jira/rest/api/2/issue/10002/watchers')
|
44
|
+
.to_return(status: 204, body: nil)
|
45
|
+
end
|
46
|
+
|
47
|
+
it 'should returns all the watchers' do
|
48
|
+
issue = client.Issue.find('10002')
|
49
|
+
watchers = client.Watcher.all(options = { issue: issue })
|
50
|
+
expect(watchers.length).to eq(1)
|
51
|
+
end
|
52
|
+
|
53
|
+
it 'should add a watcher' do
|
54
|
+
issue = client.Issue.find('10002')
|
55
|
+
watcher = JIRA::Resource::Watcher.new(client, issue: issue)
|
56
|
+
user_id = "tester"
|
57
|
+
watcher.save!(user_id)
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Resource::Webhook do
|
4
|
+
with_each_client do |site_url, client|
|
5
|
+
let(:client) { client }
|
6
|
+
let(:site_url) { site_url }
|
7
|
+
|
8
|
+
let(:key) { '2' }
|
9
|
+
|
10
|
+
let(:expected_attributes) do
|
11
|
+
{ 'name' => 'from API', 'url' => 'http://localhost:3000/webhooks/1', 'excludeBody' => false, 'filters' => { 'issue-related-events-section' => '' }, 'events' => [], 'enabled' => true, 'self' => 'http://localhost:2990/jira/rest/webhooks/1.0/webhook/2', 'lastUpdatedUser' => 'admin', 'lastUpdatedDisplayName' => 'admin', 'lastUpdated' => 1_453_306_520_188 }
|
12
|
+
end
|
13
|
+
|
14
|
+
let(:expected_collection_length) { 1 }
|
15
|
+
|
16
|
+
it_should_behave_like 'a resource'
|
17
|
+
it_should_behave_like 'a resource with a collection GET endpoint'
|
18
|
+
it_should_behave_like 'a resource with a singular GET endpoint'
|
19
|
+
|
20
|
+
it 'returns a collection of components' do
|
21
|
+
stub_request(:get, site_url + described_class.singular_path(client, key))
|
22
|
+
.to_return(status: 200, body: get_mock_response('webhook/webhook.json'))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Resource::Worklog do
|
4
|
+
with_each_client do |site_url, client|
|
5
|
+
let(:client) { client }
|
6
|
+
let(:site_url) { site_url }
|
7
|
+
|
8
|
+
let(:key) { '10000' }
|
9
|
+
|
10
|
+
let(:target) { JIRA::Resource::Worklog.new(client, attrs: { 'id' => '99999' }, issue_id: '54321') }
|
11
|
+
|
12
|
+
let(:expected_collection_length) { 3 }
|
13
|
+
|
14
|
+
let(:belongs_to) do
|
15
|
+
JIRA::Resource::Issue.new(client, attrs: {
|
16
|
+
'id' => '10002', 'fields' => {
|
17
|
+
'comment' => { 'comments' => [] }
|
18
|
+
}
|
19
|
+
})
|
20
|
+
end
|
21
|
+
|
22
|
+
let(:expected_attributes) do
|
23
|
+
{
|
24
|
+
'self' => 'http://localhost:2990/jira/rest/api/2/issue/10002/worklog/10000',
|
25
|
+
'id' => key,
|
26
|
+
'comment' => 'Some epic work.'
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
let(:attributes_for_post) do
|
31
|
+
{ 'timeSpent' => '2d' }
|
32
|
+
end
|
33
|
+
let(:expected_attributes_from_post) do
|
34
|
+
{ 'id' => '10001', 'timeSpent' => '2d' }
|
35
|
+
end
|
36
|
+
|
37
|
+
let(:attributes_for_put) do
|
38
|
+
{ 'timeSpent' => '2d' }
|
39
|
+
end
|
40
|
+
let(:expected_attributes_from_put) do
|
41
|
+
{ 'id' => '10001', 'timeSpent' => '4d' }
|
42
|
+
end
|
43
|
+
|
44
|
+
it_should_behave_like 'a resource'
|
45
|
+
it_should_behave_like 'a resource with a collection GET endpoint'
|
46
|
+
it_should_behave_like 'a resource with a singular GET endpoint'
|
47
|
+
it_should_behave_like 'a resource with a DELETE endpoint'
|
48
|
+
it_should_behave_like 'a resource with a POST endpoint'
|
49
|
+
it_should_behave_like 'a resource with a PUT endpoint'
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::BaseFactory do
|
4
|
+
class JIRA::Resource::FooFactory < JIRA::BaseFactory; end
|
5
|
+
class JIRA::Resource::Foo; end
|
6
|
+
|
7
|
+
let(:client) { double }
|
8
|
+
subject { JIRA::Resource::FooFactory.new(client) }
|
9
|
+
|
10
|
+
it 'initializes correctly' do
|
11
|
+
expect(subject.class).to eq(JIRA::Resource::FooFactory)
|
12
|
+
expect(subject.client).to eq(client)
|
13
|
+
expect(subject.target_class).to eq(JIRA::Resource::Foo)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'proxies all to the target class' do
|
17
|
+
expect(JIRA::Resource::Foo).to receive(:all).with(client)
|
18
|
+
subject.all
|
19
|
+
end
|
20
|
+
|
21
|
+
it 'proxies find to the target class' do
|
22
|
+
expect(JIRA::Resource::Foo).to receive(:find).with(client, 'FOO')
|
23
|
+
subject.find('FOO')
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'returns the target class' do
|
27
|
+
expect(subject.target_class).to eq(JIRA::Resource::Foo)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'proxies build to the target class' do
|
31
|
+
attrs = double
|
32
|
+
expect(JIRA::Resource::Foo).to receive(:build).with(client, attrs)
|
33
|
+
subject.build(attrs)
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'proxies collection path to the target class' do
|
37
|
+
expect(JIRA::Resource::Foo).to receive(:collection_path).with(client)
|
38
|
+
subject.collection_path
|
39
|
+
end
|
40
|
+
|
41
|
+
it 'proxies singular path to the target class' do
|
42
|
+
expect(JIRA::Resource::Foo).to receive(:singular_path).with(client, 'FOO')
|
43
|
+
subject.singular_path('FOO')
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,598 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe JIRA::Base do
|
4
|
+
class JIRADelegation < SimpleDelegator # :nodoc:
|
5
|
+
end
|
6
|
+
|
7
|
+
class JIRA::Resource::Deadbeef < JIRA::Base # :nodoc:
|
8
|
+
end
|
9
|
+
|
10
|
+
class JIRA::Resource::HasOneExample < JIRA::Base # :nodoc:
|
11
|
+
has_one :deadbeef
|
12
|
+
has_one :muffin, class: JIRA::Resource::Deadbeef
|
13
|
+
has_one :brunchmuffin, class: JIRA::Resource::Deadbeef,
|
14
|
+
nested_under: 'nested'
|
15
|
+
has_one :breakfastscone,
|
16
|
+
class: JIRA::Resource::Deadbeef,
|
17
|
+
nested_under: %w[nested breakfastscone]
|
18
|
+
has_one :irregularly_named_thing,
|
19
|
+
class: JIRA::Resource::Deadbeef,
|
20
|
+
attribute_key: 'irregularlyNamedThing'
|
21
|
+
end
|
22
|
+
|
23
|
+
class JIRA::Resource::HasManyExample < JIRA::Base # :nodoc:
|
24
|
+
has_many :deadbeefs
|
25
|
+
has_many :brunchmuffins, class: JIRA::Resource::Deadbeef,
|
26
|
+
nested_under: 'nested'
|
27
|
+
has_many :breakfastscones,
|
28
|
+
class: JIRA::Resource::Deadbeef,
|
29
|
+
nested_under: %w[nested breakfastscone]
|
30
|
+
has_many :irregularly_named_things,
|
31
|
+
class: JIRA::Resource::Deadbeef,
|
32
|
+
attribute_key: 'irregularlyNamedThings'
|
33
|
+
end
|
34
|
+
|
35
|
+
let(:client) { double('client') }
|
36
|
+
let(:attrs) { {} }
|
37
|
+
|
38
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs: attrs) }
|
39
|
+
|
40
|
+
let(:decorated) { JIRADelegation.new(subject) }
|
41
|
+
|
42
|
+
describe '#respond_to?' do
|
43
|
+
describe 'when decorated using SimpleDelegator' do
|
44
|
+
it 'responds to client' do
|
45
|
+
expect(decorated.respond_to?(:client)).to eq(true)
|
46
|
+
end
|
47
|
+
it 'does not raise an error' do
|
48
|
+
expect do
|
49
|
+
decorated.respond_to?(:client)
|
50
|
+
end.not_to raise_error
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
it 'assigns the client and attrs' do
|
56
|
+
expect(subject.client).to eq(client)
|
57
|
+
expect(subject.attrs).to eq(attrs)
|
58
|
+
end
|
59
|
+
|
60
|
+
it 'returns all the deadbeefs' do
|
61
|
+
response = double
|
62
|
+
expect(response).to receive(:body).and_return('[{"self":"http://deadbeef/","id":"98765"}]')
|
63
|
+
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef').and_return(response)
|
64
|
+
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
65
|
+
deadbeefs = JIRA::Resource::Deadbeef.all(client)
|
66
|
+
expect(deadbeefs.length).to eq(1)
|
67
|
+
first = deadbeefs.first
|
68
|
+
expect(first.class).to eq(JIRA::Resource::Deadbeef)
|
69
|
+
expect(first.attrs['self']).to eq('http://deadbeef/')
|
70
|
+
expect(first.attrs['id']).to eq('98765')
|
71
|
+
expect(first.expanded?).to be_falsey
|
72
|
+
end
|
73
|
+
|
74
|
+
it 'finds a deadbeef by id' do
|
75
|
+
response = instance_double('Response', body: '{"self":"http://deadbeef/","id":"98765"}')
|
76
|
+
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765').and_return(response)
|
77
|
+
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
78
|
+
deadbeef = JIRA::Resource::Deadbeef.find(client, '98765')
|
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
|
+
end
|
84
|
+
|
85
|
+
it 'finds a deadbeef containing changelog by id' do
|
86
|
+
response = instance_double(
|
87
|
+
'Response',
|
88
|
+
body: '{"self":"http://deadbeef/","id":"98765","changelog":{"histories":[]}}'
|
89
|
+
)
|
90
|
+
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765?expand=changelog').and_return(response)
|
91
|
+
|
92
|
+
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
93
|
+
|
94
|
+
deadbeef = JIRA::Resource::Deadbeef.find(client, '98765', expand: 'changelog')
|
95
|
+
expect(deadbeef.client).to eq(client)
|
96
|
+
expect(deadbeef.attrs['self']).to eq('http://deadbeef/')
|
97
|
+
expect(deadbeef.attrs['id']).to eq('98765')
|
98
|
+
expect(deadbeef.expanded?).to be_truthy
|
99
|
+
expect(deadbeef.attrs['changelog']['histories']).to eq([])
|
100
|
+
end
|
101
|
+
|
102
|
+
it 'builds a deadbeef' do
|
103
|
+
deadbeef = JIRA::Resource::Deadbeef.build(client, 'id' => '98765')
|
104
|
+
expect(deadbeef.expanded?).to be_falsey
|
105
|
+
|
106
|
+
expect(deadbeef.client).to eq(client)
|
107
|
+
expect(deadbeef.attrs['id']).to eq('98765')
|
108
|
+
end
|
109
|
+
|
110
|
+
it 'returns the endpoint name' do
|
111
|
+
expect(subject.class.endpoint_name).to eq('deadbeef')
|
112
|
+
end
|
113
|
+
|
114
|
+
it 'returns the path_component' do
|
115
|
+
attrs['id'] = '123'
|
116
|
+
expect(subject.path_component).to eq('/deadbeef/123')
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'returns the path component for unsaved instances' do
|
120
|
+
expect(subject.path_component).to eq('/deadbeef')
|
121
|
+
end
|
122
|
+
|
123
|
+
it 'converts to a symbol' do
|
124
|
+
expect(subject.to_sym).to eq(:deadbeef)
|
125
|
+
end
|
126
|
+
|
127
|
+
describe 'collection_path' do
|
128
|
+
before(:each) do
|
129
|
+
expect(client).to receive(:options).and_return(rest_base_path: '/deadbeef/bar')
|
130
|
+
end
|
131
|
+
|
132
|
+
it 'returns the collection_path' do
|
133
|
+
expect(subject.collection_path).to eq('/deadbeef/bar/deadbeef')
|
134
|
+
end
|
135
|
+
|
136
|
+
it 'returns the collection_path with a prefix' do
|
137
|
+
expect(subject.collection_path('/baz/')).to eq('/deadbeef/bar/baz/deadbeef')
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'has a class method that returns the collection_path' do
|
141
|
+
expect(subject.class.collection_path(client)).to eq('/deadbeef/bar/deadbeef')
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
it 'parses json' do
|
146
|
+
expect(described_class.parse_json('{"foo":"bar"}')).to eq('foo' => 'bar')
|
147
|
+
end
|
148
|
+
|
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) }
|
152
|
+
|
153
|
+
it 'responds to each of the top level attribute names' do
|
154
|
+
expect(subject).to respond_to(:foo)
|
155
|
+
expect(subject).to respond_to('flum')
|
156
|
+
expect(subject).to respond_to(:object_id)
|
157
|
+
|
158
|
+
expect(subject.foo).to eq('bar')
|
159
|
+
expect(subject.flum).to eq('goo')
|
160
|
+
|
161
|
+
# Should not override existing method names, but should still allow
|
162
|
+
# access to their values via the attrs[] hash
|
163
|
+
expect(subject.object_id).not_to eq('dummy')
|
164
|
+
expect(subject.attrs['object_id']).to eq('dummy')
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
describe 'fetch' do
|
169
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs: { 'id' => '98765' }) }
|
170
|
+
|
171
|
+
describe 'not cached' do
|
172
|
+
before(:each) do
|
173
|
+
response = instance_double('Response', body: '{"self":"http://deadbeef/","id":"98765"}')
|
174
|
+
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765').and_return(response)
|
175
|
+
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
176
|
+
end
|
177
|
+
|
178
|
+
it 'sets expanded to true after fetch' do
|
179
|
+
expect(subject.expanded?).to be_falsey
|
180
|
+
subject.fetch
|
181
|
+
expect(subject.expanded?).to be_truthy
|
182
|
+
end
|
183
|
+
|
184
|
+
it 'performs a fetch' do
|
185
|
+
expect(subject.expanded?).to be_falsey
|
186
|
+
subject.fetch
|
187
|
+
expect(subject.self).to eq('http://deadbeef/')
|
188
|
+
expect(subject.id).to eq('98765')
|
189
|
+
end
|
190
|
+
|
191
|
+
it 'performs a fetch if already fetched and force flag is true' do
|
192
|
+
subject.expanded = true
|
193
|
+
subject.fetch(true)
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
describe 'cached' do
|
198
|
+
it "doesn't perform a fetch if already fetched" do
|
199
|
+
subject.expanded = true
|
200
|
+
expect(client).not_to receive(:get)
|
201
|
+
subject.fetch
|
202
|
+
end
|
203
|
+
end
|
204
|
+
|
205
|
+
context "with expand parameter 'changelog'" do
|
206
|
+
it "fetchs changelogs '" do
|
207
|
+
response = instance_double(
|
208
|
+
'Response',
|
209
|
+
body: '{"self":"http://deadbeef/","id":"98765","changelog":{"histories":[]}}'
|
210
|
+
)
|
211
|
+
expect(client).to receive(:get).with('/jira/rest/api/2/deadbeef/98765?expand=changelog').and_return(response)
|
212
|
+
|
213
|
+
expect(JIRA::Resource::Deadbeef).to receive(:collection_path).and_return('/jira/rest/api/2/deadbeef')
|
214
|
+
|
215
|
+
subject.fetch(false, expand: 'changelog')
|
216
|
+
|
217
|
+
expect(subject.self).to eq('http://deadbeef/')
|
218
|
+
expect(subject.id).to eq('98765')
|
219
|
+
expect(subject.changelog['histories']).to eq([])
|
220
|
+
end
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
describe 'save' do
|
225
|
+
let(:response) { double }
|
226
|
+
|
227
|
+
subject { JIRA::Resource::Deadbeef.new(client) }
|
228
|
+
|
229
|
+
before(:each) do
|
230
|
+
expect(subject).to receive(:url).and_return('/foo/bar')
|
231
|
+
end
|
232
|
+
|
233
|
+
it 'POSTs a new record' do
|
234
|
+
response = instance_double('Response', body: '{"id":"123"}')
|
235
|
+
allow(subject).to receive(:new_record?) { true }
|
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')
|
239
|
+
expect(subject.expanded).to be_falsey
|
240
|
+
end
|
241
|
+
|
242
|
+
it 'PUTs an existing record' do
|
243
|
+
response = instance_double('Response', body: nil)
|
244
|
+
allow(subject).to receive(:new_record?) { false }
|
245
|
+
expect(client).to receive(:put).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
246
|
+
expect(subject.save('foo' => 'bar')).to be_truthy
|
247
|
+
expect(subject.expanded).to be_falsey
|
248
|
+
end
|
249
|
+
|
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')
|
256
|
+
end
|
257
|
+
|
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)
|
260
|
+
allow(subject).to receive(:new_record?) { false }
|
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
|
263
|
+
end
|
264
|
+
|
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')
|
271
|
+
end
|
272
|
+
end
|
273
|
+
|
274
|
+
describe 'save!' do
|
275
|
+
let(:response) { double }
|
276
|
+
|
277
|
+
subject { JIRA::Resource::Deadbeef.new(client) }
|
278
|
+
|
279
|
+
before(:each) do
|
280
|
+
expect(subject).to receive(:url).and_return('/foo/bar')
|
281
|
+
end
|
282
|
+
|
283
|
+
it 'POSTs a new record' do
|
284
|
+
response = instance_double('Response', body: '{"id":"123"}')
|
285
|
+
allow(subject).to receive(:new_record?) { true }
|
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')
|
289
|
+
expect(subject.expanded).to be_falsey
|
290
|
+
end
|
291
|
+
|
292
|
+
it 'PUTs an existing record' do
|
293
|
+
response = instance_double('Response', body: nil)
|
294
|
+
allow(subject).to receive(:new_record?) { false }
|
295
|
+
expect(client).to receive(:put).with('/foo/bar', '{"foo":"bar"}').and_return(response)
|
296
|
+
expect(subject.save!('foo' => 'bar')).to be_truthy
|
297
|
+
expect(subject.expanded).to be_falsey
|
298
|
+
end
|
299
|
+
|
300
|
+
it 'throws an exception when an invalid field is set' do
|
301
|
+
response = instance_double('Response', body: '{"errorMessages":["blah"]}', status: 400)
|
302
|
+
allow(subject).to receive(:new_record?) { false }
|
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)
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
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')
|
313
|
+
end
|
314
|
+
|
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')
|
319
|
+
end
|
320
|
+
end
|
321
|
+
|
322
|
+
describe 'delete' do
|
323
|
+
before(:each) do
|
324
|
+
expect(client).to receive(:delete).with('/foo/bar')
|
325
|
+
allow(subject).to receive(:url) { '/foo/bar' }
|
326
|
+
end
|
327
|
+
|
328
|
+
it 'flags itself as deleted' do
|
329
|
+
expect(subject.deleted?).to be_falsey
|
330
|
+
subject.delete
|
331
|
+
expect(subject.deleted?).to be_truthy
|
332
|
+
end
|
333
|
+
|
334
|
+
it 'sends a DELETE request' do
|
335
|
+
subject.delete
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
describe 'new_record?' do
|
340
|
+
it 'returns true for new_record? when new object' do
|
341
|
+
subject.attrs['id'] = nil
|
342
|
+
expect(subject.new_record?).to be_truthy
|
343
|
+
end
|
344
|
+
|
345
|
+
it 'returns false for new_record? when id is set' do
|
346
|
+
subject.attrs['id'] = '123'
|
347
|
+
expect(subject.new_record?).to be_falsey
|
348
|
+
end
|
349
|
+
end
|
350
|
+
|
351
|
+
describe 'has_errors?' do
|
352
|
+
it 'returns true when the response contains errors' do
|
353
|
+
attrs['errors'] = { 'invalid' => 'Field invalid' }
|
354
|
+
expect(subject.has_errors?).to be_truthy
|
355
|
+
end
|
356
|
+
|
357
|
+
it 'returns false when the response does not contain any errors' do
|
358
|
+
expect(subject.has_errors?).to be_falsey
|
359
|
+
end
|
360
|
+
end
|
361
|
+
|
362
|
+
describe 'url' do
|
363
|
+
before(:each) do
|
364
|
+
allow(client).to receive(:options) { { rest_base_path: '/foo/bar' } }
|
365
|
+
end
|
366
|
+
|
367
|
+
it 'returns self as the URL if set' do
|
368
|
+
attrs['self'] = 'http://foo/bar'
|
369
|
+
expect(subject.url).to eq('http://foo/bar')
|
370
|
+
end
|
371
|
+
|
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
|
385
|
+
attrs['self'] = nil
|
386
|
+
attrs['id'] = '98765'
|
387
|
+
expect(subject.url).to eq('/foo/bar/deadbeef/98765')
|
388
|
+
end
|
389
|
+
|
390
|
+
it 'generates the URL from collection_path if self and id not set' do
|
391
|
+
attrs['self'] = nil
|
392
|
+
attrs['id'] = nil
|
393
|
+
expect(subject.url).to eq('/foo/bar/deadbeef')
|
394
|
+
end
|
395
|
+
|
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')
|
400
|
+
end
|
401
|
+
|
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')
|
406
|
+
end
|
407
|
+
end
|
408
|
+
|
409
|
+
it 'returns the formatted attrs from to_s' do
|
410
|
+
subject.attrs['foo'] = 'bar'
|
411
|
+
subject.attrs['dead'] = 'beef'
|
412
|
+
|
413
|
+
expect(subject.to_s).to match(/#<JIRA::Resource::Deadbeef:\d+ @attrs=#{Regexp.quote(attrs.inspect)}>/)
|
414
|
+
end
|
415
|
+
|
416
|
+
it 'returns the key attribute' do
|
417
|
+
expect(subject.class.key_attribute).to eq(:id)
|
418
|
+
end
|
419
|
+
|
420
|
+
it 'returns the key value' do
|
421
|
+
subject.attrs['id'] = '123'
|
422
|
+
expect(subject.key_value).to eq('123')
|
423
|
+
end
|
424
|
+
|
425
|
+
it 'converts to json' do
|
426
|
+
subject.attrs = { 'foo' => 'bar', 'dead' => 'beef' }
|
427
|
+
expect(subject.to_json).to eq(subject.attrs.to_json)
|
428
|
+
|
429
|
+
h = { 'key' => subject }
|
430
|
+
h_attrs = { 'key' => subject.attrs }
|
431
|
+
expect(h.to_json).to eq(h_attrs.to_json)
|
432
|
+
end
|
433
|
+
|
434
|
+
describe 'extract attrs from response' do
|
435
|
+
subject { JIRA::Resource::Deadbeef.new(client, attrs: {}) }
|
436
|
+
|
437
|
+
it 'sets the attrs from a response' do
|
438
|
+
response = instance_double('Response', body: '{"foo":"bar"}')
|
439
|
+
|
440
|
+
expect(subject.set_attrs_from_response(response)).to eq('foo' => 'bar')
|
441
|
+
expect(subject.foo).to eq('bar')
|
442
|
+
end
|
443
|
+
|
444
|
+
it "doesn't clobber existing attrs not in response" do
|
445
|
+
response = instance_double('Response', body: '{"foo":"bar"}')
|
446
|
+
|
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')
|
451
|
+
end
|
452
|
+
|
453
|
+
it 'handles nil response body' do
|
454
|
+
response = instance_double('Response', body: nil)
|
455
|
+
|
456
|
+
subject.attrs = { 'flum' => 'flar' }
|
457
|
+
expect(subject.set_attrs_from_response(response)).to be_nil
|
458
|
+
expect(subject.flum).to eq('flar')
|
459
|
+
end
|
460
|
+
end
|
461
|
+
|
462
|
+
describe 'nesting' do
|
463
|
+
it 'defaults collection_attributes_are_nested to false' do
|
464
|
+
expect(JIRA::Resource::Deadbeef.collection_attributes_are_nested).to be_falsey
|
465
|
+
end
|
466
|
+
|
467
|
+
it 'allows collection_attributes_are_nested to be set' do
|
468
|
+
JIRA::Resource::Deadbeef.nested_collections true
|
469
|
+
expect(JIRA::Resource::Deadbeef.collection_attributes_are_nested).to be_truthy
|
470
|
+
end
|
471
|
+
end
|
472
|
+
|
473
|
+
describe 'has_many' do
|
474
|
+
subject { JIRA::Resource::HasManyExample.new(client, attrs: { 'deadbeefs' => [{ 'id' => '123' }] }) }
|
475
|
+
|
476
|
+
it 'returns a collection of instances for has_many relationships' do
|
477
|
+
expect(subject.deadbeefs.class).to eq(JIRA::HasManyProxy)
|
478
|
+
expect(subject.deadbeefs.length).to eq(1)
|
479
|
+
subject.deadbeefs.each do |deadbeef|
|
480
|
+
expect(deadbeef.class).to eq(JIRA::Resource::Deadbeef)
|
481
|
+
end
|
482
|
+
end
|
483
|
+
|
484
|
+
it 'returns an empty collection for empty has_many relationships' do
|
485
|
+
subject = JIRA::Resource::HasManyExample.new(client)
|
486
|
+
expect(subject.deadbeefs.length).to eq(0)
|
487
|
+
end
|
488
|
+
|
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' }] } })
|
491
|
+
expect(subject.brunchmuffins.length).to eq(2)
|
492
|
+
subject.brunchmuffins.each do |brunchmuffin|
|
493
|
+
expect(brunchmuffin.class).to eq(JIRA::Resource::Deadbeef)
|
494
|
+
end
|
495
|
+
end
|
496
|
+
|
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
|
+
} })
|
501
|
+
expect(subject.breakfastscones.length).to eq(2)
|
502
|
+
subject.breakfastscones.each do |breakfastscone|
|
503
|
+
expect(breakfastscone.class).to eq(JIRA::Resource::Deadbeef)
|
504
|
+
end
|
505
|
+
end
|
506
|
+
|
507
|
+
it 'short circuits missing deeply nested attrs' do
|
508
|
+
subject = JIRA::Resource::HasManyExample.new(client, attrs: {
|
509
|
+
'nested' => {}
|
510
|
+
})
|
511
|
+
expect(subject.breakfastscones.length).to eq(0)
|
512
|
+
end
|
513
|
+
|
514
|
+
it 'allows the attribute key to be specified' do
|
515
|
+
subject = JIRA::Resource::HasManyExample.new(client, attrs: { 'irregularlyNamedThings' => [{ 'id' => '123' }, { 'id' => '456' }] })
|
516
|
+
expect(subject.irregularly_named_things.length).to eq(2)
|
517
|
+
subject.irregularly_named_things.each do |thing|
|
518
|
+
expect(thing.class).to eq(JIRA::Resource::Deadbeef)
|
519
|
+
end
|
520
|
+
end
|
521
|
+
|
522
|
+
it 'can build child instances' do
|
523
|
+
deadbeef = subject.deadbeefs.build
|
524
|
+
expect(deadbeef.class).to eq(JIRA::Resource::Deadbeef)
|
525
|
+
end
|
526
|
+
end
|
527
|
+
|
528
|
+
describe 'has_one' do
|
529
|
+
subject { JIRA::Resource::HasOneExample.new(client, attrs: { 'deadbeef' => { 'id' => '123' } }) }
|
530
|
+
|
531
|
+
it 'returns an instance for a has one relationship' do
|
532
|
+
expect(subject.deadbeef.class).to eq(JIRA::Resource::Deadbeef)
|
533
|
+
expect(subject.deadbeef.id).to eq('123')
|
534
|
+
end
|
535
|
+
|
536
|
+
it 'returns nil when resource attribute is nonexistent' do
|
537
|
+
subject = JIRA::Resource::HasOneExample.new(client)
|
538
|
+
expect(subject.deadbeef).to be_nil
|
539
|
+
end
|
540
|
+
|
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' } })
|
543
|
+
expect(subject.muffin.class).to eq(JIRA::Resource::Deadbeef)
|
544
|
+
expect(subject.muffin.id).to eq('123')
|
545
|
+
end
|
546
|
+
|
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' } } })
|
549
|
+
expect(subject.brunchmuffin.class).to eq(JIRA::Resource::Deadbeef)
|
550
|
+
expect(subject.brunchmuffin.id).to eq('123')
|
551
|
+
end
|
552
|
+
|
553
|
+
it 'allows it to be deeply nested' do
|
554
|
+
subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'nested' => {
|
555
|
+
'breakfastscone' => { 'breakfastscone' => { 'id' => '123' } }
|
556
|
+
} })
|
557
|
+
expect(subject.breakfastscone.class).to eq(JIRA::Resource::Deadbeef)
|
558
|
+
expect(subject.breakfastscone.id).to eq('123')
|
559
|
+
end
|
560
|
+
|
561
|
+
it 'allows the attribute key to be specified' do
|
562
|
+
subject = JIRA::Resource::HasOneExample.new(client, attrs: { 'irregularlyNamedThing' => { 'id' => '123' } })
|
563
|
+
expect(subject.irregularly_named_thing.class).to eq(JIRA::Resource::Deadbeef)
|
564
|
+
expect(subject.irregularly_named_thing.id).to eq('123')
|
565
|
+
end
|
566
|
+
end
|
567
|
+
|
568
|
+
describe 'belongs_to' do
|
569
|
+
class JIRA::Resource::BelongsToExample < JIRA::Base
|
570
|
+
belongs_to :deadbeef
|
571
|
+
end
|
572
|
+
|
573
|
+
let(:deadbeef) { JIRA::Resource::Deadbeef.new(client, attrs: { 'id' => '999' }) }
|
574
|
+
|
575
|
+
subject { JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' }, deadbeef: deadbeef) }
|
576
|
+
|
577
|
+
it 'sets up an accessor for the belongs to relationship' do
|
578
|
+
expect(subject.deadbeef).to eq(deadbeef)
|
579
|
+
end
|
580
|
+
|
581
|
+
it 'raises an exception when initialized without a belongs_to instance' do
|
582
|
+
expect(lambda {
|
583
|
+
JIRA::Resource::BelongsToExample.new(client, attrs: { 'id' => '123' })
|
584
|
+
}).to raise_exception(ArgumentError, 'Required option :deadbeef missing')
|
585
|
+
end
|
586
|
+
|
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')
|
590
|
+
end
|
591
|
+
|
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')
|
596
|
+
end
|
597
|
+
end
|
598
|
+
end
|