jira-ruby 3.0.0.beta1 → 3.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/CI.yml +1 -0
  3. data/.github/workflows/codeql.yml +0 -4
  4. data/.gitignore +3 -1
  5. data/.rubocop.yml +5 -70
  6. data/.yardopts +4 -0
  7. data/lib/jira/base.rb +5 -13
  8. data/lib/jira/client.rb +59 -4
  9. data/lib/jira/has_many_proxy.rb +30 -28
  10. data/lib/jira/http_client.rb +64 -1
  11. data/lib/jira/oauth_client.rb +62 -0
  12. data/lib/jira/request_client.rb +26 -1
  13. data/lib/jira/resource/attachment.rb +88 -3
  14. data/lib/jira/resource/field.rb +4 -8
  15. data/lib/jira/resource/issue.rb +80 -11
  16. data/lib/jira/resource/issue_picker_suggestions.rb +1 -1
  17. data/lib/jira/resource/issuelink.rb +4 -3
  18. data/lib/jira/resource/project.rb +1 -1
  19. data/lib/jira/resource/sprint.rb +2 -2
  20. data/lib/jira/resource/watcher.rb +1 -1
  21. data/lib/jira/resource/webhook.rb +5 -1
  22. data/lib/jira/version.rb +1 -1
  23. data/lib/tasks/generate.rake +1 -1
  24. data/spec/integration/issue_spec.rb +2 -2
  25. data/spec/integration/project_spec.rb +2 -2
  26. data/spec/integration/rapidview_spec.rb +3 -3
  27. data/spec/integration/user_spec.rb +12 -3
  28. data/spec/integration/watcher_spec.rb +6 -2
  29. data/spec/integration/{webhook.rb → webhook_spec.rb} +8 -1
  30. data/spec/jira/base_factory_spec.rb +11 -2
  31. data/spec/jira/base_spec.rb +80 -57
  32. data/spec/jira/client_spec.rb +29 -27
  33. data/spec/jira/http_client_spec.rb +2 -2
  34. data/spec/jira/oauth_client_spec.rb +8 -4
  35. data/spec/jira/resource/agile_spec.rb +4 -4
  36. data/spec/jira/resource/attachment_spec.rb +36 -13
  37. data/spec/jira/resource/board_spec.rb +5 -5
  38. data/spec/jira/resource/field_spec.rb +23 -24
  39. data/spec/jira/resource/filter_spec.rb +3 -2
  40. data/spec/jira/resource/issue_spec.rb +103 -81
  41. data/spec/jira/resource/project_spec.rb +8 -8
  42. data/spec/jira/resource/sprint_spec.rb +23 -11
  43. data/spec/jira/resource/status_spec.rb +1 -1
  44. data/spec/jira/resource/user_factory_spec.rb +2 -2
  45. data/spec/jira/resource/worklog_spec.rb +1 -1
  46. data/spec/mock_responses/board/1_issues.json +2 -1
  47. data/spec/mock_responses/issue.json +1 -0
  48. data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.full.json +2 -1
  49. data/spec/mock_responses/rapidview/SAMPLEPROJECT.issues.json +2 -1
  50. data/spec/support/clients_helper.rb +2 -2
  51. data/spec/support/mock_client.rb +9 -0
  52. data/spec/support/mock_response.rb +8 -0
  53. data/spec/support/shared_examples/integration.rb +1 -1
  54. metadata +9 -10
@@ -4,11 +4,13 @@ describe JIRA::Resource::Attachment do
4
4
  subject(:attachment) do
5
5
  described_class.new(
6
6
  client,
7
- issue: JIRA::Resource::Issue.new(client),
8
- attrs: { 'author' => { 'foo' => 'bar' } }
7
+ issue: JIRA::Resource::Issue.new(client, attrs: { 'id' => issue_id }),
8
+ attrs: { 'author' => { 'foo' => 'bar' }, 'id' => attachment_id }
9
9
  )
10
10
  end
11
11
 
12
+ let(:issue_id) { 27_676 }
13
+ let(:attachment_id) { 30_076 }
12
14
  let(:client) do
13
15
  double(
14
16
  'client',
@@ -44,13 +46,17 @@ describe JIRA::Resource::Attachment do
44
46
  )
45
47
  end
46
48
 
47
- it 'returns meta information about attachment upload' do
48
- expect(client).to receive(:get).with('/jira/rest/api/2/attachment/meta').and_return(response)
49
+ context 'when returning meta information' do
50
+ it 'returns meta information about attachment upload' do
51
+ expect(client).to receive(:get).with('/jira/rest/api/2/attachment/meta').and_return(response)
49
52
 
50
- subject
53
+ result = subject
54
+
55
+ expect(result).to be_a(Hash)
56
+ end
51
57
  end
52
58
 
53
- context 'the factory delegates correctly' do
59
+ context 'when the factory delegates correctly' do
54
60
  subject { JIRA::Resource::AttachmentFactory.new(client) }
55
61
 
56
62
  it 'delegates #meta to to target class' do
@@ -59,7 +65,7 @@ describe JIRA::Resource::Attachment do
59
65
  end
60
66
  end
61
67
 
62
- context 'there is an attachment on an issue' do
68
+ context 'when there is an attachment on an issue' do
63
69
  subject(:attachment) do
64
70
  described_class.new(
65
71
  client,
@@ -68,22 +74,26 @@ describe JIRA::Resource::Attachment do
68
74
  )
69
75
  end
70
76
 
77
+ let(:attachment_url) { 'https://localhost:2990/secure/attachment/32323/myfile.txt' }
71
78
  let(:client) do
72
79
  JIRA::Client.new(username: 'username', password: 'password', auth_type: :basic, use_ssl: false)
73
80
  end
74
81
  let(:attachment_file_contents) { 'file contents' }
75
- let(:attachment_url) { 'https://localhost:2990/secure/attachment/32323/myfile.txt' }
82
+ let(:issue_id) { 3232 }
83
+ let(:issue) { JIRA::Resource::Issue.new(client, attrs: { 'id' => issue_id }) }
76
84
 
77
85
  before do
78
86
  stub_request(:get, attachment_url).to_return(body: attachment_file_contents)
79
87
  end
80
88
 
81
89
  describe '.download_file' do
82
- it 'passes file object to block' do
83
- expect(URI).to receive(:parse).with(attachment_url).and_call_original
90
+ context 'when passing file object to block' do
91
+ it 'passes file object to block' do
92
+ expect(URI).to receive(:parse).with(attachment_url).and_call_original
84
93
 
85
- attachment.download_file do |file|
86
- expect(file.read).to eq(attachment_file_contents)
94
+ attachment.download_file do |file|
95
+ expect(file.read).to eq(attachment_file_contents)
96
+ end
87
97
  end
88
98
  end
89
99
  end
@@ -91,6 +101,7 @@ describe JIRA::Resource::Attachment do
91
101
  describe '.download_contents' do
92
102
  it 'downloads the file contents as a string' do
93
103
  expect(URI).to receive(:parse).with(attachment_url).and_call_original
104
+
94
105
  expect(attachment.download_contents).to eq(attachment_file_contents)
95
106
  end
96
107
  end
@@ -115,7 +126,7 @@ describe JIRA::Resource::Attachment do
115
126
  ].to_json
116
127
  )
117
128
  end
118
- let(:issue) { JIRA::Resource::Issue.new(client) }
129
+ let(:issue) { JIRA::Resource::Issue.new(client, attrs: { 'id' => issue_id }) }
119
130
 
120
131
  describe '#save' do
121
132
  subject { attachment.save('file' => path_to_file) }
@@ -125,6 +136,8 @@ describe JIRA::Resource::Attachment do
125
136
  end
126
137
 
127
138
  it 'successfully update the attachment' do
139
+ expect(client).to receive(:post_multipart).and_return(response).with("/jira/rest/api/2/issue/#{issue.id}/attachments/#{attachment.id}", anything, anything)
140
+
128
141
  subject
129
142
 
130
143
  expect(attachment.filename).to eq file_name
@@ -225,4 +238,14 @@ describe JIRA::Resource::Attachment do
225
238
  end
226
239
  end
227
240
  end
241
+
242
+ context 'when an attachment is on an issue' do
243
+ describe '#delete' do
244
+ it 'removes the attachment' do
245
+ expect(client).to receive(:delete).with("/jira/rest/api/2/issue/#{issue_id}/attachments/#{attachment_id}")
246
+
247
+ attachment.delete
248
+ end
249
+ end
250
+ end
228
251
  end
@@ -107,7 +107,7 @@ EOS
107
107
 
108
108
  context 'when there are multiple pages of results' do
109
109
  let(:result_1) do
110
- OpenStruct.new(body: {
110
+ double(body: {
111
111
  'startAt' => 0,
112
112
  'maxResults' => 1,
113
113
  'total' => 2,
@@ -115,7 +115,7 @@ EOS
115
115
  }.to_json)
116
116
  end
117
117
  let(:result_2) do
118
- OpenStruct.new(body: {
118
+ double(body: {
119
119
  'startAt' => 1,
120
120
  'maxResults' => 1,
121
121
  'total' => 2,
@@ -132,7 +132,7 @@ EOS
132
132
 
133
133
  context 'when there is only one page of results' do
134
134
  let(:result_1) do
135
- OpenStruct.new(body: {
135
+ double(body: {
136
136
  'startAt' => 0,
137
137
  'maxResults' => 2,
138
138
  'total' => 2,
@@ -166,7 +166,7 @@ EOS
166
166
  }
167
167
  ]
168
168
  }
169
- EOS
169
+ EOS
170
170
  allow(response).to receive(:body).and_return(api_json)
171
171
  allow(board).to receive(:id).and_return(84)
172
172
  expect(client).to receive(:get).with('/rest/agile/1.0/board/84/sprint?').and_return(response)
@@ -215,7 +215,7 @@ EOS
215
215
  "rankCustomFieldId":10011
216
216
  }
217
217
  }
218
- EOS
218
+ EOS
219
219
  allow(response).to receive(:body).and_return(api_json)
220
220
  allow(board).to receive(:id).and_return(84)
221
221
  expect(client).to receive(:get).with('/rest/agile/1.0/board/84/configuration').and_return(response)
@@ -1,56 +1,55 @@
1
1
  require 'spec_helper'
2
2
 
3
3
  describe JIRA::Resource::Field do
4
- let(:cache) { OpenStruct.new }
5
-
6
4
  let(:client) do
7
5
  client = double(options: { rest_base_path: '/jira/rest/api/2' })
8
6
  field = JIRA::Resource::FieldFactory.new(client)
9
7
  allow(client).to receive(:Field).and_return(field)
10
- allow(client).to receive(:cache).and_return(cache)
8
+ allow(client).to receive(:field_map_cache).and_return(nil)
9
+ allow(client).to receive(:field_map_cache=)
11
10
  # info about all fields on the client
12
11
  allow(client.Field).to receive(:all).and_return([
13
12
  described_class.new(client,
14
13
  attrs: { 'id' => 'customfield_10666', 'name' => 'Priority', 'custom' => true, 'orderable' => true, 'navigable' => true,
15
- 'searchable' => true, 'clauseNames' => ['cf[10666]', 'Priority'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_666 } }),
14
+ 'searchable' => true, 'clauseNames' => ['cf[10666]', 'Priority'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_666 } }),
16
15
  described_class.new(client,
17
16
  attrs: { 'id' => 'issuekey', 'name' => 'Key', 'custom' => false, 'orderable' => false,
18
- 'navigable' => true, 'searchable' => false, 'clauseNames' => %w[id issue issuekey key] }),
17
+ 'navigable' => true, 'searchable' => false, 'clauseNames' => %w[id issue issuekey key] }),
19
18
  described_class.new(client,
20
19
  attrs: { 'id' => 'priority', 'name' => 'Priority', 'custom' => false, 'orderable' => true,
21
- 'navigable' => true, 'searchable' => true, 'clauseNames' => ['priority'], 'schema' => { 'type' => 'priority', 'system' => 'priority' } }),
20
+ 'navigable' => true, 'searchable' => true, 'clauseNames' => ['priority'], 'schema' => { 'type' => 'priority', 'system' => 'priority' } }),
22
21
  described_class.new(client,
23
22
  attrs: { 'id' => 'summary', 'name' => 'Summary', 'custom' => false, 'orderable' => true,
24
- 'navigable' => true, 'searchable' => true, 'clauseNames' => ['summary'], 'schema' => { 'type' => 'string', 'system' => 'summary' } }),
23
+ 'navigable' => true, 'searchable' => true, 'clauseNames' => ['summary'], 'schema' => { 'type' => 'string', 'system' => 'summary' } }),
25
24
  described_class.new(client,
26
25
  attrs: { 'id' => 'issuetype', 'name' => 'Issue Type', 'custom' => false, 'orderable' => true,
27
- 'navigable' => true, 'searchable' => true, 'clauseNames' => %w[issuetype type], 'schema' => { 'type' => 'issuetype', 'system' => 'issuetype' } }),
26
+ 'navigable' => true, 'searchable' => true, 'clauseNames' => %w[issuetype type], 'schema' => { 'type' => 'issuetype', 'system' => 'issuetype' } }),
28
27
  described_class.new(client,
29
28
  attrs: { 'id' => 'customfield_10111', 'name' => 'SingleWord', 'custom' => true, 'orderable' => true,
30
- 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10111]', 'SingleWord'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_111 } }),
29
+ 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10111]', 'SingleWord'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_111 } }),
31
30
  described_class.new(client,
32
31
  attrs: { 'id' => 'customfield_10222', 'name' => 'Multi Word', 'custom' => true, 'orderable' => true,
33
- 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10222]', 'Multi Word'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_222 } }),
32
+ 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10222]', 'Multi Word'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_222 } }),
34
33
  described_class.new(client,
35
34
  attrs: { 'id' => 'customfield_10333', 'name' => 'Why/N@t', 'custom' => true, 'orderable' => true,
36
- 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10333]', 'Why/N@t'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_333 } }),
35
+ 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10333]', 'Why/N@t'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_333 } }),
37
36
  described_class.new(client,
38
37
  attrs: { 'id' => 'customfield_10444', 'name' => 'SingleWord', 'custom' => true, 'orderable' => true,
39
- 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10444]', 'SingleWord'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_444 } })
38
+ 'navigable' => true, 'searchable' => true, 'clauseNames' => ['cf[10444]', 'SingleWord'], 'schema' => { 'type' => 'string', 'custom' => 'com.atlassian.jira.plugin.system.customfieldtypes:select', 'customId' => 10_444 } })
40
39
  ])
41
40
  client
42
41
  end
43
42
 
44
43
  describe 'field_mappings' do
45
- shared_context 'mapped or not' do
44
+ shared_context 'with or without mapping' do
46
45
  subject do
47
46
  described_class.new(client, attrs: {
48
47
  'priority' => 1,
49
- 'customfield_10111' => 'data_in_custom_field',
50
- 'customfield_10222' => 'multi word custom name',
51
- 'customfield_10333' => 'complex custom name',
52
- 'customfield_10444' => 'duplicated custom name',
53
- 'customfield_10666' => 'duplicate of a system name'
48
+ 'customfield_10111' => 'data_in_custom_field',
49
+ 'customfield_10222' => 'multi word custom name',
50
+ 'customfield_10333' => 'complex custom name',
51
+ 'customfield_10444' => 'duplicated custom name',
52
+ 'customfield_10666' => 'duplicate of a system name'
54
53
  })
55
54
  end
56
55
 
@@ -65,12 +64,12 @@ describe JIRA::Resource::Field do
65
64
  it 'is not confused by common attribute keys' do
66
65
  expect { subject.name }.to raise_error(NoMethodError)
67
66
  expect { subject.custom }.to raise_error(NoMethodError)
68
- expect(subject.id).to eq(nil) # picks up ID from the parent -
67
+ expect(subject.id).to be_nil # picks up ID from the parent -
69
68
  end
70
69
  end
71
70
 
72
- context 'before fields are mapped' do
73
- include_context 'mapped or not'
71
+ context 'when fields are not yet mapped' do
72
+ include_context 'with or without mapping'
74
73
 
75
74
  it 'can find a standard field by id' do
76
75
  expect(subject.priority).to eq(1)
@@ -87,12 +86,12 @@ describe JIRA::Resource::Field do
87
86
  it 'is not confused by common attribute keys and raises error' do
88
87
  expect { subject.name }.to raise_error(NoMethodError)
89
88
  expect { subject.custom }.to raise_error(NoMethodError)
90
- expect(subject.id).to eq(nil) # picks up ID from the parent -
89
+ expect(subject.id).to be_nil # picks up ID from the parent -
91
90
  end
92
91
  end
93
92
 
94
- context 'after fields are mapped' do
95
- include_context 'mapped or not'
93
+ context 'when fields have been mapped' do
94
+ include_context 'with or without mapping'
96
95
 
97
96
  it 'warns of duplicate fields' do
98
97
  expect { client.Field.map_fields }.to output(/renaming as Priority_10666/).to_stderr
@@ -28,7 +28,7 @@ describe JIRA::Resource::Filter do
28
28
  owner: jira_user,
29
29
  jql: '"Git Repository" ~ jira-ruby AND status = Resolved',
30
30
  viewUrl: 'https://localhost/secure/IssueNavigator.jspa?mode=hide&requestId=42',
31
- searchUrl: 'https://localhost/rest/api/2/search?jql=%22Git+Repository%22+~+jira-ruby+AND+status+%3D+Resolved',
31
+ searchUrl: 'https://localhost/rest/api/2/search/jql?jql=%22Git+Repository%22+~+jira-ruby+AND+status+%3D+Resolved',
32
32
  favourite: false,
33
33
  sharePermissions: [
34
34
  {
@@ -69,6 +69,7 @@ describe JIRA::Resource::Filter do
69
69
  startAt: 0,
70
70
  maxResults: 50,
71
71
  total: 2,
72
+ isLast: true,
72
73
  issues: [jql_issue]
73
74
  }
74
75
  end
@@ -86,7 +87,7 @@ describe JIRA::Resource::Filter do
86
87
  expect(filter).to be_present
87
88
  allow(client).to receive(:options).and_return(rest_base_path: 'localhost')
88
89
  expect(client).to receive(:get)
89
- .with("localhost/search?jql=#{CGI.escape(filter.jql)}")
90
+ .with("localhost/search/jql?jql=#{CGI.escape(filter.jql)}")
90
91
  .and_return(issue_jql_response)
91
92
  issues = filter.issues
92
93
  expect(issues).to be_an(Array)
@@ -7,7 +7,7 @@ describe JIRA::Resource::Issue do
7
7
  let(:client) do
8
8
  client = double(options: { rest_base_path: '/jira/rest/api/2' })
9
9
  allow(client).to receive(:Field).and_return(JIRA::Resource::FieldFactory.new(client))
10
- allow(client).to receive(:cache).and_return(OpenStruct.new)
10
+ allow(client).to receive(:field_map_cache).and_return(nil)
11
11
  client
12
12
  end
13
13
 
@@ -25,7 +25,7 @@ describe JIRA::Resource::Issue do
25
25
  end
26
26
 
27
27
  it 'responds to key' do
28
- expect(@decorated.respond_to?(:key)).to eq(true)
28
+ expect(@decorated.respond_to?(:key)).to be(true)
29
29
  end
30
30
 
31
31
  it 'does not raise an error' do
@@ -42,16 +42,16 @@ describe JIRA::Resource::Issue do
42
42
  issue = double
43
43
 
44
44
  allow(response).to receive(:body).and_return('{"issues":[{"id":"1","summary":"Bugs Everywhere"}]}')
45
- expect(client).to receive(:get).with('/jira/rest/api/2/search?expand=transitions.fields&maxResults=1000&startAt=0')
45
+ expect(client).to receive(:get).with('/jira/rest/api/2/search/jql?expand=transitions.fields&maxResults=1000&startAt=0')
46
46
  .and_return(response)
47
47
  allow(empty_response).to receive(:body).and_return('{"issues":[]}')
48
- expect(client).to receive(:get).with('/jira/rest/api/2/search?expand=transitions.fields&maxResults=1000&startAt=1')
48
+ expect(client).to receive(:get).with('/jira/rest/api/2/search/jql?expand=transitions.fields&maxResults=1000&startAt=1')
49
49
  .and_return(empty_response)
50
50
 
51
51
  expect(client).to receive(:Issue).and_return(issue)
52
52
  expect(issue).to receive(:build).with({ 'id' => '1', 'summary' => 'Bugs Everywhere' })
53
53
 
54
- issues = described_class.all(client)
54
+ described_class.all(client)
55
55
  end
56
56
 
57
57
  it 'finds an issue by key or id' do
@@ -70,85 +70,107 @@ describe JIRA::Resource::Issue do
70
70
  expect(issue_from_id.attrs).to eq(issue_from_key.attrs)
71
71
  end
72
72
 
73
- it 'searches an issue with a jql query string' do
74
- response = double
75
- issue = double
73
+ describe '.jql' do
74
+ subject { described_class.jql(client, 'foo bar', args) }
76
75
 
77
- allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}')
78
- expect(client).to receive(:get).with('/jira/rest/api/2/search?jql=foo+bar')
79
- .and_return(response)
80
- expect(client).to receive(:Issue).and_return(issue)
81
- expect(issue).to receive(:build).with(%w[key foo]).and_return('')
76
+ let(:args) { {} }
77
+ let(:issue) { double }
78
+ let(:response) { double }
79
+ let(:response_string) { '{"issues": {"key":"foo"}, "isLast": true}' }
82
80
 
83
- expect(described_class.jql(client, 'foo bar')).to eq([''])
84
- end
81
+ before do
82
+ allow(response).to receive(:body).and_return(response_string)
83
+ allow(client).to receive(:Issue).and_return(issue)
84
+ allow(issue).to receive(:build).with(%w[key foo]).and_return('')
85
+ end
85
86
 
86
- it 'searches an issue with a jql query string and fields' do
87
- response = double
88
- issue = double
87
+ it 'searches an issue with a jql query string' do
88
+ expect(client).to receive(:get).with('/jira/rest/api/2/search/jql?jql=foo+bar')
89
+ .and_return(response)
90
+ expect(described_class.jql(client, 'foo bar')).to eq([''])
91
+ end
89
92
 
90
- allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}')
91
- expect(client).to receive(:get)
92
- .with('/jira/rest/api/2/search?jql=foo+bar&fields=foo,bar')
93
- .and_return(response)
94
- expect(client).to receive(:Issue).and_return(issue)
95
- expect(issue).to receive(:build).with(%w[key foo]).and_return('')
93
+ it 'passes thorugh the reconcileIssues parameter' do
94
+ expect(client).to receive(:get)
95
+ .with('/jira/rest/api/2/search/jql?jql=foo+bar&reconcileIssues=true')
96
+ .and_return(response)
96
97
 
97
- expect(described_class.jql(client, 'foo bar', fields: %w[foo bar])).to eq([''])
98
- end
98
+ expect(described_class.jql(client, 'foo bar', reconcile_issues: true)).to eq([''])
99
+ end
99
100
 
100
- it 'searches an issue with a jql query string, start at, and maxResults' do
101
- response = double
102
- issue = double
101
+ it 'searches an issue with a jql query string and fields' do
102
+ expect(client).to receive(:get)
103
+ .with('/jira/rest/api/2/search/jql?jql=foo+bar&fields=foo,bar')
104
+ .and_return(response)
103
105
 
104
- allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}')
105
- expect(client).to receive(:get)
106
- .with('/jira/rest/api/2/search?jql=foo+bar&startAt=1&maxResults=3')
107
- .and_return(response)
108
- expect(client).to receive(:Issue).and_return(issue)
109
- expect(issue).to receive(:build).with(%w[key foo]).and_return('')
106
+ expect(described_class.jql(client, 'foo bar', fields: %w[foo bar])).to eq([''])
107
+ end
110
108
 
111
- expect(described_class.jql(client, 'foo bar', start_at: 1, max_results: 3)).to eq([''])
112
- end
109
+ context 'when maxResults is provided' do
110
+ let(:args) { { max_results: } }
113
111
 
114
- it 'searches an issue with a jql query string and maxResults equals zero and should return the count of tickets' do
115
- response = double
116
- issue = double
112
+ context 'with non-zero' do
113
+ let(:max_results) { 3 }
117
114
 
118
- allow(response).to receive(:body).and_return('{"total": 1, "issues": []}')
119
- expect(client).to receive(:get)
120
- .with('/jira/rest/api/2/search?jql=foo+bar&maxResults=0')
121
- .and_return(response)
115
+ it 'searches an issue with a jql query string and maxResults' do
116
+ expect(client).to receive(:get)
117
+ .with('/jira/rest/api/2/search/jql?jql=foo+bar&maxResults=3')
118
+ .and_return(response)
122
119
 
123
- expect(described_class.jql(client, 'foo bar', max_results: 0)).to eq(1)
124
- end
120
+ expect(subject).to eq([''])
121
+ end
122
+ end
125
123
 
126
- it 'searches an issue with a jql query string and string expand' do
127
- response = double
128
- issue = double
124
+ context 'with zero' do
125
+ let(:response_string) { '{"total": 1, "issues": []}' }
126
+ let(:max_results) { 0 }
129
127
 
130
- allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}')
131
- expect(client).to receive(:get)
132
- .with('/jira/rest/api/2/search?jql=foo+bar&expand=transitions')
133
- .and_return(response)
134
- expect(client).to receive(:Issue).and_return(issue)
135
- expect(issue).to receive(:build).with(%w[key foo]).and_return('')
128
+ it 'searches an issue with a jql query string and should return the count of tickets' do
129
+ expect(client).to receive(:get)
130
+ .with('/jira/rest/api/2/search/jql?jql=foo+bar&maxResults=0')
131
+ .and_return(response)
136
132
 
137
- expect(described_class.jql(client, 'foo bar', expand: 'transitions')).to eq([''])
138
- end
133
+ expect(subject).to eq(1)
134
+ end
135
+ end
136
+ end
139
137
 
140
- it 'searches an issue with a jql query string and array expand' do
141
- response = double
142
- issue = double
138
+ it 'searches an issue with a jql query string and string expand' do
139
+ expect(client).to receive(:get)
140
+ .with('/jira/rest/api/2/search/jql?jql=foo+bar&expand=transitions')
141
+ .and_return(response)
143
142
 
144
- allow(response).to receive(:body).and_return('{"issues": {"key":"foo"}}')
145
- expect(client).to receive(:get)
146
- .with('/jira/rest/api/2/search?jql=foo+bar&expand=transitions')
147
- .and_return(response)
148
- expect(client).to receive(:Issue).and_return(issue)
149
- expect(issue).to receive(:build).with(%w[key foo]).and_return('')
143
+ expect(described_class.jql(client, 'foo bar', expand: 'transitions')).to eq([''])
144
+ end
145
+
146
+ it 'searches an issue with a jql query string and array expand' do
147
+ expect(client).to receive(:get)
148
+ .with('/jira/rest/api/2/search/jql?jql=foo+bar&expand=transitions')
149
+ .and_return(response)
150
+
151
+ expect(described_class.jql(client, 'foo bar', expand: %w[transitions])).to eq([''])
152
+ end
153
+
154
+ context 'when pagination is required' do
155
+ let(:response_string) { '{"issues": [{"key":"foo"}], "isLast": false, "nextPageToken": "abc"}' }
156
+ let(:second_response_string) { '{"issues": [{"key":"bar"}], "isLast": true}' }
150
157
 
151
- expect(described_class.jql(client, 'foo bar', expand: %w[transitions])).to eq([''])
158
+ before do
159
+ allow(issue).to receive(:build).with({ 'key' => 'foo' }).and_return('1')
160
+ allow(issue).to receive(:build).with({ 'key' => 'bar' }).and_return('2')
161
+ end
162
+
163
+ it 'makes multiple requests' do
164
+ expect(client).to receive(:get)
165
+ .with('/jira/rest/api/2/search/jql?jql=foo+bar')
166
+ .and_return(response)
167
+ expect(client).to receive(:get)
168
+ .with('/jira/rest/api/2/search/jql?jql=foo+bar&nextPageToken=abc')
169
+ .and_return(double(body: second_response_string))
170
+
171
+ expect(subject).to eq(%w[1 2])
172
+ end
173
+ end
152
174
  end
153
175
 
154
176
  it 'returns meta data available for editing an issue' do
@@ -175,20 +197,20 @@ describe JIRA::Resource::Issue do
175
197
  subject do
176
198
  described_class.new(client, attrs: {
177
199
  'id' => '123',
178
- 'fields' => {
179
- 'reporter' => { 'foo' => 'bar' },
180
- 'assignee' => { 'foo' => 'bar' },
181
- 'project' => { 'foo' => 'bar' },
182
- 'priority' => { 'foo' => 'bar' },
183
- 'issuetype' => { 'foo' => 'bar' },
184
- 'status' => { 'foo' => 'bar' },
185
- 'resolution' => { 'foo' => 'bar' },
186
- 'components' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }],
187
- 'versions' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }],
188
- 'comment' => { 'comments' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }] },
189
- 'attachment' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }],
190
- 'worklog' => { 'worklogs' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }] }
191
- }
200
+ 'fields' => {
201
+ 'reporter' => { 'foo' => 'bar' },
202
+ 'assignee' => { 'foo' => 'bar' },
203
+ 'project' => { 'foo' => 'bar' },
204
+ 'priority' => { 'foo' => 'bar' },
205
+ 'issuetype' => { 'foo' => 'bar' },
206
+ 'status' => { 'foo' => 'bar' },
207
+ 'resolution' => { 'foo' => 'bar' },
208
+ 'components' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }],
209
+ 'versions' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }],
210
+ 'comment' => { 'comments' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }] },
211
+ 'attachment' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }],
212
+ 'worklog' => { 'worklogs' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }] }
213
+ }
192
214
  })
193
215
  end
194
216
 
@@ -11,8 +11,8 @@ describe JIRA::Resource::Project do
11
11
  subject do
12
12
  described_class.new(client, attrs: {
13
13
  'lead' => { 'foo' => 'bar' },
14
- 'issueTypes' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }],
15
- 'versions' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }]
14
+ 'issueTypes' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }],
15
+ 'versions' => [{ 'foo' => 'bar' }, { 'baz' => 'flum' }]
16
16
  })
17
17
  end
18
18
 
@@ -36,13 +36,13 @@ describe JIRA::Resource::Project do
36
36
  end
37
37
 
38
38
  it 'returns issues' do
39
- response_body = '{"expand":"schema,names","startAt":0,"maxResults":1,"total":1,"issues":[{"expand":"editmeta,renderedFields,transitions,changelog,operations","id":"53062","self":"/rest/api/2/issue/53062","key":"test key","fields":{"summary":"test summary"}}]}' # rubocop:disable Layout/LineLength
39
+ response_body = '{"expand":"schema,names","startAt":0,"maxResults":1,"total":1,"issues":[{"expand":"editmeta,renderedFields,transitions,changelog,operations","id":"53062","self":"/rest/api/2/issue/53062","key":"test key","fields":{"summary":"test summary"}}]}'
40
40
  response = double('response',
41
41
  body: response_body)
42
42
  issue_factory = double('issue factory')
43
43
 
44
44
  expect(client).to receive(:get)
45
- .with('/jira/rest/api/2/search?jql=project%3D%22test%22')
45
+ .with('/jira/rest/api/2/search/jql?jql=project%3D%22test%22')
46
46
  .and_return(response)
47
47
  expect(client).to receive(:Issue).and_return(issue_factory)
48
48
  expect(issue_factory).to receive(:build)
@@ -52,13 +52,13 @@ describe JIRA::Resource::Project do
52
52
 
53
53
  context 'with changelog' do
54
54
  it 'returns issues' do
55
- response_body = '{"expand":"schema,names","startAt":0,"maxResults":1,"total":1,"issues":[{"expand":"editmeta,renderedFields,transitions,changelog,operations","id":"53062","self":"/rest/api/2/issue/53062","key":"test key","fields":{"summary":"test summary"},"changelog":{}}]}' # rubocop:disable Layout/LineLength
55
+ response_body = '{"expand":"schema,names","startAt":0,"maxResults":1,"total":1,"issues":[{"expand":"editmeta,renderedFields,transitions,changelog,operations","id":"53062","self":"/rest/api/2/issue/53062","key":"test key","fields":{"summary":"test summary"},"changelog":{}}]}'
56
56
  response = double('response',
57
57
  body: response_body)
58
58
  issue_factory = double('issue factory')
59
59
 
60
60
  expect(client).to receive(:get)
61
- .with('/jira/rest/api/2/search?jql=project%3D%22test%22&expand=changelog&startAt=1&maxResults=100')
61
+ .with('/jira/rest/api/2/search/jql?jql=project%3D%22test%22&expand=changelog&startAt=1&maxResults=100')
62
62
  .and_return(response)
63
63
  expect(client).to receive(:Issue).and_return(issue_factory)
64
64
  expect(issue_factory).to receive(:build)
@@ -73,7 +73,7 @@ describe JIRA::Resource::Project do
73
73
  let(:project_key) { SecureRandom.hex }
74
74
  let(:response) { double('response', body: '[{}]') }
75
75
 
76
- context 'pagination' do
76
+ context 'with pagination' do
77
77
  before do
78
78
  user_factory = double('user factory')
79
79
  expect(client).to receive(:User).and_return(user_factory)
@@ -113,7 +113,7 @@ describe JIRA::Resource::Project do
113
113
  max_results = rand(1000)
114
114
 
115
115
  expect(client).to receive(:get)
116
- .with("/jira/rest/api/2/user/assignable/search?project=#{project_key}&startAt=#{start_at}&maxResults=#{max_results}") # rubocop:disable Layout/LineLength
116
+ .with("/jira/rest/api/2/user/assignable/search?project=#{project_key}&startAt=#{start_at}&maxResults=#{max_results}")
117
117
  .and_return(response)
118
118
 
119
119
  project.users(start_at:, max_results:)