ticket-replicator 1.1.0 → 1.2.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.
@@ -2,300 +2,340 @@
2
2
 
3
3
  require "ticket/replicator/row_transformer"
4
4
 
5
- RSpec.describe Ticket::Replicator::RowTransformer do
6
- let(:transformer) { described_class.send(:new, extracted_row) }
5
+ module Ticket
6
+ class Replicator
7
+ # rubocop:disable Metrics/ClassLength
8
+ class RowTransformer
9
+ RSpec.describe RowTransformer do
10
+ let(:transformer) { described_class.send(:new, extracted_row) }
7
11
 
8
- describe ".run_on" do
9
- let(:extracted_row) { :a_row_to_transform }
12
+ describe ".run_on" do
13
+ let(:extracted_row) { :a_row_to_transform }
10
14
 
11
- it "transforms the given file" do
12
- expect(described_class).to receive(:new).with(:a_row_to_transform).and_return(transformer)
15
+ it "transforms the given file" do
16
+ expect(described_class).to receive(:new).with(:a_row_to_transform).and_return(transformer)
13
17
 
14
- expect(transformer).to receive(:run)
18
+ expect(transformer).to receive(:run)
15
19
 
16
- described_class.run_on(:a_row_to_transform)
17
- end
18
- end
20
+ described_class.run_on(:a_row_to_transform)
21
+ end
22
+ end
19
23
 
20
- describe "#run" do
21
- let(:extracted_row) do
22
- {
23
- id: "123",
24
- status: "Open",
25
- priority: "4 - Low",
26
- team: "Source Team",
27
- summary: "summary"
28
- }
29
- end
24
+ describe "#run" do
25
+ let(:extracted_row) do
26
+ {
27
+ id: "123",
28
+ status: "Open",
29
+ priority: "4 - Low",
30
+ team: "Source Team",
31
+ summary: "summary"
32
+ }
33
+ end
30
34
 
31
- it "transforms a row" do
32
- allow(transformer).to receive(:transformed_id).and_return("123")
33
- allow(transformer).to receive(:transformed_status).and_return("Open")
34
- allow(transformer).to receive(:transformed_resolution).and_return("")
35
- allow(transformer).to receive(:transformed_priority).and_return("Low")
36
- allow(transformer).to receive(:transformed_summary).and_return("transformed summary (123)")
37
- allow(transformer).to receive(:transformed_source_ticket_url).and_return("url/to/source/ticket/123")
35
+ it "transforms a row" do
36
+ allow(transformer).to receive(:transformed_id).and_return("123")
37
+ allow(transformer).to receive(:transformed_status).and_return("Open")
38
+ allow(transformer).to receive(:transformed_resolution).and_return("")
39
+ allow(transformer).to receive(:transformed_priority).and_return("Low")
40
+ allow(transformer).to receive(:transformed_summary).and_return("transformed summary (123)")
41
+ allow(transformer).to receive(:transformed_source_ticket_url).and_return("url/to/source/ticket/123")
38
42
 
39
- expect(transformer.run)
40
- .to eq(["123", "Open", "", "Low", "transformed summary (123)", "url/to/source/ticket/123"])
41
- end
42
- end
43
+ expect(transformer.run)
44
+ .to eq(["123", "Open", "", "Low", "transformed summary (123)", "url/to/source/ticket/123"])
45
+ end
46
+ end
43
47
 
44
- describe "#mappings" do
45
- let(:mappings_yaml) do
46
- <<~YAML
47
- field_mapping:
48
- id: Defect
49
- summary: Defect (2)
50
- status_mapping:
51
- defaults_to: keep_original_value
52
- Open: Open
53
- Closed: Closed
54
- resolution_mapping:
55
- Closed: Done
56
- Rejected: Won't Fix
57
- team_mapping:
58
- Source Team: Transformed Team
59
- Front End: Web Team
60
- priority_mapping:
61
- 1 - Critical: Critical
62
- 2 - High: High
63
- 3 - Medium: Medium
64
- 4 - Low: Low
65
- YAML
66
- end
48
+ describe "#mappings" do
49
+ let(:mappings_yaml) do
50
+ <<~'YAML'
51
+ field_mapping:
52
+ id: Defect
53
+ summary: Defect (2)
54
+ id_extraction_regex: "\\((?<id>\\d+)\\)$"
55
+ status_mapping:
56
+ defaults_to: keep_original_value
57
+ Open: Open
58
+ Closed: Closed
59
+ resolution_mapping:
60
+ Closed: Done
61
+ Rejected: Won't Fix
62
+ team_mapping:
63
+ Source Team: Transformed Team
64
+ Front End: Web Team
65
+ priority_mapping:
66
+ 1 - Critical: Critical
67
+ 2 - High: High
68
+ 3 - Medium: Medium
69
+ 4 - Low: Low
70
+ YAML
71
+ end
67
72
 
68
- let(:extracted_row) { {} }
73
+ let(:extracted_row) { {} }
74
+
75
+ let(:file_io) { instance_double(IO) }
76
+
77
+ it "loads the mappings from the mappings.yml file" do
78
+ expect(IO).to receive(:open).with("config/ticket-replicator.mappings.yml",
79
+ "r:bom|utf-8").and_yield(mappings_yaml)
80
+
81
+ expect(transformer.mappings)
82
+ .to eq(
83
+ "field_mapping" => { "id" => "Defect", "summary" => "Defect (2)" },
84
+ "id_extraction_regex" => "\\((?<id>\\d+)\\)$",
85
+ "status_mapping" => { "defaults_to" => "keep_original_value", "Open" => "Open",
86
+ "Closed" => "Closed" },
87
+ "resolution_mapping" => { "Closed" => "Done", "Rejected" => "Won't Fix" },
88
+ "team_mapping" => { "Source Team" => "Transformed Team", "Front End" => "Web Team" },
89
+ "priority_mapping" =>
90
+ { "1 - Critical" => "Critical", "2 - High" => "High", "3 - Medium" => "Medium",
91
+ "4 - Low" => "Low" }
92
+ )
93
+ end
94
+ end
69
95
 
70
- let(:file_io) { instance_double(IO) }
96
+ describe "#ticket_replicator_source_ticket_url" do
97
+ let(:extracted_row) { {} }
71
98
 
72
- it "loads the mappings from the mappings.yml file" do
73
- expect(IO).to receive(:open).with("config/ticket-replicator.mappings.yml", "r:bom|utf-8").and_yield(mappings_yaml)
99
+ context "when the source ticket url is set in the environment" do
100
+ before do
101
+ allow(ENV)
102
+ .to receive(:fetch).with("TICKET_REPLICATOR_SOURCE_TICKET_URL").and_return("url/to/source/ticket")
103
+ end
74
104
 
75
- expect(transformer.mappings)
76
- .to eq(
77
- "field_mapping" => { "id" => "Defect", "summary" => "Defect (2)" },
78
- "status_mapping" => { "defaults_to" => "keep_original_value", "Open" => "Open", "Closed" => "Closed" },
79
- "resolution_mapping" => { "Closed" => "Done", "Rejected" => "Won't Fix" },
80
- "team_mapping" => { "Source Team" => "Transformed Team", "Front End" => "Web Team" },
81
- "priority_mapping" =>
82
- { "1 - Critical" => "Critical", "2 - High" => "High", "3 - Medium" => "Medium", "4 - Low" => "Low" }
83
- )
84
- end
85
- end
105
+ it { expect(transformer.ticket_replicator_source_ticket_url).to eq("url/to/source/ticket") }
106
+ end
86
107
 
87
- describe "#ticket_replicator_source_ticket_url" do
88
- let(:extracted_row) { {} }
108
+ context "when the source ticket url is not set in the environment" do
109
+ it do
110
+ expect { transformer.ticket_replicator_source_ticket_url }
111
+ .to raise_error("TICKET_REPLICATOR_SOURCE_TICKET_URL: not set in environment!")
112
+ end
113
+ end
114
+ end
89
115
 
90
- context "when the source ticket url is set in the environment" do
91
- before do
92
- allow(ENV)
93
- .to receive(:fetch).with("TICKET_REPLICATOR_SOURCE_TICKET_URL").and_return("url/to/source/ticket")
94
- end
116
+ describe "#source_ticket_url_builder" do
117
+ let(:extracted_row) { {} }
95
118
 
96
- it { expect(transformer.ticket_replicator_source_ticket_url).to eq("url/to/source/ticket") }
97
- end
119
+ before do
120
+ allow(transformer)
121
+ .to receive_messages(ticket_replicator_source_ticket_url: ticket_replicator_source_ticket_url)
122
+ end
98
123
 
99
- context "when the source ticket url is not set in the environment" do
100
- it do
101
- expect { transformer.ticket_replicator_source_ticket_url }
102
- .to raise_error("TICKET_REPLICATOR_SOURCE_TICKET_URL: not set in environment!")
103
- end
104
- end
105
- end
124
+ context "when the URL has no syntax error" do
125
+ let(:ticket_replicator_source_ticket_url) { "http://url/to/source/ticket/<%= source_ticket_id %>" }
106
126
 
107
- describe "#source_ticket_url_builder" do
108
- let(:extracted_row) { {} }
127
+ it { expect(transformer.source_ticket_url_builder("123")).to eq("http://url/to/source/ticket/123") }
128
+ end
109
129
 
110
- before do
111
- allow(transformer).to receive_messages(ticket_replicator_source_ticket_url: ticket_replicator_source_ticket_url)
112
- end
130
+ context "when the URL has a syntax error" do
131
+ let(:ticket_replicator_source_ticket_url) { "http://url/to/source/ticket/<%= missing_variable %>" }
113
132
 
114
- context "when the URL has no syntax error" do
115
- let(:ticket_replicator_source_ticket_url) { "http://url/to/source/ticket/<%= source_ticket_id %>" }
133
+ it { expect { transformer.source_ticket_url_builder("123") }.to raise_error(StandardError) }
134
+ end
135
+ end
116
136
 
117
- it { expect(transformer.source_ticket_url_builder("123")).to eq("http://url/to/source/ticket/123") }
118
- end
137
+ describe "using mappings" do
138
+ before { allow(transformer).to receive_messages(mappings: mappings) }
139
+
140
+ describe "#remapped_field_extracted_row" do
141
+ let(:mappings) do
142
+ {
143
+ "field_mapping" => {
144
+ "id" => "Defect", "priority" => "Defect Priority", "resolution" => "Defect (2)",
145
+ "status" => "Defect status", "summary" => "Defect (2)"
146
+ }
147
+ }
148
+ end
149
+
150
+ let(:extracted_row) do
151
+ {
152
+ "defect (2)" => "a summary", "defect priority" => "High", "defect status" => "Open",
153
+ "defect team (2)" => "system team", "defect" => "123"
154
+ }
155
+ end
156
+
157
+ it do
158
+ expect(transformer.remapped_field_extracted_row)
159
+ .to eq({ "id" => "123", "priority" => "High", "resolution" => "a summary",
160
+ "status" => "Open", "summary" => "a summary" })
161
+ end
162
+ end
119
163
 
120
- context "when the URL has a syntax error" do
121
- let(:ticket_replicator_source_ticket_url) { "http://url/to/source/ticket/<%= missing_variable %>" }
164
+ # rubocop:disable Metrics/BlockLength
165
+ context "when remapped field extracted row used" do
166
+ let(:extracted_row) { { :extracted_row => "should not be used in this context but the remapped version" } }
122
167
 
123
- it { expect { transformer.source_ticket_url_builder("123") }.to raise_error(StandardError) }
124
- end
125
- end
168
+ before do
169
+ allow(transformer).to receive_messages(remapped_field_extracted_row: remapped_field_extracted_row)
170
+ end
126
171
 
127
- describe "using mappings" do
128
- before { allow(transformer).to receive_messages(mappings: mappings) }
129
-
130
- describe "#remapped_field_extracted_row" do
131
- let(:mappings) do
132
- {
133
- "field_mapping" => {
134
- "id" => "Defect", "priority" => "Defect Priority", "resolution" => "Defect (2)",
135
- "status" => "Defect status", "summary" => "Defect (2)"
136
- }
137
- }
138
- end
172
+ describe "#transformed_id" do
173
+ context "when using the ID value as is" do
174
+ let(:mappings) { {} }
175
+ let(:remapped_field_extracted_row) { { "id" => "123" } }
139
176
 
140
- let(:extracted_row) do
141
- {
142
- "defect (2)" => "a summary", "defect priority" => "High", "defect status" => "Open",
143
- "defect team (2)" => "system team", "defect" => "123"
144
- }
145
- end
177
+ it "returns the ID" do
178
+ expect(transformer.transformed_id).to eq("123")
179
+ end
180
+ end
146
181
 
147
- it do
148
- expect(transformer.remapped_field_extracted_row)
149
- .to eq({ "id" => "123", "priority" => "High", "resolution" => "a summary",
150
- "status" => "Open", "summary" => "a summary" })
151
- end
152
- end
182
+ context "when extracting the ID from the string" do
183
+ let(:remapped_field_extracted_row) { { "id" => "also (85)73 in summary (123456)" } }
184
+ let(:mappings) { { "id_extraction_regex" => "\\((?<id>\\d+)\\)$" } }
153
185
 
154
- # rubocop:disable Metrics/BlockLength
155
- context "when remapped field extracted row used" do
156
- let(:extracted_row) { { :extracted_row => "should not be used in this context but the remapped version" } }
186
+ it { expect(transformer.transformed_id).to eq("123456") }
157
187
 
158
- before { allow(transformer).to receive_messages(remapped_field_extracted_row: remapped_field_extracted_row) }
188
+ context "when the regex does not match" do
189
+ let(:remapped_field_extracted_row) { { "id" => "summary (non numeric)" } }
159
190
 
160
- describe "#transformed_id" do
161
- let(:remapped_field_extracted_row) { { "id" => "123" } }
162
- let(:mappings) { {} }
191
+ it "raises an error" do
192
+ expect { transformer.transformed_id }
193
+ .to raise_error(FileTransformer::TransformError,
194
+ %(No match found for :id in "summary (non numeric)") +
195
+ " using /\\((?<id>\\d+)\\)$/")
196
+ end
197
+ end
198
+ end
199
+ end
163
200
 
164
- it "returns the ID" do
165
- expect(transformer.transformed_id).to eq("123")
166
- end
167
- end
201
+ describe "#transformed_source_ticket_url" do
202
+ let(:remapped_field_extracted_row) { { "id" => "123" } }
203
+ let(:mappings) { {} }
168
204
 
169
- describe "#transformed_source_ticket_url" do
170
- let(:remapped_field_extracted_row) { { "id" => "123" } }
171
- let(:mappings) { {} }
205
+ before do
206
+ allow(transformer)
207
+ .to receive_messages(
208
+ transformed_id: "123",
209
+ ticket_replicator_source_ticket_url: "url/to/source/ticket/<%= source_ticket_id %>"
210
+ )
211
+ end
172
212
 
173
- before do
174
- allow(transformer)
175
- .to receive_messages(transformed_id: "123",
176
- ticket_replicator_source_ticket_url: "url/to/source/ticket/<%= source_ticket_id %>")
177
- end
213
+ it "returns the source ticket url" do
214
+ expect(transformer.transformed_source_ticket_url).to eq("url/to/source/ticket/123")
215
+ end
216
+ end
178
217
 
179
- it "returns the source ticket url" do
180
- expect(transformer.transformed_source_ticket_url).to eq("url/to/source/ticket/123")
181
- end
182
- end
218
+ describe "#transformed_summary" do
219
+ let(:remapped_field_extracted_row) { { "id" => "426", "summary" => "a summary" } }
220
+ let(:mappings) { {} }
183
221
 
184
- describe "#transformed_summary" do
185
- let(:remapped_field_extracted_row) { { "id" => "426", "summary" => "a summary" } }
186
- let(:mappings) { {} }
222
+ before do
223
+ allow(ReplicatedSummary).to receive(:build).with("426", "a summary").and_return("replicated_summary")
224
+ end
187
225
 
188
- before do
189
- allow(Ticket::Replicator::ReplicatedSummary)
190
- .to receive(:build).with("426", "a summary").and_return("replicated_summary")
191
- end
226
+ it "returns the summary" do
227
+ expect(transformer.transformed_summary).to eq("replicated_summary")
228
+ end
229
+ end
192
230
 
193
- it "returns the summary" do
194
- expect(transformer.transformed_summary).to eq("replicated_summary")
195
- end
196
- end
231
+ shared_examples "an unmapped value handler" do |field_name, source_field|
232
+ source_field ||= field_name
197
233
 
198
- shared_examples "an unmapped value handler" do |field_name, source_field|
199
- source_field ||= field_name
234
+ let(:remapped_field_extracted_row) { { source_field.to_s => an_original_value } }
200
235
 
201
- let(:remapped_field_extracted_row) { { source_field.to_s => an_original_value } }
236
+ let(:an_original_value) { "any #{field_name} #{rand}" }
202
237
 
203
- let(:an_original_value) { "any #{field_name} #{rand}" }
238
+ let(:transformed_value) { transformer.send("transformed_#{field_name}") }
204
239
 
205
- let(:transformed_value) { transformer.send("transformed_#{field_name}") }
240
+ context "when defaulting to original value" do
241
+ let(:mappings) { { "#{field_name}_mapping" => { "defaults_to" => "keep_original_value" } } }
206
242
 
207
- context "when defaulting to original value" do
208
- let(:mappings) { { "#{field_name}_mapping" => { "defaults_to" => "keep_original_value" } } }
243
+ it { expect(transformed_value).to eq(an_original_value) }
244
+ end
209
245
 
210
- it { expect(transformed_value).to eq(an_original_value) }
211
- end
246
+ context "when defaulting to unset value" do
247
+ let(:mappings) { { "#{field_name}_mapping" => { "defaults_to" => "unset_value" } } }
212
248
 
213
- context "when defaulting to unset value" do
214
- let(:mappings) { { "#{field_name}_mapping" => { "defaults_to" => "unset_value" } } }
249
+ it { expect(transformed_value).to eq(nil) }
250
+ end
215
251
 
216
- it { expect(transformed_value).to eq(nil) }
217
- end
252
+ context "when defaulting to blank value" do
253
+ let(:mappings) { { "#{field_name}_mapping" => { "defaults_to" => "blank_value" } } }
218
254
 
219
- context "when defaulting to blank value" do
220
- let(:mappings) { { "#{field_name}_mapping" => { "defaults_to" => "blank_value" } } }
255
+ it { expect(transformed_value).to eq("") }
256
+ end
221
257
 
222
- it { expect(transformed_value).to eq("") }
223
- end
258
+ context "when the value is unexpected" do
259
+ let(:mappings) do
260
+ { "#{field_name}_mapping" => { "extracted value" => "transformed value" } }
261
+ end
224
262
 
225
- context "when the value is unexpected" do
226
- let(:mappings) do
227
- { "#{field_name}_mapping" => { "extracted value" => "transformed value" } }
228
- end
263
+ it do
264
+ expect { transformed_value }
265
+ .to raise_error(RuntimeError,
266
+ "No mapping found for #{field_name.inspect} = #{an_original_value.inspect} " \
267
+ "in #{mappings["#{field_name}_mapping"].inspect}")
268
+ end
269
+ end
270
+ end
229
271
 
230
- it do
231
- expect { transformed_value }
232
- .to raise_error(RuntimeError,
233
- "No mapping found for #{field_name.inspect} = #{an_original_value.inspect} " \
234
- "in #{mappings["#{field_name}_mapping"].inspect}")
235
- end
236
- end
237
- end
272
+ describe "#transformed_status" do
273
+ let(:mappings) do
274
+ { "status_mapping" => { "Open" => "Open", "Closed" => "Closed" } }
275
+ end
238
276
 
239
- describe "#transformed_status" do
240
- let(:mappings) do
241
- { "status_mapping" => { "Open" => "Open", "Closed" => "Closed" } }
242
- end
277
+ let(:remapped_field_extracted_row) { { "status" => "Open" } }
243
278
 
244
- let(:remapped_field_extracted_row) { { "status" => "Open" } }
279
+ it "returns the status" do
280
+ expect(transformer.transformed_status).to eq("Open")
281
+ end
245
282
 
246
- it "returns the status" do
247
- expect(transformer.transformed_status).to eq("Open")
248
- end
283
+ it_behaves_like "an unmapped value handler", :status
284
+ end
249
285
 
250
- it_behaves_like "an unmapped value handler", :status
251
- end
286
+ describe "#transformed_resolution" do
287
+ let(:mappings) { { "resolution_mapping" => { "Closed" => "Done", "Rejected" => "Won't Fix" } } }
252
288
 
253
- describe "#transformed_resolution" do
254
- let(:mappings) { { "resolution_mapping" => { "Closed" => "Done", "Rejected" => "Won't Fix" } } }
289
+ context "when the status is Closed" do
290
+ let(:remapped_field_extracted_row) { { "status" => "Closed" } }
255
291
 
256
- context "when the status is Closed" do
257
- let(:remapped_field_extracted_row) { { "status" => "Closed" } }
292
+ it "returns the resolution" do
293
+ expect(transformer.transformed_resolution).to eq("Done")
294
+ end
295
+ end
258
296
 
259
- it "returns the resolution" do
260
- expect(transformer.transformed_resolution).to eq("Done")
261
- end
262
- end
297
+ context "when the status is Rejected" do
298
+ let(:remapped_field_extracted_row) { { "status" => "Rejected" } }
263
299
 
264
- context "when the status is Rejected" do
265
- let(:remapped_field_extracted_row) { { "status" => "Rejected" } }
300
+ it "returns the resolution" do
301
+ expect(transformer.transformed_resolution).to eq("Won't Fix")
302
+ end
303
+ end
266
304
 
267
- it "returns the resolution" do
268
- expect(transformer.transformed_resolution).to eq("Won't Fix")
269
- end
270
- end
305
+ it_behaves_like "an unmapped value handler", :resolution, :status
306
+ end
271
307
 
272
- it_behaves_like "an unmapped value handler", :resolution, :status
273
- end
308
+ describe "#transformed_team" do
309
+ let(:mappings) do
310
+ { "team_mapping" => { "Source Team" => "Transformed Team", "Front End" => "Web Team" } }
311
+ end
274
312
 
275
- describe "#transformed_team" do
276
- let(:mappings) { { "team_mapping" => { "Source Team" => "Transformed Team", "Front End" => "Web Team" } } }
313
+ let(:remapped_field_extracted_row) { { "team" => "Source Team" } }
277
314
 
278
- let(:remapped_field_extracted_row) { { "team" => "Source Team" } }
315
+ it "returns the team" do
316
+ expect(transformer.transformed_team).to eq("Transformed Team")
317
+ end
279
318
 
280
- it "returns the team" do
281
- expect(transformer.transformed_team).to eq("Transformed Team")
282
- end
319
+ it_behaves_like "an unmapped value handler", :team
320
+ end
283
321
 
284
- it_behaves_like "an unmapped value handler", :team
285
- end
322
+ describe "#transformed_priority" do
323
+ let(:mappings) { { "priority_mapping" => { "1 - Critical" => "Critical", "2 - High" => "High" } } }
286
324
 
287
- describe "#transformed_priority" do
288
- let(:mappings) { { "priority_mapping" => { "1 - Critical" => "Critical", "2 - High" => "High" } } }
325
+ let(:remapped_field_extracted_row) { { "priority" => "1 - Critical" } }
289
326
 
290
- let(:remapped_field_extracted_row) { { "priority" => "1 - Critical" } }
327
+ it "returns the priority" do
328
+ expect(transformer.transformed_priority).to eq("Critical")
329
+ end
291
330
 
292
- it "returns the priority" do
293
- expect(transformer.transformed_priority).to eq("Critical")
331
+ it_behaves_like "an unmapped value handler", :priority
332
+ end
333
+ end
334
+ # rubocop:enable Metrics/BlockLength
294
335
  end
295
-
296
- it_behaves_like "an unmapped value handler", :priority
297
336
  end
298
337
  end
299
- # rubocop:enable Metrics/BlockLength
338
+
339
+ # rubocop:enable Metrics/ClassLength
300
340
  end
301
341
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ticket-replicator
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.1.0
4
+ version: 1.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Christophe Broult
@@ -146,6 +146,7 @@ extra_rdoc_files: []
146
146
  files:
147
147
  - ".rspec"
148
148
  - ".rubocop.yml"
149
+ - ".ruby-version"
149
150
  - CHANGELOG.md
150
151
  - CODE_OF_CONDUCT.md
151
152
  - Guardfile