jira-auto-tool 0.1.1
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/.rspec +3 -0
- data/.rubocop.yml +291 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +132 -0
- data/Guardfile +105 -0
- data/LICENSE.txt +21 -0
- data/README.md +159 -0
- data/Rakefile +20 -0
- data/bin/jira-auto-tool +57 -0
- data/bin/jira-auto-tool.bat +2 -0
- data/bin/setup +8 -0
- data/bin/setup-dev-win.bat +3 -0
- data/cucumber.yml +7 -0
- data/features/align_sprint_time_in_dates.feature +33 -0
- data/features/assign_tickets_to_team_sprints.feature +73 -0
- data/features/cache_boards.feature +24 -0
- data/features/control_http_request_rate_limit.feature +17 -0
- data/features/create_sprints_using_existing_ones_as_reference.feature +79 -0
- data/features/list_boards.feature +12 -0
- data/features/list_project_fields.feature +89 -0
- data/features/list_sprint_prefixes.feature +77 -0
- data/features/quarterly_add_sprints_using_existing_ones_as_a_reference.feature +71 -0
- data/features/quarterly_create_sprints_until_specific_date.feature +75 -0
- data/features/quarterly_rename_sprints.feature +179 -0
- data/features/rename_sprints.feature +203 -0
- data/features/self_documented_command_line.feature +15 -0
- data/features/sprint_filtering.feature +111 -0
- data/features/step_definitions/execution_context_steps.rb +33 -0
- data/features/step_definitions/jira_board_steps.rb +102 -0
- data/features/step_definitions/jira_ticket_steps.rb +63 -0
- data/features/support/10.setup_cucumber.rb +10 -0
- data/features/support/env.rb +25 -0
- data/features/support/hooks.rb +25 -0
- data/features/support/setup_rspec.rb +14 -0
- data/features/support/setup_simplecov.rb +5 -0
- data/features/update_sprint_end_date_and_shift_following_ones.feature +52 -0
- data/lib/jira/auto/tool/board/cache.rb +67 -0
- data/lib/jira/auto/tool/board/unavailable_board.rb +36 -0
- data/lib/jira/auto/tool/board.rb +105 -0
- data/lib/jira/auto/tool/board_controller/options.rb +32 -0
- data/lib/jira/auto/tool/board_controller.rb +88 -0
- data/lib/jira/auto/tool/common_options.rb +37 -0
- data/lib/jira/auto/tool/config/options.rb +19 -0
- data/lib/jira/auto/tool/config.rb +64 -0
- data/lib/jira/auto/tool/fetch_custom_field_options.rb +47 -0
- data/lib/jira/auto/tool/field.rb +59 -0
- data/lib/jira/auto/tool/field_controller.rb +50 -0
- data/lib/jira/auto/tool/field_option.rb +35 -0
- data/lib/jira/auto/tool/get_createmeta_for_project.rb +24 -0
- data/lib/jira/auto/tool/helpers/environment_based_value.rb +96 -0
- data/lib/jira/auto/tool/helpers/option_parser.rb +16 -0
- data/lib/jira/auto/tool/helpers/overridable_time.rb +18 -0
- data/lib/jira/auto/tool/helpers/pagination.rb +50 -0
- data/lib/jira/auto/tool/jira_http_options.rb +20 -0
- data/lib/jira/auto/tool/next_sprint_creator.rb +60 -0
- data/lib/jira/auto/tool/performer/options.rb +76 -0
- data/lib/jira/auto/tool/performer/planning_increment_sprint_creator.rb +42 -0
- data/lib/jira/auto/tool/performer/prefix_sprint_updater.rb +42 -0
- data/lib/jira/auto/tool/performer/quarterly_sprint_renamer/next_name_generator.rb +60 -0
- data/lib/jira/auto/tool/performer/quarterly_sprint_renamer.rb +19 -0
- data/lib/jira/auto/tool/performer/sprint_end_date_updater.rb +55 -0
- data/lib/jira/auto/tool/performer/sprint_renamer/keep_same_name_generator.rb +19 -0
- data/lib/jira/auto/tool/performer/sprint_renamer/next_name_generator.rb +47 -0
- data/lib/jira/auto/tool/performer/sprint_renamer.rb +55 -0
- data/lib/jira/auto/tool/performer/sprint_time_in_dates_aligner.rb +52 -0
- data/lib/jira/auto/tool/project/options.rb +22 -0
- data/lib/jira/auto/tool/project/ticket_fields.rb +70 -0
- data/lib/jira/auto/tool/project.rb +40 -0
- data/lib/jira/auto/tool/rate_limited_jira_client.rb +50 -0
- data/lib/jira/auto/tool/request_builder/field_context_fetcher.rb +78 -0
- data/lib/jira/auto/tool/request_builder/field_option_fetcher.rb +54 -0
- data/lib/jira/auto/tool/request_builder/get.rb +29 -0
- data/lib/jira/auto/tool/request_builder/sprint_creator.rb +112 -0
- data/lib/jira/auto/tool/request_builder/sprint_state_updater.rb +60 -0
- data/lib/jira/auto/tool/request_builder.rb +89 -0
- data/lib/jira/auto/tool/setup_logging.rb +35 -0
- data/lib/jira/auto/tool/sprint/name.rb +105 -0
- data/lib/jira/auto/tool/sprint/prefix.rb +66 -0
- data/lib/jira/auto/tool/sprint.rb +183 -0
- data/lib/jira/auto/tool/sprint_controller/options.rb +61 -0
- data/lib/jira/auto/tool/sprint_controller.rb +152 -0
- data/lib/jira/auto/tool/sprint_state_controller.rb +58 -0
- data/lib/jira/auto/tool/team.rb +23 -0
- data/lib/jira/auto/tool/team_sprint_prefix_mapper/options.rb +27 -0
- data/lib/jira/auto/tool/team_sprint_prefix_mapper.rb +62 -0
- data/lib/jira/auto/tool/team_sprint_ticket_dispatcher.rb +76 -0
- data/lib/jira/auto/tool/ticket.rb +110 -0
- data/lib/jira/auto/tool/until_date.rb +68 -0
- data/lib/jira/auto/tool/version.rb +9 -0
- data/lib/jira/auto/tool.rb +216 -0
- data/sig/jira/sprint/tool.rbs +8 -0
- data/spec/jira/auto/tool/board/cache_spec.rb +179 -0
- data/spec/jira/auto/tool/board/unavailable_board_spec.rb +34 -0
- data/spec/jira/auto/tool/board_controller/options_spec.rb +52 -0
- data/spec/jira/auto/tool/board_controller_spec.rb +154 -0
- data/spec/jira/auto/tool/board_spec.rb +163 -0
- data/spec/jira/auto/tool/common_options_spec.rb +49 -0
- data/spec/jira/auto/tool/config_spec.rb +108 -0
- data/spec/jira/auto/tool/field_controller_spec.rb +121 -0
- data/spec/jira/auto/tool/field_option_spec.rb +42 -0
- data/spec/jira/auto/tool/field_spec.rb +99 -0
- data/spec/jira/auto/tool/helpers/environment_based_value_spec.rb +21 -0
- data/spec/jira/auto/tool/helpers/option_parser_spec.rb +21 -0
- data/spec/jira/auto/tool/helpers/overridable_time_spec.rb +43 -0
- data/spec/jira/auto/tool/helpers/pagination_spec.rb +72 -0
- data/spec/jira/auto/tool/jira_http_options_spec.rb +32 -0
- data/spec/jira/auto/tool/next_sprint_creator_spec.rb +85 -0
- data/spec/jira/auto/tool/performer/option_spec.rb +55 -0
- data/spec/jira/auto/tool/performer/planning_increment_sprint_creator_spec.rb +62 -0
- data/spec/jira/auto/tool/performer/prefix_sprint_updater_spec.rb +35 -0
- data/spec/jira/auto/tool/performer/quarterly_sprint_renamer/next_name_generator_spec.rb +175 -0
- data/spec/jira/auto/tool/performer/quarterly_sprint_renamer_spec.rb +239 -0
- data/spec/jira/auto/tool/performer/sprint_end_date_updater_spec.rb +90 -0
- data/spec/jira/auto/tool/performer/sprint_renamer/keep_same_name_generator_spec.rb +12 -0
- data/spec/jira/auto/tool/performer/sprint_renamer/next_name_generator_spec.rb +129 -0
- data/spec/jira/auto/tool/performer/sprint_renamer_spec.rb +240 -0
- data/spec/jira/auto/tool/performer/sprint_time_in_dates_aligner_spec.rb +132 -0
- data/spec/jira/auto/tool/project/ticket_fields_spec.rb +390 -0
- data/spec/jira/auto/tool/project_spec.rb +31 -0
- data/spec/jira/auto/tool/rate_limited_jira_client_spec.rb +82 -0
- data/spec/jira/auto/tool/request_builder/field_context_fetcher_spec.rb +54 -0
- data/spec/jira/auto/tool/request_builder/field_option_fetcher_spec.rb +64 -0
- data/spec/jira/auto/tool/request_builder/get_spec.rb +40 -0
- data/spec/jira/auto/tool/request_builder/sprint_creator_spec.rb +179 -0
- data/spec/jira/auto/tool/request_builder/sprint_state_updater_spec.rb +31 -0
- data/spec/jira/auto/tool/request_builder_spec.rb +73 -0
- data/spec/jira/auto/tool/sprint/name_spec.rb +101 -0
- data/spec/jira/auto/tool/sprint/prefix_spec.rb +207 -0
- data/spec/jira/auto/tool/sprint_controller_spec.rb +406 -0
- data/spec/jira/auto/tool/sprint_spec.rb +309 -0
- data/spec/jira/auto/tool/team_spec.rb +21 -0
- data/spec/jira/auto/tool/team_sprint_prefix_mapper_spec.rb +97 -0
- data/spec/jira/auto/tool/team_sprint_ticket_dispatcher_spec.rb +232 -0
- data/spec/jira/auto/tool/ticket_spec.rb +116 -0
- data/spec/jira/auto/tool/until_date_spec.rb +80 -0
- data/spec/jira/auto/tool_spec.rb +458 -0
- data/spec/spec_helper.rb +42 -0
- metadata +368 -0
@@ -0,0 +1,40 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jira/auto/tool/request_builder/get"
|
4
|
+
|
5
|
+
module Jira
|
6
|
+
module Auto
|
7
|
+
class Tool
|
8
|
+
class RequestBuilder
|
9
|
+
RSpec.describe Get do
|
10
|
+
let(:jira_client) { instance_double(JIRA::Client) }
|
11
|
+
let(:request_builder) { described_class.new(jira_client) }
|
12
|
+
|
13
|
+
describe "#request_payload" do
|
14
|
+
it "returns nil" do
|
15
|
+
expect(request_builder.request_payload).to be_nil
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#http_verb" do
|
20
|
+
it "returns :get" do
|
21
|
+
expect(request_builder.http_verb).to eq(:get)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#expected_response" do
|
26
|
+
it "returns 200" do
|
27
|
+
expect(request_builder.expected_response).to eq(200)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
describe "#request_headers" do
|
32
|
+
it "returns nil" do
|
33
|
+
expect(request_builder.request_headers).to be_nil
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,179 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rspec"
|
4
|
+
require "jira/auto/tool/request_builder/sprint_state_updater"
|
5
|
+
|
6
|
+
module Jira
|
7
|
+
module Auto
|
8
|
+
class Tool
|
9
|
+
class RequestBuilder
|
10
|
+
RSpec.describe SprintCreator do
|
11
|
+
let(:sprint_creator_instance) { described_class.new(jira_client, 32, attributes) }
|
12
|
+
let(:attributes) { { name: "a_name", start_date: "2024-12-19 13:16 UTC", length_in_days: 14 } }
|
13
|
+
|
14
|
+
let(:jira_client) { instance_spy(JIRA::Client, options: { context_path: :a_context_path }).as_null_object }
|
15
|
+
let(:tool) { instance_double(Tool, jira_client: jira_client) }
|
16
|
+
|
17
|
+
it { expect(sprint_creator_instance.send(:request_path)).to eq("/rest/agile/1.0/sprint") }
|
18
|
+
|
19
|
+
it { expect(sprint_creator_instance.send(:http_verb)).to eq(:post) }
|
20
|
+
|
21
|
+
it { expect(sprint_creator_instance.send(:expected_response)).to eq(201) }
|
22
|
+
|
23
|
+
def get_date(date_string)
|
24
|
+
Time.parse(date_string)
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#start_date" do
|
28
|
+
let(:actual_start_date) { sprint_creator_instance.send(:start_date) }
|
29
|
+
|
30
|
+
context "when no start date is provided" do
|
31
|
+
let(:attributes) { {} }
|
32
|
+
|
33
|
+
it { expect(actual_start_date).to be_nil }
|
34
|
+
end
|
35
|
+
|
36
|
+
context "when a start date is an empty string" do
|
37
|
+
let(:attributes) { { start_date: "" } }
|
38
|
+
|
39
|
+
it { expect(actual_start_date).to be_nil }
|
40
|
+
end
|
41
|
+
|
42
|
+
context "when a start date is provided" do
|
43
|
+
let(:attributes) { { start_date: "2024-12-19 13:16 UTC" } }
|
44
|
+
|
45
|
+
it { expect(actual_start_date).to eq(get_date("2024-12-19 13:16 UTC")) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
describe "#end_date" do
|
50
|
+
let(:actual_end_date) { sprint_creator_instance.send(:end_date) }
|
51
|
+
|
52
|
+
context "when no end date is provided" do
|
53
|
+
let(:attributes) { {} }
|
54
|
+
|
55
|
+
it { expect(actual_end_date).to be_nil }
|
56
|
+
end
|
57
|
+
|
58
|
+
context "when end date is an empty string" do
|
59
|
+
let(:attributes) { { end_date: "" } }
|
60
|
+
|
61
|
+
it { expect(actual_end_date).to be_nil }
|
62
|
+
end
|
63
|
+
|
64
|
+
context "when no end date specified and start date with length in days are provided" do
|
65
|
+
let(:attributes) { { start_date: "2024-12-19 13:16 UTC", length_in_days: 4 } }
|
66
|
+
|
67
|
+
it { expect(actual_end_date).to eq(get_date("2024-12-23 13:16 UTC")) }
|
68
|
+
end
|
69
|
+
|
70
|
+
context "when no end date is an empty string and start date with length in days are provided" do
|
71
|
+
let(:attributes) { { end_date: "", start_date: "2024-12-19 13:16 UTC", length_in_days: 4 } }
|
72
|
+
|
73
|
+
it { expect(actual_end_date).to eq(get_date("2024-12-23 13:16 UTC")) }
|
74
|
+
end
|
75
|
+
|
76
|
+
context "when end date is provided" do
|
77
|
+
let(:attributes) { { end_date: "2024-12-19 13:16 UTC" } }
|
78
|
+
|
79
|
+
it { expect(actual_end_date).to eq(get_date("2024-12-19 13:16 UTC")) }
|
80
|
+
end
|
81
|
+
|
82
|
+
context "when end date and length in days are provided" do
|
83
|
+
let(:attributes) { { end_date: "2024-12-19 13:16 UTC", length_in_days: 7 } }
|
84
|
+
|
85
|
+
it do
|
86
|
+
expect { actual_end_date }
|
87
|
+
.to raise_error(ArgumentError,
|
88
|
+
"Should not provide both :end_date (2024-12-19 13:16:00 UTC) " \
|
89
|
+
"and :length_in_days (7)!")
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
describe "#length_in_days" do
|
95
|
+
let(:length_in_days) { sprint_creator_instance.send(:length_in_days) }
|
96
|
+
|
97
|
+
context "when no :start_date provided" do
|
98
|
+
let(:attributes) { { length_in_days: 5 } }
|
99
|
+
|
100
|
+
it do
|
101
|
+
expect { length_in_days }
|
102
|
+
.to raise_error(ArgumentError, "Should provide :start_date in order to use :length_in_days!")
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
context "when :start_date provided" do
|
107
|
+
let(:start_date_attributes) { { start_date: "2024-12-19 13:16 UTC" } }
|
108
|
+
|
109
|
+
context "when no length in days is provided" do
|
110
|
+
let(:attributes) { start_date_attributes.merge({}) }
|
111
|
+
|
112
|
+
it { expect(length_in_days).to be_nil }
|
113
|
+
end
|
114
|
+
|
115
|
+
context "when length in days is an empty string" do
|
116
|
+
let(:attributes) { start_date_attributes.merge({ length_in_days: "" }) }
|
117
|
+
|
118
|
+
it { expect(length_in_days).to be_nil }
|
119
|
+
end
|
120
|
+
|
121
|
+
context "when length in days is a number" do
|
122
|
+
let(:attributes) { start_date_attributes.merge({ length_in_days: 28 }) }
|
123
|
+
|
124
|
+
it { expect(length_in_days).to eq(28) }
|
125
|
+
end
|
126
|
+
|
127
|
+
context "when length in days is number in a string" do
|
128
|
+
let(:attributes) { start_date_attributes.merge({ length_in_days: "21" }) }
|
129
|
+
|
130
|
+
it { expect(length_in_days).to eq(21) }
|
131
|
+
end
|
132
|
+
|
133
|
+
context "when length in days is improper integer number in a string" do
|
134
|
+
let(:attributes) { start_date_attributes.merge({ length_in_days: "21xx" }) }
|
135
|
+
|
136
|
+
it { expect { length_in_days }.to raise_error(ArgumentError, 'invalid value for Integer(): "21xx"') }
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
it do
|
142
|
+
expect(sprint_creator_instance.send(:request_payload))
|
143
|
+
.to eq({
|
144
|
+
name: "a_name",
|
145
|
+
startDate: "2024-12-19T13:16:00Z",
|
146
|
+
endDate: "2025-01-02T13:16:00Z",
|
147
|
+
originBoardId: 32
|
148
|
+
})
|
149
|
+
end
|
150
|
+
|
151
|
+
describe ".create_sprint" do
|
152
|
+
let(:actual_response) do
|
153
|
+
instance_double(Net::HTTPResponse, code: "201", body: { "id" => 512 }.to_json)
|
154
|
+
end
|
155
|
+
|
156
|
+
let(:actual_sprints) do
|
157
|
+
[
|
158
|
+
instance_spy(JIRA::Resource::Sprint, id: 256),
|
159
|
+
instance_spy(JIRA::Resource::Sprint, id: 512)
|
160
|
+
]
|
161
|
+
end
|
162
|
+
|
163
|
+
it "returns the object corresponding to the created sprint" do
|
164
|
+
allow(jira_client).to receive_messages(send: actual_response)
|
165
|
+
allow(jira_client).to receive_messages(Sprint: actual_sprints)
|
166
|
+
allow(actual_sprints).to receive(:find).with(512).and_return(actual_sprints.last)
|
167
|
+
|
168
|
+
expect(described_class.create_sprint(tool, 32,
|
169
|
+
{ name: "a_name",
|
170
|
+
start_date: "2024-12-19 13:16 UTC",
|
171
|
+
length_in_days: 14 }))
|
172
|
+
.to be_a(Sprint)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rspec"
|
4
|
+
require "jira/auto/tool/request_builder/sprint_state_updater"
|
5
|
+
|
6
|
+
RSpec.describe Jira::Auto::Tool::RequestBuilder::SprintStateUpdater do
|
7
|
+
let(:sprint_creator_instance) { described_class.new(jira_client, sprint: sprint_to_update, new_state: "closed") }
|
8
|
+
|
9
|
+
let(:sprint_to_update) do
|
10
|
+
instance_spy(JIRA::Resource::Sprint, id: 12_345, attrs: { id: 12_345, state: "open" })
|
11
|
+
end
|
12
|
+
|
13
|
+
let(:jira_client) { instance_spy(JIRA::Client, options: { context_path: :a_context_path }).as_null_object }
|
14
|
+
|
15
|
+
it { expect(sprint_creator_instance.send(:request_path)).to eq("/rest/agile/1.0/sprint/12345") }
|
16
|
+
|
17
|
+
it { expect(sprint_creator_instance.send(:http_verb)).to eq(:put) }
|
18
|
+
|
19
|
+
it do
|
20
|
+
expect(sprint_creator_instance.send(:request_payload))
|
21
|
+
.to eq({
|
22
|
+
id: 123_45,
|
23
|
+
self: nil,
|
24
|
+
name: nil,
|
25
|
+
startDate: nil,
|
26
|
+
endDate: nil,
|
27
|
+
originBoardId: nil,
|
28
|
+
state: "closed"
|
29
|
+
})
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "rspec"
|
4
|
+
|
5
|
+
RSpec.describe Jira::Auto::Tool::RequestBuilder do
|
6
|
+
describe "#run" do
|
7
|
+
let(:request_builder) { described_class.new(jira_client) }
|
8
|
+
|
9
|
+
let(:jira_client) do
|
10
|
+
jira_client = instance_spy(JIRA::Client).as_null_object
|
11
|
+
allow(JIRA::Client).to receive_messages(new: jira_client)
|
12
|
+
jira_client
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:expected_response) do
|
16
|
+
instance_double(Net::HTTPResponse, code: 200, body: "sprint updated successfully")
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#context_path" do
|
20
|
+
before do
|
21
|
+
allow(jira_client).to receive_messages(options: { context_path: "/path/to/context" })
|
22
|
+
end
|
23
|
+
|
24
|
+
it { expect(request_builder.send(:context_path)).to eq("/path/to/context") }
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#request_path_with_context" do
|
28
|
+
before do
|
29
|
+
allow(request_builder).to receive_messages(context_path: "/path/to/context", request_path: "/request/path")
|
30
|
+
end
|
31
|
+
|
32
|
+
it { expect(request_builder.send(:request_path_with_context)).to eq("/path/to/context/request/path") }
|
33
|
+
end
|
34
|
+
|
35
|
+
describe "#run" do
|
36
|
+
before do
|
37
|
+
allow(jira_client).to receive_messages(put: expected_response)
|
38
|
+
|
39
|
+
allow(request_builder).to receive_messages(
|
40
|
+
http_verb: :put,
|
41
|
+
context_path: "/path/to/context", request_path: "/request/path",
|
42
|
+
request_payload: { some_payload: "value" },
|
43
|
+
expected_response: 200,
|
44
|
+
error_message_prefix: "Error updating auto state",
|
45
|
+
success_message_prefix: "Sprint state updated successfully"
|
46
|
+
)
|
47
|
+
end
|
48
|
+
|
49
|
+
it "sends the expected request" do
|
50
|
+
request_builder.run
|
51
|
+
|
52
|
+
expect(jira_client).to have_received(:put)
|
53
|
+
.with("/path/to/context/request/path",
|
54
|
+
{ some_payload: "value" }.to_json,
|
55
|
+
{ "Content-Type" => "application/json" })
|
56
|
+
end
|
57
|
+
|
58
|
+
it "returns the response" do
|
59
|
+
allow(request_builder).to receive_messages(send_request: expected_response, expected_response: 200)
|
60
|
+
|
61
|
+
expect(request_builder.run).to eq(expected_response)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
describe ".build_request_args" do
|
66
|
+
it "removes payload as an argument if it is nil" do
|
67
|
+
allow(request_builder).to receive_messages(request_headers: :some_headers)
|
68
|
+
|
69
|
+
expect(request_builder.send(:build_request_args, "/request/path", nil)).to eq(["/request/path", :some_headers])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jira/auto/tool/sprint/name"
|
4
|
+
|
5
|
+
module Jira
|
6
|
+
module Auto
|
7
|
+
class Tool
|
8
|
+
class Sprint
|
9
|
+
RSpec.describe Name do
|
10
|
+
describe ".parse" do
|
11
|
+
let(:parsed_name) { described_class.parse("ART_Team_24.4.5") }
|
12
|
+
|
13
|
+
it { expect(parsed_name.prefix).to eq("ART_Team") }
|
14
|
+
it { expect(parsed_name.year).to eq(24) }
|
15
|
+
it { expect(parsed_name.quarter).to eq(4) }
|
16
|
+
it { expect(parsed_name.index_in_quarter).to eq(5) }
|
17
|
+
it { expect(parsed_name.planning_interval).to eq([24, 4]) }
|
18
|
+
|
19
|
+
it "raise an error if the sprint name is not according to convention" do
|
20
|
+
expect { described_class.parse("name ignoring naming convention") }
|
21
|
+
.to raise_error(Name::NameConventionError,
|
22
|
+
"'name ignoring naming convention': " \
|
23
|
+
"sprint name not matching #{Name::SPRINT_NAME_REGEX}!")
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#next_in_planning_interval" do
|
28
|
+
let(:parsed_name) { described_class.parse("ART_Team_25.2.1") }
|
29
|
+
|
30
|
+
it do
|
31
|
+
expect(parsed_name.next_in_planning_interval)
|
32
|
+
.to eq(described_class.new("ART_Team", 25, 2, 2))
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# rubocop:disable RSpec/PredicateMatcher
|
37
|
+
describe ".respects_naming_convention?" do
|
38
|
+
it "returns true if the sprint name is according to convention" do
|
39
|
+
expect(described_class.respects_naming_convention?("ART_Team_24.4.5"))
|
40
|
+
.to be_truthy
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns false if the sprint name is not according to convention" do
|
44
|
+
expect(described_class.respects_naming_convention?("name ignoring naming convention"))
|
45
|
+
.to be_falsey
|
46
|
+
end
|
47
|
+
end
|
48
|
+
# rubocop:enable RSpec/PredicateMatcher
|
49
|
+
|
50
|
+
describe ".build" do
|
51
|
+
it "builds the expected name" do
|
52
|
+
expect(described_class.build("ART_Team", 25, 2, 3)).to eq("ART_Team_25.2.3")
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
describe "#new_with" do
|
57
|
+
it "builds the expected name" do
|
58
|
+
expect(described_class.new_with("ART_Team", "25.3.4"))
|
59
|
+
.to eq(described_class.new("ART_Team", 25, 3, 4))
|
60
|
+
end
|
61
|
+
|
62
|
+
it "fails if the sprint name is not according to convention" do
|
63
|
+
expect { described_class.new_with("ART_Team", "MALFORMED_SUFFIX") }
|
64
|
+
.to raise_error(
|
65
|
+
Name::NameConventionError,
|
66
|
+
"suffix not following convention 'MALFORMED_SUFFIX': " \
|
67
|
+
"resulting sprint name 'ART_Team_MALFORMED_SUFFIX' not matching #{Name::SPRINT_NAME_REGEX}!"
|
68
|
+
)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
describe "#<=>" do
|
73
|
+
let(:parsed_name) { described_class.parse("ART_Team_24.4.5") }
|
74
|
+
|
75
|
+
it { expect(parsed_name).to be > described_class.parse("ART_Team_24.4.1") }
|
76
|
+
it { expect(parsed_name).to eq described_class.parse("ART_Team_24.4.05") }
|
77
|
+
it { expect(parsed_name).to be < described_class.parse("ART_Team_24.4.10") }
|
78
|
+
|
79
|
+
context "when comparing to a sprint name not according to convention" do
|
80
|
+
it { expect(parsed_name).to be > "1st sprint" }
|
81
|
+
it { expect(parsed_name).to be < "name ignoring naming convention" }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
describe "#comparison_values" do
|
86
|
+
let(:parsed_name) { described_class.parse("ART_Team_24.4.5") }
|
87
|
+
|
88
|
+
it { expect(parsed_name.send(:comparison_values, parsed_name)).to eq(["ART_Team", 24, 4, 5]) }
|
89
|
+
|
90
|
+
context "when sprint name not according to convention" do
|
91
|
+
it {
|
92
|
+
expect(parsed_name.send(:comparison_values,
|
93
|
+
"name not following conventions")).to eq(["name not following conventions"])
|
94
|
+
}
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,207 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "jira/auto/tool/sprint/prefix"
|
4
|
+
require "jira/auto/tool/until_date"
|
5
|
+
|
6
|
+
module Jira
|
7
|
+
module Auto
|
8
|
+
class Tool
|
9
|
+
class Sprint
|
10
|
+
RSpec.describe Prefix do
|
11
|
+
def new_prefix(name, sprints = [])
|
12
|
+
described_class.new(name, sprints)
|
13
|
+
end
|
14
|
+
|
15
|
+
describe "#name" do
|
16
|
+
it "returns the name of the prefix" do
|
17
|
+
expect(new_prefix("ART_Team").name).to eq("ART_Team")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
describe "#sprints" do
|
22
|
+
it "is empty by default" do
|
23
|
+
expect(described_class.new("a_prefix").sprints).to be_empty
|
24
|
+
end
|
25
|
+
|
26
|
+
it "can be specified explicitly at creation" do
|
27
|
+
expect(new_prefix("ART_Another-Team", %w[sprint_1 sprint_2]).sprints)
|
28
|
+
.to eq(%w[sprint_1 sprint_2])
|
29
|
+
end
|
30
|
+
|
31
|
+
it "accepts new sprints" do
|
32
|
+
prefix = new_prefix("ART_Team")
|
33
|
+
prefix << :one_sprint
|
34
|
+
prefix << :another_sprint
|
35
|
+
|
36
|
+
expect(prefix.sprints).to eq(%i[another_sprint one_sprint])
|
37
|
+
end
|
38
|
+
|
39
|
+
context "when looking at the sprint order" do
|
40
|
+
let(:prefix) { new_prefix("ART_Team", %i[sprint_comes_2nd sprint_comes_1st]) }
|
41
|
+
|
42
|
+
it "has sorted sprints" do
|
43
|
+
expect(prefix.sprints).to eq(%i[sprint_comes_1st sprint_comes_2nd])
|
44
|
+
end
|
45
|
+
|
46
|
+
it "stays sorted when new sprints are added" do
|
47
|
+
prefix << :before_2nd_sprint
|
48
|
+
prefix << :before_1st_sprint
|
49
|
+
|
50
|
+
expect(prefix.sprints).to eq(%i[before_1st_sprint before_2nd_sprint sprint_comes_1st sprint_comes_2nd])
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
describe "#to_table_row" do
|
56
|
+
let(:prefix) { new_prefix("Food_Supply", []) }
|
57
|
+
|
58
|
+
let(:last_sprint) { instance_double(Sprint) }
|
59
|
+
|
60
|
+
before { allow(prefix).to receive_messages(last_sprint: last_sprint) }
|
61
|
+
|
62
|
+
it do
|
63
|
+
allow(last_sprint)
|
64
|
+
.to receive_messages(to_table_row:
|
65
|
+
[512, "Food_Supply_24.4.5", 4, "2024-12-27 13:00 UTC", "2024-12-31 13:00 UTC",
|
66
|
+
"Food Supply Team Board", :url_to_suppply_team_board, "FOOD"])
|
67
|
+
|
68
|
+
expect(prefix.to_table_row)
|
69
|
+
.to eq(["Food_Supply", 512, "Food_Supply_24.4.5", 4, "2024-12-27 13:00 UTC", "2024-12-31 13:00 UTC",
|
70
|
+
"Food Supply Team Board", :url_to_suppply_team_board, "FOOD"])
|
71
|
+
end
|
72
|
+
|
73
|
+
it "can exclude the board information" do
|
74
|
+
allow(last_sprint)
|
75
|
+
.to receive(:to_table_row)
|
76
|
+
.with(without_board_information: true)
|
77
|
+
.and_return([512, "Food_Supply_24.4.5", 4, "2024-12-27 13:00 UTC", "2024-12-31 13:00 UTC"])
|
78
|
+
|
79
|
+
expect(prefix.to_table_row(without_board_information: true))
|
80
|
+
.to eq(["Food_Supply", 512, "Food_Supply_24.4.5", 4, "2024-12-27 13:00 UTC", "2024-12-31 13:00 UTC"])
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
describe ".to_table_row_header" do
|
85
|
+
it do
|
86
|
+
allow(Sprint)
|
87
|
+
.to receive_messages(to_table_row_header: ["Id", "Name", "Length In Days", "Start Date", "End Date",
|
88
|
+
"Board Name", "Board UI URL", "Board Project Key"])
|
89
|
+
|
90
|
+
expect(described_class.to_table_row_header)
|
91
|
+
.to eq(["Sprint Prefix", "Last Sprint Id", "Last Sprint Name", "Length In Days",
|
92
|
+
"Start Date", "End Date", "Board Name", "Board UI URL", "Board Project Key"])
|
93
|
+
end
|
94
|
+
|
95
|
+
it "can exclude the board information" do
|
96
|
+
allow(Sprint)
|
97
|
+
.to receive(:to_table_row_header).with(without_board_information: true)
|
98
|
+
.and_return(["Id", "Name", "Length In Days", "Start Date", "End Date"])
|
99
|
+
|
100
|
+
expect(described_class.to_table_row_header(without_board_information: true))
|
101
|
+
.to eq(["Sprint Prefix", "Last Sprint Id", "Last Sprint Name", "Length In Days", "Start Date",
|
102
|
+
"End Date"])
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
describe "#<=>" do
|
107
|
+
let(:art_team_prefix) { new_prefix("ART_Team", %w[sprint_1 sprint_2]) }
|
108
|
+
|
109
|
+
it { expect(art_team_prefix).to eq(new_prefix("ART_Team", %w[sprint_1 sprint_2])) }
|
110
|
+
it { expect(art_team_prefix).to be > new_prefix("ART_Team", %w[sprint_1]) }
|
111
|
+
it { expect(art_team_prefix).to be > new_prefix("ART_Team", %w[p1 p2]) }
|
112
|
+
it { expect(art_team_prefix).to be < new_prefix("ART_Team", %w[x1]) }
|
113
|
+
it { expect(art_team_prefix).to be < new_prefix("GreaterART_Team", []) }
|
114
|
+
it { expect(art_team_prefix).to be > new_prefix("AA_Team", []) }
|
115
|
+
end
|
116
|
+
|
117
|
+
describe "#add_sprint_following_last_one" do
|
118
|
+
context "when unclosed sprint are found" do
|
119
|
+
let(:actual_sprints) do
|
120
|
+
[
|
121
|
+
"1st sprint",
|
122
|
+
"last sprint",
|
123
|
+
"2nd sprint"
|
124
|
+
]
|
125
|
+
end
|
126
|
+
|
127
|
+
let(:prefix) { described_class.new("sprint", actual_sprints) }
|
128
|
+
let(:newly_added_sprint) { instance_double(Sprint, name: "sprint following last sprint") }
|
129
|
+
|
130
|
+
it "add a sprint for the sprint prefixes having at least one unclosed sprint" do
|
131
|
+
allow(NextSprintCreator)
|
132
|
+
.to receive(:create_sprint_following).with("last sprint").and_return(newly_added_sprint)
|
133
|
+
allow(prefix).to receive_messages(:<< => nil)
|
134
|
+
|
135
|
+
prefix.add_sprint_following_last_one
|
136
|
+
|
137
|
+
expect(prefix).to have_received(:<<).with(newly_added_sprint)
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
describe "#quarterly_add_sprints_until" do
|
143
|
+
let(:until_date) { UntilDate.new("2024-05-15") }
|
144
|
+
let(:prefix) { described_class.new("name_prefix") }
|
145
|
+
|
146
|
+
it "adds no sprint if date is already covered" do
|
147
|
+
allow(prefix).to receive_messages(covered?: true, add_sprint_following_last_one: nil)
|
148
|
+
|
149
|
+
prefix.quarterly_add_sprints_until(until_date)
|
150
|
+
|
151
|
+
expect(prefix).not_to have_received(:add_sprint_following_last_one)
|
152
|
+
end
|
153
|
+
|
154
|
+
it "adds sprints until date is covered" do
|
155
|
+
allow(prefix).to receive(:covered?).and_return(false, false, true)
|
156
|
+
allow(prefix).to receive_messages(add_sprint_following_last_one: nil)
|
157
|
+
|
158
|
+
prefix.quarterly_add_sprints_until(until_date)
|
159
|
+
|
160
|
+
expect(prefix).to have_received(:add_sprint_following_last_one).exactly(:twice)
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
describe "#covered?" do
|
165
|
+
let(:last_sprint) do
|
166
|
+
instance_double(Sprint,
|
167
|
+
start_date: Time.parse("2024-05-01 11:00 UTC"),
|
168
|
+
end_date: Time.parse("2024-05-14 11:00 UTC"))
|
169
|
+
end
|
170
|
+
|
171
|
+
let(:prefix) { described_class.new("name_prefix") }
|
172
|
+
|
173
|
+
before { allow(prefix).to receive_messages(last_sprint: last_sprint) }
|
174
|
+
|
175
|
+
it "handles date anterior to last sprint" do
|
176
|
+
expect(prefix_has_covered?("2024-04-30 11:00 UTC")).to be true
|
177
|
+
end
|
178
|
+
|
179
|
+
it "handles date included in the last sprint" do
|
180
|
+
expect(prefix_has_covered?("2024-05-01 11:00 UTC")).to be true
|
181
|
+
end
|
182
|
+
|
183
|
+
it "handles time past to the last sprint end time on the same day" do
|
184
|
+
expect(prefix_has_covered?("2024-05-14 12:00 UTC")).to be false
|
185
|
+
end
|
186
|
+
|
187
|
+
it "handles date past to the last sprint day" do
|
188
|
+
expect(prefix_has_covered?("2024-05-15 11:00 UTC")).to be false
|
189
|
+
end
|
190
|
+
|
191
|
+
def prefix_has_covered?(date_string)
|
192
|
+
prefix.covered?(UntilDate.new(date_string))
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe "#last_sprint" do
|
197
|
+
it "returns the last sprint as per the sprint default sort criteria" do
|
198
|
+
prefix = new_prefix("ART_Team", %w[sprint_2 xx_last_sprint sprint_1 another_sprint1])
|
199
|
+
|
200
|
+
expect(prefix.last_sprint).to eq("xx_last_sprint")
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
207
|
+
end
|