ticket-replicator 1.0.0 → 1.1.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/README.md +7 -8
- data/features/load_tickets_in_jira.feature +65 -69
- data/features/transform-solution-manager-tickets-into-jira-loadable-tickets.feature +31 -32
- data/features/transform_and_load_extracted_ticket_queue.feature +18 -14
- data/lib/ticket/replicator/row_loader.rb +50 -1
- data/lib/ticket/replicator/row_transformer.rb +23 -2
- data/lib/ticket/replicator/ticket.rb +17 -0
- data/lib/ticket/replicator/version.rb +1 -1
- data/spec/ticket/replicator/file_transformer_spec.rb +2 -1
- data/spec/ticket/replicator/row_loader_spec.rb +153 -10
- data/spec/ticket/replicator/row_transformer_spec.rb +57 -2
- data/spec/ticket/replicator/ticket_spec.rb +50 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c470d25146cd29253a36275e23181f072d7131333f271f748ebe3560d9849825
|
4
|
+
data.tar.gz: 72393510381f5d4abdd4aa3bccafbec872fc30f585ddee5fdf7a8ae4e0f28f2e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: d1460f599c98ef9bdd4c0852cbf9c0b3734a7cba3453c96c4ab25e7dbac7a6e3303cf3b051b2d54ac80f7296113e383fafd1236eb62b8c4b57fe1d1cf190dc12
|
7
|
+
data.tar.gz: d641aa5a31b2efab1f6028383d534caf55336eeb49f43ceae446044df1cd0073873d6ca5f0b835d0581de049a402813a50a03478fdd81b9914259af079fbdadc
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -5,9 +5,9 @@
|
|
5
5
|
## Purpose
|
6
6
|
|
7
7
|
In order to:
|
8
|
-
*
|
9
|
-
*
|
10
|
-
*
|
8
|
+
* Have an overall transparency about all the activities pertaining to a project/product,
|
9
|
+
* Track progress
|
10
|
+
* Act accordingly
|
11
11
|
|
12
12
|
As a stakeholder
|
13
13
|
|
@@ -20,17 +20,15 @@ I need to replicate defect information from one system to a single reference sys
|
|
20
20
|
* ... and add to the application's Gemfile by executing:
|
21
21
|
|
22
22
|
```bash
|
23
|
-
bundle add
|
23
|
+
bundle add ticket-replicator
|
24
24
|
```
|
25
25
|
|
26
26
|
* ... if bundler is not being used to manage dependencies, by executing:
|
27
27
|
|
28
28
|
```bash
|
29
|
-
gem install
|
29
|
+
gem install ticket-replicator
|
30
30
|
```
|
31
31
|
|
32
|
-
TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_ORG` with your gem name right after releasing it to RubyGems.org. Please do not do it earlier due to security reasons. Alternatively, replace this section with instructions to install your gem from git if you don't plan to release to RubyGems.org.
|
33
|
-
|
34
32
|
## Setup
|
35
33
|
|
36
34
|
### Set environment variables
|
@@ -38,6 +36,7 @@ TODO: Replace `UPDATE_WITH_YOUR_GEM_NAME_IMMEDIATELY_AFTER_RELEASE_TO_RUBYGEMS_O
|
|
38
36
|
Define the following environment variables
|
39
37
|
- `TICKET_REPLICATOR_JIRA_PROJECT_KEY`
|
40
38
|
- `TICKET_REPLICATOR_JIRA_TICKET_TYPE_NAME`
|
39
|
+
- `TICKET_REPLICATOR_SOURCE_TICKET_URL_ERB`
|
41
40
|
|
42
41
|
For your JIRA access the following variables have to be defined:
|
43
42
|
|
@@ -61,7 +60,7 @@ Optional environment variables:
|
|
61
60
|
```
|
62
61
|
1. Edit the field mapping configuration file to fit your context.
|
63
62
|
|
64
|
-
###
|
63
|
+
### Jira Ticket Type Workflow Expectation - Any to Any
|
65
64
|
|
66
65
|
The target issue type in Jira must have a workflow that allows direct transitions between any states. This avoids
|
67
66
|
the need for administrative rights to calculate complex transition paths or discover workflow states through trial
|
@@ -8,122 +8,118 @@ Feature: Load tickets into Jira
|
|
8
8
|
| name |
|
9
9
|
| TICKET_REPLICATOR_JIRA_PROJECT_KEY |
|
10
10
|
| TICKET_REPLICATOR_JIRA_TICKET_TYPE_NAME |
|
11
|
+
# And the following environment variables have been set:
|
12
|
+
# | name | value |
|
13
|
+
# | TICKET_REPLICATOR_SOURCE_TICKET_URL | http://url/to/source/ticket/<%= source_ticket_id %> |
|
11
14
|
And the project has no tickets
|
12
15
|
|
13
16
|
Scenario: Load tickets in Jira
|
14
17
|
Given a file named "queue/20.transformed/sap_solution_manager_defects.csv" with:
|
15
18
|
"""
|
16
|
-
"ID","Status","Resolution","Priority","Summary"
|
17
|
-
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)"
|
18
|
-
"10002","In Process","","Highest","SMAN-10002 | Database deadlock during order processing (10002)"
|
19
|
-
"10003","Confirmed","Done","Medium","SMAN-10003 | Invalid date format in SOAP response (10003)"
|
20
|
-
"10004","Closed","Done","Low","SMAN-10004 | App crashes when offline on Android 12 (10004)"
|
21
|
-
"10005","Open","","High","SMAN-10005 | Session tokens not properly invalidated (10005)"
|
22
|
-
"10006","Solution Proposal","","Medium","SMAN-10006 | Jenkins pipeline timeout on large builds (10006)"
|
23
|
-
"10007","Withdrawn","Won't Do","Low","SMAN-10007 | Test data missing edge case scenarios (10007)"
|
24
|
-
"10008","Open","","Highest","SMAN-10008 | Memory leak in caching implementation (10008)"
|
25
|
-
"10009","Wait on External","","Medium","SMAN-10009 | Inconsistent button styles across modules (10009)"
|
19
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
20
|
+
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)","http://url/to/source/ticket/10001"
|
21
|
+
"10002","In Process","","Highest","SMAN-10002 | Database deadlock during order processing (10002)","http://url/to/source/ticket/10002"
|
22
|
+
"10003","Confirmed","Done","Medium","SMAN-10003 | Invalid date format in SOAP response (10003)","http://url/to/source/ticket/10003"
|
23
|
+
"10004","Closed","Done","Low","SMAN-10004 | App crashes when offline on Android 12 (10004)","http://url/to/source/ticket/10004"
|
24
|
+
"10005","Open","","High","SMAN-10005 | Session tokens not properly invalidated (10005)","http://url/to/source/ticket/10005"
|
25
|
+
"10006","Solution Proposal","","Medium","SMAN-10006 | Jenkins pipeline timeout on large builds (10006)","http://url/to/source/ticket/10006"
|
26
|
+
"10007","Withdrawn","Won't Do","Low","SMAN-10007 | Test data missing edge case scenarios (10007)","http://url/to/source/ticket/10007"
|
27
|
+
"10008","Open","","Highest","SMAN-10008 | Memory leak in caching implementation (10008)","http://url/to/source/ticket/10008"
|
28
|
+
"10009","Wait on External","","Medium","SMAN-10009 | Inconsistent button styles across modules (10009)","http://url/to/source/ticket/10009"
|
26
29
|
"""
|
27
30
|
When I successfully run `ticket-replicator --load`
|
28
31
|
Then the Jira project should only have the following tickets:
|
29
|
-
| status | resolution | priority | summary |
|
30
|
-
| Open | | Highest | SMAN-10001 \| Login page randomly fails to load CSS assets (10001) |
|
31
|
-
| In Process | | Highest | SMAN-10002 \| Database deadlock during order processing (10002) |
|
32
|
-
| Confirmed | Done | Medium | SMAN-10003 \| Invalid date format in SOAP response (10003) |
|
33
|
-
| Closed | Done | Low | SMAN-10004 \| App crashes when offline on Android 12 (10004) |
|
34
|
-
| Open | | High | SMAN-10005 \| Session tokens not properly invalidated (10005) |
|
35
|
-
| Solution Proposal | | Medium | SMAN-10006 \| Jenkins pipeline timeout on large builds (10006) |
|
36
|
-
| Withdrawn | Won't Do | Low | SMAN-10007 \| Test data missing edge case scenarios (10007) |
|
37
|
-
| Open | | Highest | SMAN-10008 \| Memory leak in caching implementation (10008) |
|
38
|
-
| Wait on External | | Medium | SMAN-10009 \| Inconsistent button styles across modules (10009) |
|
39
|
-
And the source ticket URL is found in the ticket descriptions of those tickets
|
32
|
+
| status | resolution | priority | summary | source_ticket_url |
|
33
|
+
| Open | | Highest | SMAN-10001 \| Login page randomly fails to load CSS assets (10001) | http://url/to/source/ticket/10001 |
|
34
|
+
| In Process | | Highest | SMAN-10002 \| Database deadlock during order processing (10002) | http://url/to/source/ticket/10002 |
|
35
|
+
| Confirmed | Done | Medium | SMAN-10003 \| Invalid date format in SOAP response (10003) | http://url/to/source/ticket/10003 |
|
36
|
+
| Closed | Done | Low | SMAN-10004 \| App crashes when offline on Android 12 (10004) | http://url/to/source/ticket/10004 |
|
37
|
+
| Open | | High | SMAN-10005 \| Session tokens not properly invalidated (10005) | http://url/to/source/ticket/10005 |
|
38
|
+
| Solution Proposal | | Medium | SMAN-10006 \| Jenkins pipeline timeout on large builds (10006) | http://url/to/source/ticket/10006 |
|
39
|
+
| Withdrawn | Won't Do | Low | SMAN-10007 \| Test data missing edge case scenarios (10007) | http://url/to/source/ticket/10007 |
|
40
|
+
| Open | | Highest | SMAN-10008 \| Memory leak in caching implementation (10008) | http://url/to/source/ticket/10008 |
|
41
|
+
| Wait on External | | Medium | SMAN-10009 \| Inconsistent button styles across modules (10009) | http://url/to/source/ticket/10009 |
|
40
42
|
|
41
43
|
Scenario: Loading the ticket information twice in Jira does not create additional tickets
|
42
44
|
Given a file named "queue/20.transformed/sap_solution_manager_defects.csv" with:
|
43
45
|
"""
|
44
|
-
"ID","Status","Resolution","Priority","Summary"
|
45
|
-
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)"
|
46
|
-
"10002","In Process","","Highest","SMAN-10002 | Database deadlock during order processing (10002)"
|
46
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
47
|
+
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)","http://url/to/source/ticket/10001"
|
48
|
+
"10002","In Process","","Highest","SMAN-10002 | Database deadlock during order processing (10002)","http://url/to/source/ticket/10002"
|
47
49
|
"""
|
48
50
|
And a file named "queue/20.transformed/sap_solution_manager_defects.SAME_CONTENT.csv" with:
|
49
51
|
"""
|
50
|
-
"ID","Status","Resolution","Priority","Summary"
|
51
|
-
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)"
|
52
|
-
"10002","In Process","","Highest","SMAN-10002 | Database deadlock during order processing (10002)"
|
52
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
53
|
+
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)","http://url/to/source/ticket/10001"
|
54
|
+
"10002","In Process","","Highest","SMAN-10002 | Database deadlock during order processing (10002)","http://url/to/source/ticket/10002"
|
53
55
|
"""
|
54
56
|
When I successfully run `ticket-replicator --load`
|
55
57
|
Then the Jira project should only have the following tickets:
|
56
|
-
| status | resolution | priority | summary |
|
57
|
-
| Open | | Highest | SMAN-10001 \| Login page randomly fails to load CSS assets (10001) |
|
58
|
-
| In Process | | Highest | SMAN-10002 \| Database deadlock during order processing (10002) |
|
59
|
-
And the source ticket URL is found in the ticket descriptions of those tickets
|
58
|
+
| status | resolution | priority | summary | source_ticket_url |
|
59
|
+
| Open | | Highest | SMAN-10001 \| Login page randomly fails to load CSS assets (10001) | http://url/to/source/ticket/10001 |
|
60
|
+
| In Process | | Highest | SMAN-10002 \| Database deadlock during order processing (10002) | http://url/to/source/ticket/10002 |
|
60
61
|
|
61
62
|
Scenario: Tickets are updated only if changes happened or they were not replicated before
|
62
63
|
Given a file named "queue/20.transformed/2025-03-29.16h16.sap_solution_manager_defects.csv" with:
|
63
64
|
"""
|
64
|
-
"ID","Status","Resolution","Priority","Summary"
|
65
|
-
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)"
|
66
|
-
"10002","In Process","Cannot Reproduce","Medium","SMAN-10002 | Database deadlock during order processing (10002)"
|
67
|
-
"10016","Confirmed","Done","High","SMAN-10016 | Slow response time on product search API (10016)"
|
65
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
66
|
+
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)","http://url/to/source/ticket/10001"
|
67
|
+
"10002","In Process","Cannot Reproduce","Medium","SMAN-10002 | Database deadlock during order processing (10002)","http://url/to/source/ticket/10002"
|
68
|
+
"10016","Confirmed","Done","High","SMAN-10016 | Slow response time on product search API (10016)","http://url/to/source/ticket/10016"
|
68
69
|
"""
|
69
70
|
And I successfully run `ticket-replicator --load`
|
70
71
|
When a file named "queue/20.transformed/2025-03-29.19h56.update_tickets.csv" with:
|
71
72
|
"""
|
72
|
-
"ID","Status","Resolution","Priority","Summary"
|
73
|
-
"10001","Closed","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)"
|
74
|
-
"10002","In Process","","High","SMAN-10002 | *Recurring* database deadlock during order processing (10002)"
|
75
|
-
"10008","Open","","Highest","SMAN-10008 | Memory leak in caching implementation (10008)"
|
76
|
-
"10016","Confirmed","Done","High","SMAN-10016 | Slow response time on product search API (10016)"
|
73
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
74
|
+
"10001","Closed","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)","http://url/to/source/ticket/10001"
|
75
|
+
"10002","In Process","","High","SMAN-10002 | *Recurring* database deadlock during order processing (10002)","http://url/to/source/ticket/10002"
|
76
|
+
"10008","Open","","Highest","SMAN-10008 | Memory leak in caching implementation (10008)","http://url/to/source/ticket/10008"
|
77
|
+
"10016","Confirmed","Done","High","SMAN-10016 | Slow response time on product search API (10016)","http://url/to/source/ticket/10016"
|
77
78
|
"""
|
78
79
|
And I successfully run `ticket-replicator --jira-http-debug --load`
|
79
80
|
Then the Jira project should only have the following tickets:
|
80
|
-
| example purpose
|
81
|
-
| one update due to status change
|
82
|
-
| one update due to priority, resolution and summary updates
|
83
|
-
|
|
84
|
-
| no update since no status or field change
|
85
|
-
And only
|
81
|
+
| example purpose | status | resolution | priority | summary | source_ticket_url |
|
82
|
+
| one update due to status change | Closed | | Highest | SMAN-10001 \| Login page randomly fails to load CSS assets (10001) | http://url/to/source/ticket/10001 |
|
83
|
+
| one update due to priority, resolution and summary updates | In Process | | High | SMAN-10002 \| *Recurring* database deadlock during order processing (10002) | http://url/to/source/ticket/10002 |
|
84
|
+
| three updates due to new ticket created (fields, source ticket link and status) | Open | | Highest | SMAN-10008 \| Memory leak in caching implementation (10008) | http://url/to/source/ticket/10008 |
|
85
|
+
| no update since no status or field change | Confirmed | Done | High | SMAN-10016 \| Slow response time on product search API (10016) | http://url/to/source/ticket/10016 |
|
86
|
+
And only 5 Jira update requests were emitted
|
86
87
|
|
87
88
|
Scenario: Attempting to set an unexpected status generates information about the current row being loaded
|
88
89
|
Given a file named "queue/20.transformed/sap_solution_manager_defects.csv" with:
|
89
90
|
"""
|
90
|
-
"ID","Status","Resolution","Priority","Summary"
|
91
|
-
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)"
|
92
|
-
"10002","In Process","","Highest","SMAN-10002 | Database deadlock during order processing (10002)"
|
93
|
-
"10003","Confirmed","Done","Medium","SMAN-10003 | Invalid date format in SOAP response (10003)"
|
94
|
-
"10004","____ INEXISTING STATUS ____","Done","Low","SMAN-10004 | App crashes when offline on Android 12 (10004)"
|
95
|
-
"10005","Open","","High","SMAN-10005 | Session tokens not properly invalidated (10005)"
|
96
|
-
"10006","Solution Proposal","","Medium","SMAN-10006 | Jenkins pipeline timeout on large builds (10006)"
|
97
|
-
"10007","Withdrawn","Won't Do","Low","SMAN-10007 | Test data missing edge case scenarios (10007)"
|
98
|
-
"10008","Open","","Highest","SMAN-10008 | Memory leak in caching implementation (10008)"
|
99
|
-
"10009","Wait on External","","Medium","SMAN-10009 | Inconsistent button styles across modules (10009)"
|
91
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
92
|
+
"10001","Open","","Highest","SMAN-10001 | Login page randomly fails to load CSS assets (10001)","http://url/to/source/ticket/10001"
|
93
|
+
"10002","In Process","","Highest","SMAN-10002 | Database deadlock during order processing (10002)","http://url/to/source/ticket/10002"
|
94
|
+
"10003","Confirmed","Done","Medium","SMAN-10003 | Invalid date format in SOAP response (10003)","http://url/to/source/ticket/10003"
|
95
|
+
"10004","____ INEXISTING STATUS ____","Done","Low","SMAN-10004 | App crashes when offline on Android 12 (10004)","http://url/to/source/ticket/10004"
|
96
|
+
"10005","Open","","High","SMAN-10005 | Session tokens not properly invalidated (10005)","http://url/to/source/ticket/10005"
|
97
|
+
"10006","Solution Proposal","","Medium","SMAN-10006 | Jenkins pipeline timeout on large builds (10006)","http://url/to/source/ticket/10006"
|
98
|
+
"10007","Withdrawn","Won't Do","Low","SMAN-10007 | Test data missing edge case scenarios (10007)","http://url/to/source/ticket/10007"
|
99
|
+
"10008","Open","","Highest","SMAN-10008 | Memory leak in caching implementation (10008)","http://url/to/source/ticket/10008"
|
100
|
+
"10009","Wait on External","","Medium","SMAN-10009 | Inconsistent button styles across modules (10009)","http://url/to/source/ticket/10009"
|
100
101
|
"""
|
101
102
|
When I run `ticket-replicator --load`
|
102
103
|
Then it should fail with:
|
103
104
|
"""
|
104
105
|
ERROR Object : Ticket::Replicator::FileLoader::LoadError: queue/20.transformed/sap_solution_manager_defects.csv:5: error while loading row:
|
105
106
|
No transition found for "____ INEXISTING STATUS ____" in ["No Error -> No Error", "Tester Action -> Tester Action", "Close Bug -> Closed", "Confirmed -> Confirmed", "Defect Correction in Process -> Defect Correction in Process", "Deferred -> Deferred", "Forwarded -> Forwarded", "Information Required -> Information Required", "New -> New", "Open -> Open", "Solution Proposal -> Solution Proposal", "Wait for Defect Correction -> Wait for Defect Correction", "Wait on External -> Wait on External", "Withdrawn -> Withdrawn", "In Process -> In Process"].:
|
106
|
-
#<CSV::Row id:"10004" status:"____ INEXISTING STATUS ____" resolution:"Done" priority:"Low" summary:"SMAN-10004 | App crashes when offline on Android 12 (10004)">
|
107
|
+
#<CSV::Row id:"10004" status:"____ INEXISTING STATUS ____" resolution:"Done" priority:"Low" summary:"SMAN-10004 | App crashes when offline on Android 12 (10004)" source_ticket_url:"http://url/to/source/ticket/10004">
|
107
108
|
"""
|
108
109
|
|
109
110
|
Scenario: Attempting to set an unexpected priority generates information about the current row being loaded
|
110
111
|
Given a file named "queue/20.transformed/sap_solution_manager_defects.csv" with:
|
111
112
|
"""
|
112
|
-
"ID","Status","Resolution","Priority","Summary"
|
113
|
-
"
|
114
|
-
"
|
115
|
-
"10003","Confirmed","Done","Medium","SMAN-10003 | Invalid date format in SOAP response (10003)"
|
116
|
-
"10004","Closed","Done","Low","SMAN-10004 | App crashes when offline on Android 12 (10004)"
|
117
|
-
"10005","Open","","High","SMAN-10005 | Session tokens not properly invalidated (10005)"
|
118
|
-
"10006","Solution Proposal","","Medium","SMAN-10006 | Jenkins pipeline timeout on large builds (10006)"
|
119
|
-
"10007","Withdrawn","Won't Do","Low","SMAN-10007 | Test data missing edge case scenarios (10007)"
|
120
|
-
"10008","Open","","____ UNEXPECTED PRIORITY ____","SMAN-10008 | Memory leak in caching implementation (10008)"
|
121
|
-
"10009","Wait on External","","Medium","SMAN-10009 | Inconsistent button styles across modules (10009)"
|
113
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
114
|
+
"10008","Open","","____ UNEXPECTED PRIORITY ____","SMAN-10008 | Memory leak in caching implementation (10008)","http://url/to/source/ticket/10008"
|
115
|
+
"10009","Wait on External","","Medium","SMAN-10009 | Inconsistent button styles across modules (10009)","http://url/to/source/ticket/10009"
|
122
116
|
"""
|
123
117
|
When I run `ticket-replicator --load`
|
124
118
|
Then it should fail with:
|
125
119
|
"""
|
126
|
-
ERROR Object : Ticket::Replicator::FileLoader::LoadError: queue/20.transformed/sap_solution_manager_defects.csv:
|
120
|
+
ERROR Object : Ticket::Replicator::FileLoader::LoadError: queue/20.transformed/sap_solution_manager_defects.csv:2: error while loading row:
|
127
121
|
Bad Request:
|
128
|
-
#<CSV::Row id:"10008" status:"Open" resolution:"" priority:"____ UNEXPECTED PRIORITY ____" summary:"SMAN-10008 | Memory leak in caching implementation (10008)">
|
122
|
+
#<CSV::Row id:"10008" status:"Open" resolution:"" priority:"____ UNEXPECTED PRIORITY ____" summary:"SMAN-10008 | Memory leak in caching implementation (10008)" source_ticket_url:"http://url/to/source/ticket/10008">
|
129
123
|
"""
|
124
|
+
|
125
|
+
|
@@ -5,9 +5,8 @@ Feature: Transform SAP tickets to Jira format
|
|
5
5
|
|
6
6
|
Background:
|
7
7
|
Given the following environment variables have been set:
|
8
|
-
| name
|
9
|
-
|
|
10
|
-
|
8
|
+
| name | value |
|
9
|
+
| TICKET_REPLICATOR_SOURCE_TICKET_URL | http://url/to/source/ticket/<%= source_ticket_id %> |
|
11
10
|
|
12
11
|
Scenario: Transform valid ticket CSV
|
13
12
|
Given a file named "config/ticket-replicator.mappings.yml" with:
|
@@ -62,17 +61,17 @@ Feature: Transform SAP tickets to Jira format
|
|
62
61
|
When I successfully run `ticket-replicator --transform`
|
63
62
|
Then a file named "queue/20.transformed/sap_solution_manager_defects.csv" should contain exactly:
|
64
63
|
"""
|
65
|
-
"ID","Status","Resolution","Priority","Summary"
|
66
|
-
"10001","Open","","High","SMAN-10001 | Login page randomly fails to load CSS assets (10001)"
|
67
|
-
"10002","In Progress","","Highest","SMAN-10002 | Database deadlock during order processing (10002)"
|
68
|
-
"10003","Resolved","Done","Medium","SMAN-10003 | Invalid date format in SOAP response (10003)"
|
69
|
-
"10004","Closed","Done","Low","SMAN-10004 | App crashes when offline on Android 12 (10004)"
|
70
|
-
"10005","Open","","High","SMAN-10005 | Session tokens not properly invalidated (10005)"
|
71
|
-
"10006","In Review","","Medium","SMAN-10006 | Jenkins pipeline timeout on large builds (10006)"
|
72
|
-
"10007","Closed","Won't Do","Low","SMAN-10007 | Test data missing edge case scenarios (10007)"
|
73
|
-
"10008","Open","","Highest","SMAN-10008 | Memory leak in caching implementation (10008)"
|
74
|
-
"10009","Blocked","","Medium","SMAN-10009 | Inconsistent button styles across modules (10009)"
|
75
|
-
"10010","Resolved","Done","High","SMAN-10010 | Slow response time on product search API (10010)"
|
64
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
65
|
+
"10001","Open","","High","SMAN-10001 | Login page randomly fails to load CSS assets (10001)","http://url/to/source/ticket/10001"
|
66
|
+
"10002","In Progress","","Highest","SMAN-10002 | Database deadlock during order processing (10002)","http://url/to/source/ticket/10002"
|
67
|
+
"10003","Resolved","Done","Medium","SMAN-10003 | Invalid date format in SOAP response (10003)","http://url/to/source/ticket/10003"
|
68
|
+
"10004","Closed","Done","Low","SMAN-10004 | App crashes when offline on Android 12 (10004)","http://url/to/source/ticket/10004"
|
69
|
+
"10005","Open","","High","SMAN-10005 | Session tokens not properly invalidated (10005)","http://url/to/source/ticket/10005"
|
70
|
+
"10006","In Review","","Medium","SMAN-10006 | Jenkins pipeline timeout on large builds (10006)","http://url/to/source/ticket/10006"
|
71
|
+
"10007","Closed","Won't Do","Low","SMAN-10007 | Test data missing edge case scenarios (10007)","http://url/to/source/ticket/10007"
|
72
|
+
"10008","Open","","Highest","SMAN-10008 | Memory leak in caching implementation (10008)","http://url/to/source/ticket/10008"
|
73
|
+
"10009","Blocked","","Medium","SMAN-10009 | Inconsistent button styles across modules (10009)","http://url/to/source/ticket/10009"
|
74
|
+
"10010","Resolved","Done","High","SMAN-10010 | Slow response time on product search API (10010)","http://url/to/source/ticket/10010"
|
76
75
|
"""
|
77
76
|
|
78
77
|
Scenario: Transform valid ticket Excel
|
@@ -127,24 +126,24 @@ Feature: Transform SAP tickets to Jira format
|
|
127
126
|
When I successfully run `ticket-replicator --transform`
|
128
127
|
Then a file named "queue/20.transformed/sap_solution_manager_defects.csv" should contain exactly:
|
129
128
|
"""
|
130
|
-
"ID","Status","Resolution","Priority","Summary"
|
131
|
-
"3000017049","Closed","Done","Medium","SMAN-3000017049 | Summary"
|
132
|
-
"9400011377","Confirmed","","Low","SMAN-9400011377 | Summary"
|
133
|
-
"3000016618","Defect Correction in Process","","Medium","SMAN-3000016618 | Summary"
|
134
|
-
"9400013805","Deferred","","Medium","SMAN-9400013805 | Summary"
|
135
|
-
"9400013816","Forwarded","","High","SMAN-9400013816 | Summary"
|
136
|
-
"9400011382","In Process","","Medium","SMAN-9400011382 | Summary"
|
137
|
-
"9400011393","Information Required","","Medium","SMAN-9400011393 | Summary"
|
138
|
-
"9400011381","New","","Medium","SMAN-9400011381 | Summary"
|
139
|
-
"3000016617","No Error","","Medium","SMAN-3000016617 | Summary"
|
140
|
-
"9400011372","Open","","Highest","SMAN-9400011372 | Summary"
|
141
|
-
"9400011403","Solution Proposal","","Medium","SMAN-9400011403 | Summary"
|
142
|
-
"9400011380","Tester Action","","Medium","SMAN-9400011380 | Summary"
|
143
|
-
"9400011705","Wait for Defect Correction","","Medium","SMAN-9400011705 | Summary"
|
144
|
-
"9400011437","Wait on External","","Medium","SMAN-9400011437 | Summary"
|
145
|
-
"9400011466","Withdrawn","Cannot Reproduce","Medium","SMAN-9400011466 | Summary"
|
146
|
-
"3000017667","Closed","Done","Medium","SMAN-3000017667 | Summary"
|
147
|
-
"3000018423","No Error","","Medium","SMAN-3000018423 | Summary"
|
129
|
+
"ID","Status","Resolution","Priority","Summary","Source Ticket URL"
|
130
|
+
"3000017049","Closed","Done","Medium","SMAN-3000017049 | Summary","http://url/to/source/ticket/3000017049"
|
131
|
+
"9400011377","Confirmed","","Low","SMAN-9400011377 | Summary","http://url/to/source/ticket/9400011377"
|
132
|
+
"3000016618","Defect Correction in Process","","Medium","SMAN-3000016618 | Summary","http://url/to/source/ticket/3000016618"
|
133
|
+
"9400013805","Deferred","","Medium","SMAN-9400013805 | Summary","http://url/to/source/ticket/9400013805"
|
134
|
+
"9400013816","Forwarded","","High","SMAN-9400013816 | Summary","http://url/to/source/ticket/9400013816"
|
135
|
+
"9400011382","In Process","","Medium","SMAN-9400011382 | Summary","http://url/to/source/ticket/9400011382"
|
136
|
+
"9400011393","Information Required","","Medium","SMAN-9400011393 | Summary","http://url/to/source/ticket/9400011393"
|
137
|
+
"9400011381","New","","Medium","SMAN-9400011381 | Summary","http://url/to/source/ticket/9400011381"
|
138
|
+
"3000016617","No Error","","Medium","SMAN-3000016617 | Summary","http://url/to/source/ticket/3000016617"
|
139
|
+
"9400011372","Open","","Highest","SMAN-9400011372 | Summary","http://url/to/source/ticket/9400011372"
|
140
|
+
"9400011403","Solution Proposal","","Medium","SMAN-9400011403 | Summary","http://url/to/source/ticket/9400011403"
|
141
|
+
"9400011380","Tester Action","","Medium","SMAN-9400011380 | Summary","http://url/to/source/ticket/9400011380"
|
142
|
+
"9400011705","Wait for Defect Correction","","Medium","SMAN-9400011705 | Summary","http://url/to/source/ticket/9400011705"
|
143
|
+
"9400011437","Wait on External","","Medium","SMAN-9400011437 | Summary","http://url/to/source/ticket/9400011437"
|
144
|
+
"9400011466","Withdrawn","Cannot Reproduce","Medium","SMAN-9400011466 | Summary","http://url/to/source/ticket/9400011466"
|
145
|
+
"3000017667","Closed","Done","Medium","SMAN-3000017667 | Summary","http://url/to/source/ticket/3000017667"
|
146
|
+
"3000018423","No Error","","Medium","SMAN-3000018423 | Summary","http://url/to/source/ticket/3000018423"
|
148
147
|
"""
|
149
148
|
|
150
149
|
Scenario: Processing an invalid Excel file generates an error with relevant information
|
@@ -8,6 +8,10 @@ Feature: Transform and load extracted ticket queue
|
|
8
8
|
| name |
|
9
9
|
| TICKET_REPLICATOR_JIRA_PROJECT_KEY |
|
10
10
|
| TICKET_REPLICATOR_JIRA_TICKET_TYPE_NAME |
|
11
|
+
And the following environment variables have been set:
|
12
|
+
| name | value |
|
13
|
+
| TICKET_REPLICATOR_SOURCE_TICKET_URL | http://url/to/source/ticket/<%= source_ticket_id %> |
|
14
|
+
|
11
15
|
And a file named "config/ticket-replicator.mappings.yml" with:
|
12
16
|
"""
|
13
17
|
field_mapping:
|
@@ -57,20 +61,20 @@ Feature: Transform and load extracted ticket queue
|
|
57
61
|
And the current date time is "2025-05-10 07:52:00 UTC"
|
58
62
|
When I successfully run `ticket-replicator --ticket-queue-transform-and-load`
|
59
63
|
Then the Jira project should only have the following tickets:
|
60
|
-
| status | resolution | priority | summary |
|
61
|
-
| Closed | Done | Medium | SMAN-3000017049 \| Summary |
|
62
|
-
| Confirmed | | Low | SMAN-9400011377 \| Summary |
|
63
|
-
| Defect Correction in Process | | Medium | SMAN-3000016618 \| Summary |
|
64
|
-
| Deferred | | Medium | SMAN-9400013805 \| Summary |
|
65
|
-
| Forwarded | | High | SMAN-9400013816 \| Summary |
|
66
|
-
| In Process | | Medium | SMAN-9400011382 \| Summary |
|
67
|
-
| Information Required | | Medium | SMAN-9400011393 \| Summary |
|
68
|
-
| Withdrawn | Won't Do | Medium | SMAN-9400011381 \| Summary |
|
69
|
-
| No Error | | Medium | SMAN-3000016617 \| Summary |
|
70
|
-
| Open | | Highest | SMAN-9400011372 \| Summary |
|
71
|
-
| Withdrawn | Won't Do | Medium | SMAN-9400011466 \| Summary |
|
72
|
-
| Closed | Done | Medium | SMAN-3000017667 \| Summary |
|
73
|
-
| No Error | | Medium | SMAN-3000018423 \| Summary |
|
64
|
+
| status | resolution | priority | summary | source_ticket_url |
|
65
|
+
| Closed | Done | Medium | SMAN-3000017049 \| Summary | http://url/to/source/ticket/3000017049 |
|
66
|
+
| Confirmed | | Low | SMAN-9400011377 \| Summary | http://url/to/source/ticket/9400011377 |
|
67
|
+
| Defect Correction in Process | | Medium | SMAN-3000016618 \| Summary | http://url/to/source/ticket/3000016618 |
|
68
|
+
| Deferred | | Medium | SMAN-9400013805 \| Summary | http://url/to/source/ticket/9400013805 |
|
69
|
+
| Forwarded | | High | SMAN-9400013816 \| Summary | http://url/to/source/ticket/9400013816 |
|
70
|
+
| In Process | | Medium | SMAN-9400011382 \| Summary | http://url/to/source/ticket/9400011382 |
|
71
|
+
| Information Required | | Medium | SMAN-9400011393 \| Summary | http://url/to/source/ticket/9400011393 |
|
72
|
+
| Withdrawn | Won't Do | Medium | SMAN-9400011381 \| Summary | http://url/to/source/ticket/9400011381 |
|
73
|
+
| No Error | | Medium | SMAN-3000016617 \| Summary | http://url/to/source/ticket/3000016617 |
|
74
|
+
| Open | | Highest | SMAN-9400011372 \| Summary | http://url/to/source/ticket/9400011372 |
|
75
|
+
| Withdrawn | Won't Do | Medium | SMAN-9400011466 \| Summary | http://url/to/source/ticket/9400011466 |
|
76
|
+
| Closed | Done | Medium | SMAN-3000017667 \| Summary | http://url/to/source/ticket/3000017667 |
|
77
|
+
| No Error | | Medium | SMAN-3000018423 \| Summary | http://url/to/source/ticket/3000018423 |
|
74
78
|
# TODO: And the source ticket URL is found in the ticket descriptions of those tickets
|
75
79
|
And a file named "queue/30.archived/2025-05-10.07h52m00.sap_solution_manager_defects.xlsx" should exist
|
76
80
|
And the file named "queue/10.extracted/sap_solution_manager_defects.xlsx" should not exist anymore
|
@@ -20,6 +20,7 @@ module Ticket
|
|
20
20
|
|
21
21
|
def run
|
22
22
|
save_ticket
|
23
|
+
update_source_ticket_remote_link
|
23
24
|
transition_ticket_to_the_expected_status
|
24
25
|
end
|
25
26
|
|
@@ -70,6 +71,53 @@ module Ticket
|
|
70
71
|
%i[summary priority]
|
71
72
|
end
|
72
73
|
|
74
|
+
def update_source_ticket_remote_link
|
75
|
+
return unless source_ticket_link_needs_update?
|
76
|
+
|
77
|
+
ticket.source_ticket_link&.delete
|
78
|
+
|
79
|
+
ticket.jira_ticket.remotelink.build.save!(ticket_link_attributes)
|
80
|
+
end
|
81
|
+
|
82
|
+
# rubocop:disable Metrics/MethodLength
|
83
|
+
def source_ticket_link_needs_update?
|
84
|
+
existing_ticket_link_attributes = ticket.source_ticket_link&.attrs
|
85
|
+
|
86
|
+
object = existing_ticket_link_attributes&.[]("object")
|
87
|
+
existing_url = object&.[]("url")
|
88
|
+
existing_title = object&.[]("title")
|
89
|
+
|
90
|
+
filtered_existing_ticket_link_attributes = ticket_link_attributes(existing_url, existing_title)
|
91
|
+
|
92
|
+
log.debug do
|
93
|
+
<<~EOLOG
|
94
|
+
|
95
|
+
existing_ticket_link_attributes = #{existing_ticket_link_attributes.inspect}
|
96
|
+
filtered_existing_ticket_link_attributes = #{filtered_existing_ticket_link_attributes}
|
97
|
+
ticket_link_attributes: #{ticket_link_attributes.inspect}
|
98
|
+
EOLOG
|
99
|
+
end
|
100
|
+
|
101
|
+
filtered_existing_ticket_link_attributes != ticket_link_attributes
|
102
|
+
end
|
103
|
+
|
104
|
+
# rubocop:enable Metrics/MethodLength
|
105
|
+
|
106
|
+
def source_ticket_link_title
|
107
|
+
"Source Ticket #{id}"
|
108
|
+
end
|
109
|
+
|
110
|
+
def ticket_link_attributes(url = source_ticket_url, title = source_ticket_link_title)
|
111
|
+
self.class.build_ticket_link_attributes(url, title)
|
112
|
+
end
|
113
|
+
|
114
|
+
def self.build_ticket_link_attributes(
|
115
|
+
url, title, application_name = Ticket::SOURCE_TICKET_REMOTE_LINK_APPLICATION
|
116
|
+
)
|
117
|
+
{ "object" => { "url" => url, "title" => title } }
|
118
|
+
.merge(application_name ? { "application" => { "name" => application_name } } : {})
|
119
|
+
end
|
120
|
+
|
73
121
|
def transition_ticket_to_the_expected_status
|
74
122
|
ticket.transition_to(status)
|
75
123
|
end
|
@@ -115,7 +163,8 @@ module Ticket
|
|
115
163
|
|
116
164
|
class << self
|
117
165
|
def fields_to_load
|
118
|
-
@fields_to_load ||=
|
166
|
+
@fields_to_load ||=
|
167
|
+
RowTransformer.fields_to_transform.collect { |field| field.to_s.parameterize.underscore.to_sym }
|
119
168
|
end
|
120
169
|
end
|
121
170
|
end
|
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require "ticket/replicator/replicated_summary"
|
4
|
+
require "erb"
|
4
5
|
|
5
6
|
module Ticket
|
6
7
|
class Replicator
|
@@ -18,13 +19,25 @@ module Ticket
|
|
18
19
|
end
|
19
20
|
|
20
21
|
def run
|
21
|
-
fields_to_transform.collect { |field| send("transformed_#{field.to_s.
|
22
|
+
fields_to_transform.collect { |field| send("transformed_#{field.to_s.parameterize.underscore}") }
|
22
23
|
end
|
23
24
|
|
24
25
|
def transformed_id
|
25
26
|
remapped_field_extracted_value_for :id
|
26
27
|
end
|
27
28
|
|
29
|
+
def transformed_source_ticket_url
|
30
|
+
source_ticket_url_builder(transformed_id)
|
31
|
+
end
|
32
|
+
|
33
|
+
def source_ticket_url_builder(source_ticket_id)
|
34
|
+
ERB.new(ticket_replicator_source_ticket_url).result(binding)
|
35
|
+
end
|
36
|
+
|
37
|
+
def ticket_replicator_source_ticket_url
|
38
|
+
ENV.fetch("TICKET_REPLICATOR_SOURCE_TICKET_URL") { |name| raise "#{name}: not set in environment!" }
|
39
|
+
end
|
40
|
+
|
28
41
|
def transformed_status
|
29
42
|
mapped_value_for :status
|
30
43
|
end
|
@@ -50,7 +63,15 @@ module Ticket
|
|
50
63
|
self.class.fields_to_transform
|
51
64
|
end
|
52
65
|
|
66
|
+
def fields_to_remap
|
67
|
+
self.class.fields_to_remap
|
68
|
+
end
|
69
|
+
|
53
70
|
def self.fields_to_transform
|
71
|
+
fields_to_remap + ["Source Ticket URL"]
|
72
|
+
end
|
73
|
+
|
74
|
+
def self.fields_to_remap
|
54
75
|
%i[ID Status Resolution Priority Summary]
|
55
76
|
end
|
56
77
|
|
@@ -77,7 +98,7 @@ module Ticket
|
|
77
98
|
end
|
78
99
|
|
79
100
|
def remapped_field_extracted_row
|
80
|
-
@remapped_field_extracted_row ||=
|
101
|
+
@remapped_field_extracted_row ||= fields_to_remap.to_h do |field|
|
81
102
|
mapped_field_key = remapped_field_key(field)
|
82
103
|
[mapped_field_key, extracted_row_value_for(mapped_field_key)]
|
83
104
|
end
|
@@ -36,8 +36,25 @@ module Ticket
|
|
36
36
|
ReplicatedSummary.parse(summary).source_id
|
37
37
|
end
|
38
38
|
|
39
|
+
SOURCE_TICKET_REMOTE_LINK_APPLICATION = "Ticket Source"
|
40
|
+
|
41
|
+
def source_ticket_link
|
42
|
+
@source_ticket_link ||=
|
43
|
+
jira_ticket.remotelink.all.find do |link|
|
44
|
+
source_ticket_link_attributes(link)&.[]("application")&.[]("name") == SOURCE_TICKET_REMOTE_LINK_APPLICATION
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def source_ticket_url
|
49
|
+
source_ticket_link_attributes(source_ticket_link)&.[]("object")&.[]("url")
|
50
|
+
end
|
51
|
+
|
39
52
|
private
|
40
53
|
|
54
|
+
def source_ticket_link_attributes(link)
|
55
|
+
link&.attrs
|
56
|
+
end
|
57
|
+
|
41
58
|
def cmp_values(object)
|
42
59
|
object&.source_id
|
43
60
|
end
|
@@ -43,7 +43,8 @@ module Ticket
|
|
43
43
|
|
44
44
|
describe "#transformed_headers" do
|
45
45
|
it "returns the headers" do
|
46
|
-
expect(transformer.transformed_headers)
|
46
|
+
expect(transformer.transformed_headers)
|
47
|
+
.to eq(%w[ID Status Resolution Priority Summary] + ["Source Ticket URL"])
|
47
48
|
end
|
48
49
|
end
|
49
50
|
|
@@ -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
|
@@ -187,6 +188,30 @@ module Ticket
|
|
187
188
|
end
|
188
189
|
end
|
189
190
|
|
191
|
+
describe "#build_ticket_link_attributes" do
|
192
|
+
it "builds the link attributes with a default application" do
|
193
|
+
expect(described_class.send(:build_ticket_link_attributes,
|
194
|
+
"https://url/to/source/ticket/123", "Source Ticket 123"))
|
195
|
+
.to eq({ "object" => { "url" => "https://url/to/source/ticket/123", "title" => "Source Ticket 123" },
|
196
|
+
"application" => { "name" => "Ticket Source" } })
|
197
|
+
end
|
198
|
+
|
199
|
+
it "builds the link attributes with a custom application" do
|
200
|
+
expect(described_class.send(:build_ticket_link_attributes,
|
201
|
+
"https://url/to/source/ticket/123", "Source Ticket 123",
|
202
|
+
"A custom application"))
|
203
|
+
.to eq({ "object" => { "url" => "https://url/to/source/ticket/123", "title" => "Source Ticket 123" },
|
204
|
+
"application" => { "name" => "A custom application" } })
|
205
|
+
end
|
206
|
+
|
207
|
+
it "builds the link attributes with no application" do
|
208
|
+
expect(described_class.send(:build_ticket_link_attributes,
|
209
|
+
"https://url/to/source/ticket/123", "Source Ticket 123",
|
210
|
+
nil))
|
211
|
+
.to eq({ "object" => { "url" => "https://url/to/source/ticket/123", "title" => "Source Ticket 123" } })
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
190
215
|
describe "#ticket_fields_need_to_be_updated?" do
|
191
216
|
before do
|
192
217
|
allow(loader).to receive_messages(ticket_previously_replicated?: ticket_previously_replicated?,
|
@@ -275,21 +300,139 @@ module Ticket
|
|
275
300
|
end
|
276
301
|
end
|
277
302
|
|
278
|
-
|
303
|
+
# rubocop:disable Metrics/BlockLength
|
304
|
+
context "when caring for the source ticket link" do
|
305
|
+
def build_link(url, title, application_name = "Ticket Source")
|
306
|
+
RowLoader.build_ticket_link_attributes(url, title, application_name)
|
307
|
+
end
|
308
|
+
|
279
309
|
let(:loader) { described_class.send(:new, jira_project, row) }
|
280
|
-
let(:row) { { id: "123",
|
310
|
+
let(:row) { { id: "123", source_ticket_url: "https://url/to/source/ticket/123" } }
|
311
|
+
let(:ticket) { instance_double(Ticket, jira_ticket: jira_ticket) }
|
312
|
+
let(:jira_ticket) { double(JIRA::Resource::Issue, remotelink: double(build: remotelink)) }
|
313
|
+
let(:remotelink) { instance_double(JIRA::Resource::Remotelink) }
|
281
314
|
|
282
|
-
|
315
|
+
before { allow(loader).to receive_messages(ticket: ticket) }
|
283
316
|
|
284
|
-
|
285
|
-
|
286
|
-
|
317
|
+
let(:expected_link) { build_link("https://url/to/source/ticket/123", "Source Ticket 123") }
|
318
|
+
|
319
|
+
describe "#source_ticket_link_title" do
|
320
|
+
it { expect(loader.source_ticket_link_title).to eq("Source Ticket 123") }
|
321
|
+
end
|
322
|
+
|
323
|
+
describe "#update_source_ticket_remote_link" do
|
324
|
+
before do
|
325
|
+
allow(loader).to receive_messages(source_ticket_link_needs_update?: needs_update)
|
326
|
+
end
|
327
|
+
|
328
|
+
context "when the link does not exist" do
|
329
|
+
let(:needs_update) { true }
|
330
|
+
|
331
|
+
before { allow(ticket).to receive_messages(source_ticket_link: nil) }
|
332
|
+
|
333
|
+
it "setups the link" do
|
334
|
+
expect(remotelink).to receive(:save!).with(expected_link)
|
335
|
+
|
336
|
+
loader.update_source_ticket_remote_link
|
337
|
+
end
|
338
|
+
end
|
339
|
+
|
340
|
+
context "when the link needs to be updated" do
|
341
|
+
let(:needs_update) { true }
|
342
|
+
let(:source_ticket_link) { instance_double(JIRA::Resource::Remotelink) }
|
343
|
+
|
344
|
+
before { allow(ticket).to receive_messages(source_ticket_link: source_ticket_link) }
|
345
|
+
|
346
|
+
it "deletes the previous link and creates a new one" do
|
347
|
+
expect(source_ticket_link).to receive(:delete)
|
348
|
+
expect(remotelink).to receive(:save!).with(expected_link)
|
349
|
+
|
350
|
+
loader.update_source_ticket_remote_link
|
351
|
+
end
|
352
|
+
end
|
353
|
+
|
354
|
+
context "when the link does not need to be updated" do
|
355
|
+
let(:needs_update) { false }
|
287
356
|
|
288
|
-
|
357
|
+
it "no link creation happens" do
|
358
|
+
expect(remotelink).not_to receive(:save!)
|
359
|
+
|
360
|
+
loader.update_source_ticket_remote_link
|
361
|
+
end
|
362
|
+
end
|
363
|
+
end
|
364
|
+
|
365
|
+
describe "#source_ticket_link_needs_update?" do
|
366
|
+
before do
|
367
|
+
allow(ticket).to receive_messages(source_ticket_link: remote_link)
|
368
|
+
end
|
369
|
+
|
370
|
+
context "when link has yet to be created" do
|
371
|
+
let(:remote_link) { nil }
|
372
|
+
|
373
|
+
it { expect(loader.source_ticket_link_needs_update?).to be_truthy }
|
374
|
+
end
|
375
|
+
|
376
|
+
context "when link already exists" do
|
377
|
+
let(:remote_link) do
|
378
|
+
instance_double(JIRA::Resource::Remotelink,
|
379
|
+
attrs: source_ticket_link,
|
380
|
+
inspect: "<JIRA::Resource::Remotelink attrs: #{source_ticket_link}>")
|
381
|
+
end
|
382
|
+
|
383
|
+
context "when the link is not up to date" do
|
384
|
+
let(:source_ticket_link) { build_link(existing_url, existing_title) }
|
385
|
+
|
386
|
+
context "when url has changed" do
|
387
|
+
let(:existing_url) { "https://__OLD_URL__/to/source/ticket/1234" }
|
388
|
+
let(:existing_title) { "Source Ticket 123" }
|
389
|
+
|
390
|
+
it { expect(loader.source_ticket_link_needs_update?).to be_truthy }
|
391
|
+
end
|
392
|
+
|
393
|
+
context "when title has changed" do
|
394
|
+
let(:existing_url) { "https://url/to/source/ticket/123" }
|
395
|
+
let(:existing_title) { "__OLD TITLE__Source Ticket 1234" }
|
396
|
+
|
397
|
+
it { expect(loader.source_ticket_link_needs_update?).to be_truthy }
|
398
|
+
end
|
399
|
+
end
|
400
|
+
|
401
|
+
context "when the link is up to date" do
|
402
|
+
let(:existing_url) { "https://url/to/source/ticket/123" }
|
403
|
+
let(:existing_title) { "Source Ticket 123" }
|
404
|
+
let(:source_ticket_link) do
|
405
|
+
{ "object" => {
|
406
|
+
"url" => "https://url/to/source/ticket/123",
|
407
|
+
"title" => "Source Ticket 123",
|
408
|
+
"icon" => { "title" => "link", "url16x16" => "https://url/to/source/ticket/123/icon" }
|
409
|
+
},
|
410
|
+
"application" => { "name" => "Ticket Source" },
|
411
|
+
"extra_attributes" =>
|
412
|
+
{ "id" => 10_443, "self" => "https://url/to/source/ticket/123/remotelink/10443" } }
|
413
|
+
end
|
414
|
+
|
415
|
+
it("only checks the needed attributes") { expect(loader.source_ticket_link_needs_update?).to be_falsey }
|
416
|
+
end
|
417
|
+
end
|
418
|
+
end
|
419
|
+
# rubocop:enable Metrics/BlockLength
|
420
|
+
|
421
|
+
describe "#transition_ticket_to_the_expected_status" do
|
422
|
+
let(:loader) { described_class.send(:new, jira_project, row) }
|
423
|
+
let(:row) { { id: "123", status: "Testing" } }
|
424
|
+
|
425
|
+
let(:ticket) { double(Ticket) }
|
426
|
+
|
427
|
+
it do
|
428
|
+
expect(loader).to receive(:ticket).at_least(:once).and_return(ticket)
|
429
|
+
expect(ticket).to receive(:transition_to).with("Testing")
|
430
|
+
|
431
|
+
loader.transition_ticket_to_the_expected_status
|
432
|
+
end
|
289
433
|
end
|
290
434
|
end
|
291
435
|
end
|
436
|
+
# rubocop:enable Metrics/ClassLength
|
292
437
|
end
|
293
|
-
|
294
|
-
# rubocop:enable Metrics/ClassLength
|
295
438
|
end
|
@@ -33,11 +33,11 @@ RSpec.describe Ticket::Replicator::RowTransformer do
|
|
33
33
|
allow(transformer).to receive(:transformed_status).and_return("Open")
|
34
34
|
allow(transformer).to receive(:transformed_resolution).and_return("")
|
35
35
|
allow(transformer).to receive(:transformed_priority).and_return("Low")
|
36
|
-
allow(transformer).to receive(:transformed_team).and_return("Transformed Team")
|
37
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")
|
38
38
|
|
39
39
|
expect(transformer.run)
|
40
|
-
.to eq(["123", "Open", "", "Low", "transformed summary (123)"])
|
40
|
+
.to eq(["123", "Open", "", "Low", "transformed summary (123)", "url/to/source/ticket/123"])
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
@@ -84,6 +84,46 @@ RSpec.describe Ticket::Replicator::RowTransformer do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
+
describe "#ticket_replicator_source_ticket_url" do
|
88
|
+
let(:extracted_row) { {} }
|
89
|
+
|
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
|
95
|
+
|
96
|
+
it { expect(transformer.ticket_replicator_source_ticket_url).to eq("url/to/source/ticket") }
|
97
|
+
end
|
98
|
+
|
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
|
106
|
+
|
107
|
+
describe "#source_ticket_url_builder" do
|
108
|
+
let(:extracted_row) { {} }
|
109
|
+
|
110
|
+
before do
|
111
|
+
allow(transformer).to receive_messages(ticket_replicator_source_ticket_url: ticket_replicator_source_ticket_url)
|
112
|
+
end
|
113
|
+
|
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 %>" }
|
116
|
+
|
117
|
+
it { expect(transformer.source_ticket_url_builder("123")).to eq("http://url/to/source/ticket/123") }
|
118
|
+
end
|
119
|
+
|
120
|
+
context "when the URL has a syntax error" do
|
121
|
+
let(:ticket_replicator_source_ticket_url) { "http://url/to/source/ticket/<%= missing_variable %>" }
|
122
|
+
|
123
|
+
it { expect { transformer.source_ticket_url_builder("123") }.to raise_error(StandardError) }
|
124
|
+
end
|
125
|
+
end
|
126
|
+
|
87
127
|
describe "using mappings" do
|
88
128
|
before { allow(transformer).to receive_messages(mappings: mappings) }
|
89
129
|
|
@@ -126,6 +166,21 @@ RSpec.describe Ticket::Replicator::RowTransformer do
|
|
126
166
|
end
|
127
167
|
end
|
128
168
|
|
169
|
+
describe "#transformed_source_ticket_url" do
|
170
|
+
let(:remapped_field_extracted_row) { { "id" => "123" } }
|
171
|
+
let(:mappings) { {} }
|
172
|
+
|
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
|
178
|
+
|
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
|
183
|
+
|
129
184
|
describe "#transformed_summary" do
|
130
185
|
let(:remapped_field_extracted_row) { { "id" => "426", "summary" => "a summary" } }
|
131
186
|
let(:mappings) { {} }
|
@@ -136,7 +136,6 @@ module Ticket
|
|
136
136
|
let(:jira_auto_tool) { instance_double(Jira::Auto::Tool) }
|
137
137
|
let(:jira_client) { instance_double(JIRA::Client, Field: field_navigator) }
|
138
138
|
|
139
|
-
# let(:jira_ticket) { OpenStruct.new(jira_ticket_properties_hash["fields"]) }
|
140
139
|
let(:jira_ticket) { JIRA::Resource::Issue.build(jira_client, jira_ticket_properties_hash) }
|
141
140
|
|
142
141
|
let(:ticket) { described_class.new(jira_auto_tool, jira_ticket) }
|
@@ -149,6 +148,56 @@ module Ticket
|
|
149
148
|
it { expect(ticket.summary).to eq("SMAN-16384 | Login page randomly fails to load CSS assets (10001)") }
|
150
149
|
end
|
151
150
|
|
151
|
+
def build_ticket_link(url, title, application = Ticket::SOURCE_TICKET_REMOTE_LINK_APPLICATION)
|
152
|
+
double(JIRA::Resource::Remotelink, attrs: RowLoader.build_ticket_link_attributes(url, title, application))
|
153
|
+
end
|
154
|
+
|
155
|
+
describe "#source_ticket_link" do
|
156
|
+
let(:ticket_links) { [double(JIRA::Resource::Remotelink, attrs: {}), source_ticket_jira_link].compact }
|
157
|
+
|
158
|
+
before { allow(jira_ticket).to receive_messages(remotelink: double(all: ticket_links)) }
|
159
|
+
|
160
|
+
let(:source_ticket_jira_link) { build_ticket_link("https://ur/to/source/ticket/16384", "Source Ticket 16384") }
|
161
|
+
|
162
|
+
it { expect(ticket.source_ticket_link).to eq(source_ticket_jira_link) }
|
163
|
+
|
164
|
+
context "when no source ticket is linked" do
|
165
|
+
let(:source_ticket_jira_link) { nil }
|
166
|
+
it { expect(ticket.source_ticket_link).to be_nil }
|
167
|
+
end
|
168
|
+
|
169
|
+
context "when the existing remote link is not a ticket source link (no application specified)" do
|
170
|
+
let(:source_ticket_jira_link) do
|
171
|
+
build_ticket_link("https://ur/to/source/ticket/16384", "Source Ticket 16384", nil)
|
172
|
+
end
|
173
|
+
|
174
|
+
it { expect(ticket.source_ticket_link).to be_nil }
|
175
|
+
end
|
176
|
+
|
177
|
+
context "when the existing remote link is not a ticket source link (another application specified)" do
|
178
|
+
let(:source_ticket_jira_link) do
|
179
|
+
build_ticket_link("https://ur/to/source/ticket/16384", "Source Ticket 16384", "Another application")
|
180
|
+
end
|
181
|
+
|
182
|
+
it { expect(ticket.source_ticket_link).to be_nil }
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
describe "#source_ticket_url" do
|
187
|
+
before do
|
188
|
+
allow(ticket).to receive_messages(source_ticket_link: source_ticket_jira_link)
|
189
|
+
end
|
190
|
+
|
191
|
+
let(:source_ticket_jira_link) { build_ticket_link("https://ur/to/source/ticket/16384", "Source Ticket 16384") }
|
192
|
+
|
193
|
+
it { expect(ticket.source_ticket_url).to eq("https://ur/to/source/ticket/16384") }
|
194
|
+
|
195
|
+
context "when no source ticket is linked" do
|
196
|
+
let(:source_ticket_jira_link) { nil }
|
197
|
+
it { expect(ticket.source_ticket_url).to be_nil }
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
152
201
|
describe "replicated?" do
|
153
202
|
let(:ticket) { described_class.new(jira_auto_tool, jira_ticket) }
|
154
203
|
let(:jira_ticket) { double(JIRA::Resource::Issue, summary: actual_summary) }
|