ticket-replicator 1.0.0 → 1.2.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.
- checksums.yaml +4 -4
- data/.rubocop.yml +5 -0
- data/.ruby-version +1 -0
- data/README.md +9 -9
- data/config/examples/ticket-replicator.mappings.yml +3 -1
- data/features/load_tickets_in_jira.feature +82 -69
- data/features/setup_ticket_replicator.feature +3 -1
- data/features/transform-solution-manager-tickets-into-jira-loadable-tickets.feature +86 -32
- data/features/transform_and_load_extracted_ticket_queue.feature +18 -14
- data/lib/ticket/replicator/file_transformer.rb +1 -0
- data/lib/ticket/replicator/row_loader.rb +73 -16
- data/lib/ticket/replicator/row_transformer.rb +47 -4
- data/lib/ticket/replicator/ticket.rb +17 -0
- data/lib/ticket/replicator/version.rb +1 -1
- data/spec/ticket/replicator/file_transformer_spec.rb +5 -6
- data/spec/ticket/replicator/row_loader_spec.rb +270 -40
- data/spec/ticket/replicator/row_transformer_spec.rb +280 -185
- data/spec/ticket/replicator/ticket_spec.rb +50 -1
- metadata +2 -1
@@ -20,12 +20,13 @@ module Ticket
|
|
20
20
|
end
|
21
21
|
|
22
22
|
describe ".fields_to_load" do
|
23
|
-
it { expect(described_class.fields_to_load).to eq(%i[id status resolution priority summary]) }
|
23
|
+
it { expect(described_class.fields_to_load).to eq(%i[id status resolution priority summary source_ticket_url]) }
|
24
24
|
end
|
25
25
|
|
26
26
|
describe "#run" do
|
27
27
|
it do
|
28
28
|
expect(loader).to receive(:save_ticket)
|
29
|
+
expect(loader).to receive(:update_source_ticket_remote_link)
|
29
30
|
expect(loader).to receive(:transition_ticket_to_the_expected_status)
|
30
31
|
|
31
32
|
loader.run
|
@@ -41,7 +42,7 @@ module Ticket
|
|
41
42
|
it do
|
42
43
|
expect(loader).to receive(:create_ticket)
|
43
44
|
|
44
|
-
loader.ticket
|
45
|
+
loader.send(:ticket)
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
@@ -51,7 +52,7 @@ module Ticket
|
|
51
52
|
it do
|
52
53
|
expect(loader).to receive(:fetch_ticket)
|
53
54
|
|
54
|
-
loader.ticket
|
55
|
+
loader.send(:ticket)
|
55
56
|
end
|
56
57
|
end
|
57
58
|
end
|
@@ -107,7 +108,7 @@ module Ticket
|
|
107
108
|
it do
|
108
109
|
expect(jira_project).to receive(:replicated_tickets).and_return(replicated_tickets)
|
109
110
|
|
110
|
-
expect(loader.fetch_ticket).to be(:a_ticket)
|
111
|
+
expect(loader.send(:fetch_ticket)).to be(:a_ticket)
|
111
112
|
end
|
112
113
|
end
|
113
114
|
|
@@ -127,7 +128,7 @@ module Ticket
|
|
127
128
|
expect(issue_accessor).to receive(:build).and_return(jira_ticket)
|
128
129
|
expect(Ticket).to receive(:new).with(jira_auto_tool, jira_ticket).and_return(:ticket)
|
129
130
|
|
130
|
-
expect(loader.create_ticket).to eq(:ticket)
|
131
|
+
expect(loader.send(:create_ticket)).to eq(:ticket)
|
131
132
|
end
|
132
133
|
end
|
133
134
|
|
@@ -148,26 +149,14 @@ module Ticket
|
|
148
149
|
ticket: ticket,
|
149
150
|
:ticket_fields_need_to_be_updated? => ticket_fields_need_to_be_updated?
|
150
151
|
)
|
151
|
-
|
152
|
-
allow(jira_project)
|
153
|
-
.to receive_messages(resolutions: {
|
154
|
-
"Done" => nil, "Won't Do" => nil, "Duplicate" => nil, "Cannot Reproduce" => nil
|
155
|
-
})
|
156
152
|
end
|
157
153
|
|
158
154
|
context "when the previously replicated needs to be updated " do
|
159
155
|
let(:ticket_fields_need_to_be_updated?) { true }
|
160
156
|
|
161
157
|
it do
|
162
|
-
|
163
|
-
|
164
|
-
project: { key: "PROJKEY" },
|
165
|
-
issuetype: { name: "Bug" },
|
166
|
-
resolution: { name: "Done" },
|
167
|
-
priority: { name: "Low" },
|
168
|
-
summary: "PREFIX | summary"
|
169
|
-
}
|
170
|
-
})
|
158
|
+
allow(loader).to receive_messages(attributes_for_save: { fields: { a: "b" } })
|
159
|
+
expect(jira_ticket).to receive(:save!).with({ fields: { a: "b" } })
|
171
160
|
|
172
161
|
expect(jira_ticket).to receive(:fetch)
|
173
162
|
|
@@ -187,6 +176,95 @@ module Ticket
|
|
187
176
|
end
|
188
177
|
end
|
189
178
|
|
179
|
+
describe "#attributes_for_save" do
|
180
|
+
let(:loader) { described_class.send(:new, jira_project, row) }
|
181
|
+
|
182
|
+
let(:row) do
|
183
|
+
{ id: "123", priority: "Low", resolution: "Done", status: "Open", summary: "PREFIX | summary",
|
184
|
+
team: "A Team" }
|
185
|
+
end
|
186
|
+
|
187
|
+
before do
|
188
|
+
allow(jira_project)
|
189
|
+
.to receive_messages(resolutions: {
|
190
|
+
"Done" => nil, "Won't Do" => nil, "Duplicate" => nil, "Cannot Reproduce" => nil
|
191
|
+
})
|
192
|
+
|
193
|
+
allow(loader).to receive_messages(exclude_resolution?: exclude_resolution?)
|
194
|
+
end
|
195
|
+
|
196
|
+
context "when the resolution is not excluded" do
|
197
|
+
let(:exclude_resolution?) { false }
|
198
|
+
|
199
|
+
it do
|
200
|
+
expect(loader.send(:attributes_for_save)).to eq({
|
201
|
+
fields: {
|
202
|
+
project: { key: "PROJKEY" },
|
203
|
+
issuetype: { name: "Bug" },
|
204
|
+
priority: { name: "Low" },
|
205
|
+
resolution: { name: "Done" },
|
206
|
+
summary: "PREFIX | summary"
|
207
|
+
}
|
208
|
+
})
|
209
|
+
end
|
210
|
+
end
|
211
|
+
|
212
|
+
context "when excluding the resolution" do
|
213
|
+
let(:exclude_resolution?) { true }
|
214
|
+
|
215
|
+
it do
|
216
|
+
expect(loader.send(:attributes_for_save)).to eq({
|
217
|
+
fields: {
|
218
|
+
project: { key: "PROJKEY" },
|
219
|
+
issuetype: { name: "Bug" },
|
220
|
+
priority: { name: "Low" },
|
221
|
+
summary: "PREFIX | summary"
|
222
|
+
}
|
223
|
+
})
|
224
|
+
end
|
225
|
+
end
|
226
|
+
end
|
227
|
+
|
228
|
+
describe "#exclude_resolution?" do
|
229
|
+
before { allow(ENV).to receive(:[]).with("TICKET_REPLICATOR_DISABLE_RESOLUTION_LOADING").and_return(env_value) }
|
230
|
+
|
231
|
+
context "when the environment variable is not set to true" do
|
232
|
+
let(:env_value) { nil }
|
233
|
+
|
234
|
+
it { expect(loader.send(:exclude_resolution?)).to be_falsey }
|
235
|
+
end
|
236
|
+
|
237
|
+
context "when the environment variable is set to true" do
|
238
|
+
let(:env_value) { "true" }
|
239
|
+
|
240
|
+
it { expect(loader.send(:exclude_resolution?)).to be_truthy }
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
describe "#build_ticket_link_attributes" do
|
245
|
+
it "builds the link attributes with a default application" do
|
246
|
+
expect(described_class.send(:build_ticket_link_attributes,
|
247
|
+
"https://url/to/source/ticket/123", "Source Ticket 123"))
|
248
|
+
.to eq({ "object" => { "url" => "https://url/to/source/ticket/123", "title" => "Source Ticket 123" },
|
249
|
+
"application" => { "name" => "Ticket Source" } })
|
250
|
+
end
|
251
|
+
|
252
|
+
it "builds the link attributes with a custom application" do
|
253
|
+
expect(described_class.send(:build_ticket_link_attributes,
|
254
|
+
"https://url/to/source/ticket/123", "Source Ticket 123",
|
255
|
+
"A custom application"))
|
256
|
+
.to eq({ "object" => { "url" => "https://url/to/source/ticket/123", "title" => "Source Ticket 123" },
|
257
|
+
"application" => { "name" => "A custom application" } })
|
258
|
+
end
|
259
|
+
|
260
|
+
it "builds the link attributes with no application" do
|
261
|
+
expect(described_class.send(:build_ticket_link_attributes,
|
262
|
+
"https://url/to/source/ticket/123", "Source Ticket 123",
|
263
|
+
nil))
|
264
|
+
.to eq({ "object" => { "url" => "https://url/to/source/ticket/123", "title" => "Source Ticket 123" } })
|
265
|
+
end
|
266
|
+
end
|
267
|
+
|
190
268
|
describe "#ticket_fields_need_to_be_updated?" do
|
191
269
|
before do
|
192
270
|
allow(loader).to receive_messages(ticket_previously_replicated?: ticket_previously_replicated?,
|
@@ -197,21 +275,21 @@ module Ticket
|
|
197
275
|
let(:ticket_previously_replicated?) { false }
|
198
276
|
let(:ticket_fields_changed?) { false }
|
199
277
|
|
200
|
-
it { expect(loader.ticket_fields_need_to_be_updated?).to be_truthy }
|
278
|
+
it { expect(loader.send(:ticket_fields_need_to_be_updated?)).to be_truthy }
|
201
279
|
end
|
202
280
|
|
203
281
|
context "when the fields changed since last replication" do
|
204
282
|
let(:ticket_previously_replicated?) { true }
|
205
283
|
let(:ticket_fields_changed?) { true }
|
206
284
|
|
207
|
-
it { expect(loader.ticket_fields_need_to_be_updated?).to be_truthy }
|
285
|
+
it { expect(loader.send(:ticket_fields_need_to_be_updated?)).to be_truthy }
|
208
286
|
end
|
209
287
|
|
210
288
|
context "when the fields did not change since last replication" do
|
211
289
|
let(:ticket_previously_replicated?) { true }
|
212
290
|
let(:ticket_fields_changed?) { false }
|
213
291
|
|
214
|
-
it { expect(loader.ticket_fields_need_to_be_updated?).to be_falsey }
|
292
|
+
it { expect(loader.send(:ticket_fields_need_to_be_updated?)).to be_falsey }
|
215
293
|
end
|
216
294
|
end
|
217
295
|
|
@@ -221,75 +299,227 @@ module Ticket
|
|
221
299
|
end
|
222
300
|
|
223
301
|
let(:loader) { described_class.send(:new, jira_project, row) }
|
224
|
-
|
302
|
+
|
303
|
+
let(:jira_ticket) do
|
304
|
+
double(JIRA::Resource::Issue,
|
305
|
+
summary: current_summary,
|
306
|
+
priority: current_priority,
|
307
|
+
resolution: current_resolution)
|
308
|
+
end
|
225
309
|
|
226
310
|
before do
|
227
311
|
expect(loader).to receive(:ticket).at_least(:once).and_return(jira_ticket)
|
228
312
|
end
|
229
313
|
|
230
|
-
context "when
|
314
|
+
context "when summary, priority and resolution changed" do
|
231
315
|
let(:current_summary) { "PREFIX | different summary" }
|
232
316
|
let(:current_priority) { "High" }
|
317
|
+
let(:current_resolution) { "Won't Do" }
|
233
318
|
|
234
|
-
it { expect(loader.ticket_fields_changed?).to be_truthy }
|
319
|
+
it { expect(loader.send(:ticket_fields_changed?)).to be_truthy }
|
235
320
|
end
|
236
321
|
|
237
322
|
context "when only summary changed" do
|
238
323
|
let(:current_summary) { "PREFIX | different summary" }
|
239
324
|
let(:current_priority) { "Low" }
|
325
|
+
let(:current_resolution) { "" }
|
240
326
|
|
241
|
-
it { expect(loader.ticket_fields_changed?).to be_truthy }
|
327
|
+
it { expect(loader.send(:ticket_fields_changed?)).to be_truthy }
|
242
328
|
end
|
243
329
|
|
244
330
|
context "when only priority changed" do
|
245
331
|
let(:current_summary) { "PREFIX | summary" }
|
246
332
|
let(:current_priority) { "High" }
|
333
|
+
let(:current_resolution) { "" }
|
247
334
|
|
248
|
-
it { expect(loader.ticket_fields_changed?).to be_truthy }
|
335
|
+
it { expect(loader.send(:ticket_fields_changed?)).to be_truthy }
|
336
|
+
end
|
337
|
+
|
338
|
+
context "when only resolution changed" do
|
339
|
+
let(:current_summary) { "PREFIX | summary" }
|
340
|
+
let(:current_priority) { "Low" }
|
341
|
+
let(:current_resolution) { "Done" }
|
342
|
+
|
343
|
+
before do
|
344
|
+
allow(loader).to receive_messages(exclude_resolution?: exclude_resolution?)
|
345
|
+
end
|
346
|
+
|
347
|
+
context "when the resolution is not excluded" do
|
348
|
+
let(:exclude_resolution?) { false }
|
349
|
+
|
350
|
+
it { expect(loader.send(:ticket_fields_changed?)).to be_truthy }
|
351
|
+
end
|
352
|
+
|
353
|
+
context "when excluding the resolution" do
|
354
|
+
let(:exclude_resolution?) { true }
|
355
|
+
|
356
|
+
it { expect(loader.send(:ticket_fields_changed?)).to be_falsey }
|
357
|
+
end
|
249
358
|
end
|
250
359
|
|
251
360
|
context "when neither summary nor priority changed" do
|
252
361
|
let(:current_summary) { "PREFIX | summary" }
|
253
362
|
let(:current_priority) { "Low" }
|
363
|
+
let(:current_resolution) { "" }
|
254
364
|
|
255
|
-
it { expect(loader.ticket_fields_changed?).to be_falsey }
|
365
|
+
it { expect(loader.send(:ticket_fields_changed?)).to be_falsey }
|
256
366
|
end
|
257
367
|
end
|
258
368
|
|
259
369
|
describe "#ticket_previously_replicated?" do
|
260
370
|
let(:loader) { described_class.send(:new, jira_project, row) }
|
261
|
-
let(:replicated_tickets) { { "123" => "a_ticket" } }
|
371
|
+
let(:replicated_tickets) { { "123" => "a_ticket", "880" => "another_ticket" } }
|
262
372
|
|
263
373
|
before { expect(jira_project).to receive(:replicated_tickets).and_return(replicated_tickets) }
|
264
374
|
|
265
375
|
context "when previously replicated" do
|
266
376
|
let(:row) { { id: "123" } }
|
267
377
|
|
268
|
-
it { expect(loader.ticket_previously_replicated?).to be_truthy }
|
378
|
+
it { expect(loader.send(:ticket_previously_replicated?)).to be_truthy }
|
269
379
|
end
|
270
380
|
|
271
381
|
context "when previously replicated" do
|
272
382
|
let(:row) { { id: "456" } }
|
273
383
|
|
274
|
-
it { expect(loader.ticket_previously_replicated?).to be_falsey }
|
384
|
+
it { expect(loader.send(:ticket_previously_replicated?)).to be_falsey }
|
275
385
|
end
|
276
386
|
end
|
277
387
|
|
278
|
-
|
388
|
+
# rubocop:disable Metrics/BlockLength
|
389
|
+
context "when caring for the source ticket link" do
|
390
|
+
def build_link(url, title, application_name = "Ticket Source")
|
391
|
+
RowLoader.build_ticket_link_attributes(url, title, application_name)
|
392
|
+
end
|
393
|
+
|
279
394
|
let(:loader) { described_class.send(:new, jira_project, row) }
|
280
|
-
let(:row) { { id: "123",
|
395
|
+
let(:row) { { id: "123", source_ticket_url: "https://url/to/source/ticket/123" } }
|
396
|
+
let(:ticket) { instance_double(Ticket, jira_ticket: jira_ticket) }
|
397
|
+
let(:jira_ticket) { double(JIRA::Resource::Issue, remotelink: double(build: remotelink)) }
|
398
|
+
let(:remotelink) { instance_double(JIRA::Resource::Remotelink) }
|
281
399
|
|
282
|
-
|
400
|
+
before { allow(loader).to receive_messages(ticket: ticket) }
|
283
401
|
|
284
|
-
|
285
|
-
|
286
|
-
|
402
|
+
let(:expected_link) { build_link("https://url/to/source/ticket/123", "Source Ticket 123") }
|
403
|
+
|
404
|
+
describe "#source_ticket_link_title" do
|
405
|
+
it { expect(loader.send(:source_ticket_link_title)).to eq("Source Ticket 123") }
|
406
|
+
end
|
407
|
+
|
408
|
+
describe "#update_source_ticket_remote_link" do
|
409
|
+
before do
|
410
|
+
allow(loader).to receive_messages(source_ticket_link_needs_update?: needs_update)
|
411
|
+
end
|
412
|
+
|
413
|
+
context "when the link does not exist" do
|
414
|
+
let(:needs_update) { true }
|
415
|
+
|
416
|
+
before { allow(ticket).to receive_messages(source_ticket_link: nil) }
|
417
|
+
|
418
|
+
it "setups the link" do
|
419
|
+
expect(remotelink).to receive(:save!).with(expected_link)
|
420
|
+
|
421
|
+
loader.send(:update_source_ticket_remote_link)
|
422
|
+
end
|
423
|
+
end
|
424
|
+
|
425
|
+
context "when the link needs to be updated" do
|
426
|
+
let(:needs_update) { true }
|
427
|
+
let(:source_ticket_link) { instance_double(JIRA::Resource::Remotelink) }
|
428
|
+
|
429
|
+
before { allow(ticket).to receive_messages(source_ticket_link: source_ticket_link) }
|
430
|
+
|
431
|
+
it "deletes the previous link and creates a new one" do
|
432
|
+
expect(source_ticket_link).to receive(:delete)
|
433
|
+
expect(remotelink).to receive(:save!).with(expected_link)
|
434
|
+
|
435
|
+
loader.send(:update_source_ticket_remote_link)
|
436
|
+
end
|
437
|
+
end
|
438
|
+
|
439
|
+
context "when the link does not need to be updated" do
|
440
|
+
let(:needs_update) { false }
|
441
|
+
|
442
|
+
it "no link creation happens" do
|
443
|
+
expect(remotelink).not_to receive(:save!)
|
287
444
|
|
288
|
-
|
445
|
+
loader.send(:update_source_ticket_remote_link)
|
446
|
+
end
|
447
|
+
end
|
448
|
+
end
|
449
|
+
|
450
|
+
describe "#source_ticket_link_needs_update?" do
|
451
|
+
before do
|
452
|
+
allow(ticket).to receive_messages(source_ticket_link: remote_link)
|
453
|
+
end
|
454
|
+
|
455
|
+
context "when link has yet to be created" do
|
456
|
+
let(:remote_link) { nil }
|
457
|
+
|
458
|
+
it { expect(loader.send(:source_ticket_link_needs_update?)).to be_truthy }
|
459
|
+
end
|
460
|
+
|
461
|
+
context "when link already exists" do
|
462
|
+
let(:remote_link) do
|
463
|
+
instance_double(JIRA::Resource::Remotelink,
|
464
|
+
attrs: source_ticket_link,
|
465
|
+
inspect: "<JIRA::Resource::Remotelink attrs: #{source_ticket_link}>")
|
466
|
+
end
|
467
|
+
|
468
|
+
context "when the link is not up to date" do
|
469
|
+
let(:source_ticket_link) { build_link(existing_url, existing_title) }
|
470
|
+
|
471
|
+
context "when url has changed" do
|
472
|
+
let(:existing_url) { "https://__OLD_URL__/to/source/ticket/1234" }
|
473
|
+
let(:existing_title) { "Source Ticket 123" }
|
474
|
+
|
475
|
+
it { expect(loader.send(:source_ticket_link_needs_update?)).to be_truthy }
|
476
|
+
end
|
477
|
+
|
478
|
+
context "when title has changed" do
|
479
|
+
let(:existing_url) { "https://url/to/source/ticket/123" }
|
480
|
+
let(:existing_title) { "__OLD TITLE__Source Ticket 1234" }
|
481
|
+
|
482
|
+
it { expect(loader.send(:source_ticket_link_needs_update?)).to be_truthy }
|
483
|
+
end
|
484
|
+
end
|
485
|
+
|
486
|
+
context "when the link is up to date" do
|
487
|
+
let(:existing_url) { "https://url/to/source/ticket/123" }
|
488
|
+
let(:existing_title) { "Source Ticket 123" }
|
489
|
+
let(:source_ticket_link) do
|
490
|
+
{ "object" => {
|
491
|
+
"url" => "https://url/to/source/ticket/123",
|
492
|
+
"title" => "Source Ticket 123",
|
493
|
+
"icon" => { "title" => "link", "url16x16" => "https://url/to/source/ticket/123/icon" }
|
494
|
+
},
|
495
|
+
"application" => { "name" => "Ticket Source" },
|
496
|
+
"extra_attributes" =>
|
497
|
+
{ "id" => 10_443, "self" => "https://url/to/source/ticket/123/remotelink/10443" } }
|
498
|
+
end
|
499
|
+
|
500
|
+
it("only checks the needed attributes") {
|
501
|
+
expect(loader.send(:source_ticket_link_needs_update?)).to be_falsey
|
502
|
+
}
|
503
|
+
end
|
504
|
+
end
|
505
|
+
end
|
506
|
+
# rubocop:enable Metrics/BlockLength
|
507
|
+
|
508
|
+
describe "#transition_ticket_to_the_expected_status" do
|
509
|
+
let(:loader) { described_class.send(:new, jira_project, row) }
|
510
|
+
let(:row) { { id: "123", status: "Testing" } }
|
511
|
+
|
512
|
+
let(:ticket) { double(Ticket) }
|
513
|
+
|
514
|
+
it do
|
515
|
+
expect(loader).to receive(:ticket).at_least(:once).and_return(ticket)
|
516
|
+
expect(ticket).to receive(:transition_to).with("Testing")
|
517
|
+
|
518
|
+
loader.send(:transition_ticket_to_the_expected_status)
|
519
|
+
end
|
289
520
|
end
|
290
521
|
end
|
291
522
|
end
|
523
|
+
# rubocop:enable Metrics/ClassLength
|
292
524
|
end
|
293
|
-
|
294
|
-
# rubocop:enable Metrics/ClassLength
|
295
525
|
end
|