github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/test/groovy/com/sap/piper/cm/ChangeManagementTest.groovy (about)

     1  package com.sap.piper.cm
     2  import static org.hamcrest.Matchers.allOf
     3  import static org.hamcrest.Matchers.contains
     4  import static org.hamcrest.Matchers.containsString
     5  import static org.hamcrest.Matchers.equalTo
     6  import static org.hamcrest.Matchers.hasItem
     7  import static org.hamcrest.Matchers.is
     8  import static org.hamcrest.Matchers.not
     9  import static org.junit.Assert.assertThat
    10  
    11  import static org.junit.Assert.assertEquals
    12  
    13  import org.hamcrest.Matchers
    14  import org.junit.Assert
    15  import org.junit.Rule
    16  import org.junit.Test
    17  import org.junit.rules.ExpectedException
    18  import org.junit.rules.RuleChain
    19  
    20  import com.sap.piper.GitUtils
    21  
    22  import util.BasePiperTest
    23  import util.JenkinsLoggingRule
    24  import util.JenkinsScriptLoaderRule
    25  import util.JenkinsShellCallRule
    26  import util.JenkinsCredentialsRule
    27  import util.JenkinsDockerExecuteRule
    28  import util.JenkinsFileExistsRule
    29  import util.Rules
    30  
    31  import hudson.AbortException
    32  
    33  public class ChangeManagementTest extends BasePiperTest {
    34  
    35      private ExpectedException thrown = ExpectedException.none()
    36  
    37      private JenkinsShellCallRule script = new JenkinsShellCallRule(this)
    38      private JenkinsLoggingRule logging = new JenkinsLoggingRule(this)
    39      private JenkinsDockerExecuteRule dockerExecuteRule = new JenkinsDockerExecuteRule(this)
    40      private JenkinsFileExistsRule files = new JenkinsFileExistsRule(this)
    41  
    42      @Rule
    43      public RuleChain rules = Rules.getCommonRules(this)
    44          .around(thrown)
    45          .around(script)
    46          .around(logging)
    47          .around(new JenkinsCredentialsRule(this).withCredentials('me','user','password'))
    48          .around(dockerExecuteRule)
    49          .around(files)
    50  
    51      @Test
    52      public void testRetrieveChangeDocumentIdOutsideGitWorkTreeTest() {
    53  
    54          thrown.expect(ChangeManagementException)
    55          thrown.expectMessage('Cannot retrieve ChangeDocumentId. ' +
    56                               'Not in a git work tree. ' +
    57                               'ChangeDocumentId is extracted from git commit messages.')
    58  
    59          new ChangeManagement(nullScript, gitUtilsMock(false, new String[0])).getChangeDocumentId()
    60      }
    61  
    62      @Test
    63      public void testRetrieveChangeDocumentIdNothingFound() {
    64  
    65          thrown.expect(ChangeManagementException)
    66          thrown.expectMessage('Cannot retrieve ChangeDocumentId from git commits.')
    67  
    68          new ChangeManagement(nullScript, gitUtilsMock(true, new String[0])).getChangeDocumentId()
    69      }
    70  
    71      @Test
    72      public void testRetrieveChangeDocumentIdReturnsArrayWithNullValue() {
    73  
    74          thrown.expect(ChangeManagementException)
    75          thrown.expectMessage('Cannot retrieve ChangeDocumentId from git commits.')
    76  
    77          new ChangeManagement(nullScript, gitUtilsMock(true, (String[])[ null ])).getChangeDocumentId()
    78      }
    79  
    80      @Test
    81      public void testRetrieveChangeDocumentNotUnique() {
    82  
    83          thrown.expect(ChangeManagementException)
    84          thrown.expectMessage('Multiple ChangeDocumentIds found')
    85  
    86          String[] changeIds = [ 'a', 'b' ]
    87          new ChangeManagement(nullScript, gitUtilsMock(true, changeIds)).getChangeDocumentId()
    88      }
    89  
    90      @Test
    91      public void testRetrieveChangeDocumentSameChangeIdFoundTwice() {
    92  
    93          String[] changeIds = [ 'a', 'a' ]
    94          def changeID = new ChangeManagement(nullScript, gitUtilsMock(true, changeIds)).getChangeDocumentId()
    95  
    96          assert changeID == 'a'
    97      }
    98  
    99      @Test
   100      public void testIsChangeInDevelopmentReturnsTrueWhenChangeIsInDevelopent() {
   101  
   102          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, "cmclient.*is-change-in-development -cID '001'", 0)
   103          boolean inDevelopment = new ChangeManagement(nullScript, null).isChangeInDevelopment(
   104              [
   105                  image: 'ppiper/cm-client',
   106                  pullImage: true,
   107              ],
   108              '001', 'endpoint', 'me')
   109  
   110          assertThat(inDevelopment, is(equalTo(true)))
   111          assertThat(script.shell[0], allOf(containsString("cmclient"),
   112                                              containsString("-u 'user'"),
   113                                              containsString("-p 'password'"),
   114                                              containsString("-e 'endpoint'"),
   115                                              containsString('is-change-in-development'),
   116                                              containsString("-cID '001'"),
   117                                              containsString("-t SOLMAN")))
   118  
   119          assert dockerExecuteRule.getDockerParams().dockerImage == 'ppiper/cm-client'
   120          assert dockerExecuteRule.getDockerParams().dockerPullImage == true
   121      }
   122  
   123      @Test
   124      public void testIsChangeInDevelopmentReturnsFalseWhenChangeIsNotInDevelopent() {
   125  
   126          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, "cmclient.*is-change-in-development -cID '001'", 3)
   127  
   128          boolean inDevelopment = new ChangeManagement(nullScript, null)
   129                                      .isChangeInDevelopment([:],
   130                                                             '001',
   131                                                             'endpoint',
   132                                                             'me')
   133  
   134          assertThat(inDevelopment, is(equalTo(false)))
   135      }
   136  
   137      @Test
   138      public void testIsChangeInDevelopmentThrowsExceptionWhenCMClientReturnsUnexpectedExitCode() {
   139  
   140          thrown.expect(ChangeManagementException)
   141          thrown.expectMessage('Cannot retrieve status for change document \'001\'. Does this change exist? Return code from cmclient: 1.')
   142  
   143          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, "cmclient.*is-change-in-development -cID '001'", 1)
   144          new ChangeManagement(nullScript, null).isChangeInDevelopment([:], '001', 'endpoint', 'me')
   145      }
   146  
   147      @Test
   148      public void testGetCommandLineWithoutCMClientOpts() {
   149          String commandLine = new ChangeManagement(nullScript, null)
   150              .getCMCommandLine(BackendType.SOLMAN,
   151                                'https://example.org/cm',
   152                                "me",
   153                                "topSecret",
   154                                "the-command",
   155                                ["-key1", "val1", "-key2", "val2"])
   156          commandLine = commandLine.replaceAll(' +', " ")
   157          assertThat(commandLine, not(containsString("CMCLIENT_OPTS")))
   158          assertThat(commandLine, containsString("cmclient -e 'https://example.org/cm' -u 'me' -p 'topSecret' -t SOLMAN the-command -key1 val1 -key2 val2"))
   159      }
   160  
   161  @Test
   162  public void testGetCommandLineWithCMClientOpts() {
   163      String commandLine = new ChangeManagement(nullScript, null)
   164          .getCMCommandLine(BackendType.SOLMAN,
   165                            'https://example.org/cm',
   166                            "me",
   167                            "topSecret",
   168                            "the-command",
   169                            ["-key1", "val1", "-key2", "val2"],
   170                            '-Djavax.net.debug=all')
   171      commandLine = commandLine.replaceAll(' +', " ")
   172      assertThat(commandLine, containsString('export CMCLIENT_OPTS="-Djavax.net.debug=all"'))
   173  }
   174  
   175      @Test
   176      public void testCreateTransportRequestSOLMANSucceeds() {
   177  
   178          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, ".*cmclient.*create-transport -cID 001 -dID 002.*", '004')
   179          def transportRequestId = new ChangeManagement(nullScript).createTransportRequestSOLMAN(
   180              [
   181                  image: 'ppiper/cm-client',
   182                  pullImage: true,
   183              ],
   184              '001', '002', '003', 'me')
   185  
   186          // the check for the transportRequestID is sufficient. This checks implicit the command line since that value is
   187          // returned only in case the shell call matches.
   188          assert transportRequestId == '004'
   189  
   190          assert dockerExecuteRule.getDockerParams().dockerImage == 'ppiper/cm-client'
   191          assert dockerExecuteRule.getDockerParams().dockerPullImage == true
   192  
   193      }
   194  
   195      @Test
   196      public void testCreateTransportRequestRFCSucceeds() {
   197  
   198          script.setReturnValue('cts createTransportRequest', '{"REQUESTID":"XYZK9000004"}')
   199  
   200          def transportRequestId = new ChangeManagement(nullScript).createTransportRequestRFC(
   201              [image: 'rfc', options: []],
   202              'https://example.org/rfc', // endpoint
   203              '01', // instance
   204              '001', // client
   205              'me', // credentialsId
   206              'Lorem ipsum', // description
   207              true // verbose
   208          )
   209  
   210          assert dockerExecuteRule.dockerParams.dockerImage == 'rfc'
   211  
   212          assert dockerExecuteRule.dockerParams.dockerEnvVars == [
   213              TRANSPORT_DESCRIPTION: 'Lorem ipsum',
   214              ABAP_DEVELOPMENT_INSTANCE: '01',
   215              ABAP_DEVELOPMENT_CLIENT: '001',
   216              ABAP_DEVELOPMENT_SERVER: 'https://example.org/rfc',
   217              ABAP_DEVELOPMENT_USER: 'user',
   218              ABAP_DEVELOPMENT_PASSWORD: 'password',
   219              VERBOSE: true
   220          ]
   221  
   222          assert transportRequestId == 'XYZK9000004'
   223  
   224      }
   225  
   226      @Test
   227      public void testCreateTransportRequestRFCFails() {
   228  
   229          thrown.expect(ChangeManagementException)
   230          thrown.expectMessage('Cannot create transport request: script returned exit code 3')
   231  
   232          script.setReturnValue('cts createTransportRequest',
   233              { throw new AbortException('script returned exit code 3')})
   234  
   235          def transportRequestId = new ChangeManagement(nullScript).createTransportRequestRFC(
   236              [image: 'rfc', options: []],
   237              'https://example.org/rfc', // endpoint
   238              '001', // client
   239              '01', // instance
   240              'me', // credentialsId
   241              'Lorem ipsum', // description
   242              true, //verbose
   243          )
   244      }
   245  
   246      @Test
   247      public void testCreateTransportRequestCTSSucceeds() {
   248  
   249          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'cmclient.* -t CTS .*create-transport -tt W -ts XYZ -d "desc 123"$', '004')
   250          def transportRequestId = new ChangeManagement(nullScript)
   251              .createTransportRequestCTS(
   252                  [
   253                      image: 'ppiper/cmclient',
   254                      pullImage: true
   255                  ],
   256                  'W', // transport type
   257                  'XYZ', // target system
   258                  'desc 123', // description
   259                  'https://example.org/cm',
   260                  'me')
   261  
   262          // the check for the transportRequestID is sufficient. This checks implicit the command line since that value is
   263          // returned only in case the shell call matches.
   264          assert transportRequestId == '004'
   265  
   266          dockerExecuteRule.getDockerParams().dockerImage = 'ppiper/cmclient'
   267          dockerExecuteRule.getDockerParams().dockerPullImage = true
   268  
   269      }
   270  
   271      @Test
   272      public void testUploadFileToTransportSucceedsSOLMAN() {
   273  
   274          // the regex provided below is an implicit check that the command line is fine.
   275          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'upload-file-to-transport.*-cID 001 -tID 002 XXX "/path"', 0)
   276  
   277          new ChangeManagement(nullScript).uploadFileToTransportRequestSOLMAN(
   278              [
   279                  image: 'ppiper/cm-client',
   280                  pullImage: true,
   281                  volumeBind: ['/home/me/certs' : '/certs'],
   282              ],
   283              '001',
   284              '002',
   285              'XXX',
   286              '/path',
   287              'https://example.org/cm',
   288              'me')
   289  
   290          // no assert required here for the shell script, since the regex registered above
   291          // to the script rule is an implicit check for the command line.
   292  
   293          assert dockerExecuteRule.getDockerParams().dockerImage == 'ppiper/cm-client'
   294          assert dockerExecuteRule.getDockerParams().dockerPullImage
   295          assert dockerExecuteRule.getDockerParams().dockerVolumeBind == ['/home/me/certs' : '/certs']
   296      }
   297  
   298      @Test
   299      public void testUploadFileToTransportSucceedsCTSDeployConfigYamlExists() {
   300  
   301          files.existingFiles.add('ui5-deploy.yaml')
   302  
   303          new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
   304              [
   305                  image: 'node',
   306                  pullImage: true
   307               ],
   308              '002',
   309              'https://example.org/cm',
   310              '001',
   311              'myApp',
   312              'the description',
   313              'aPackage',
   314              'node2',
   315              ['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
   316              ['--verbose'],
   317              'ui5-deploy.yaml',
   318              'me',
   319          )
   320  
   321          assert script.shell[0].contains('npm install --global --verbose @ui5/cli @sap/ux-ui5-tooling @ui5/logger @ui5/fs @dummy/foo')
   322  
   323          assert script.shell[0].contains("fiori deploy -c \"ui5-deploy.yaml\" -t 002 -u https://example.org/cm")
   324  
   325          assert dockerExecuteRule.getDockerParams().dockerImage == 'node'
   326          assert dockerExecuteRule.getDockerParams().dockerPullImage == true
   327          assert dockerExecuteRule.getDockerParams().dockerEnvVars == [ABAP_USER: "user", ABAP_PASSWORD: 'password']
   328          // we launch the container as root (uid 0) in order to be able to install
   329          // the deploytool. Before deploying we su to another user.
   330          assert dockerExecuteRule.getDockerParams().dockerOptions == ['-u', '0']
   331      }
   332  
   333      @Test
   334      public void testUploadFileToTransportSucceedsCTSDefaultDeployConfigYamlDoesNotExist() {
   335  
   336          // the file does not exist, since it was not explicity added to the files rule
   337  
   338          new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
   339              [
   340                  image: 'node',
   341                  pullImage: true
   342               ],
   343              '002',
   344              'https://example.org/cm',
   345              '001',
   346              'myApp',
   347              'the description',
   348              'aPackage',
   349              'node2',
   350              ['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
   351              ['--verbose'],
   352              'ui5-deploy.yaml',
   353              'me',
   354          )
   355  
   356          // more details already checked with test "testUploadFileToTransportSucceedsCTSDeployConfigYamlExists"
   357          assert script.shell[0].contains("fiori deploy --noConfig -t 002 -u https://example.org/cm")
   358      }
   359  
   360      @Test
   361      public void testUploadFileToTransportFailsCTSExplicitlyConfiguredDeployConfigYamlDoesNotExist() {
   362  
   363          // the file does not exist, since it was not explicitly added to the files rule
   364  
   365          thrown.expect(AbortException)
   366          thrown.expectMessage('Configured deploy config file \'my-deploy.yaml\' does not exists.')
   367  
   368          new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
   369              [
   370                  image: 'node',
   371                  pullImage: true
   372               ],
   373              '002',
   374              'https://example.org/cm',
   375              '001',
   376              'myApp',
   377              'the description',
   378              'aPackage',
   379              'node2',
   380              ['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
   381              ['--verbose'],
   382              'my-deploy.yaml',
   383              'me',
   384          )
   385  
   386          // more details already checked with test "testUploadFileToTransportSucceedsCTSDeployConfigYamlExists"
   387          assert script.shell[0].contains("fiori deploy --noConfig -t 002 -u https://example.org/cm")
   388      }
   389  
   390      @Test
   391      public void testUploadFileToTransportSucceesCTSExplicitlyConfiguredDeployConfigYamExists() {
   392  
   393          files.existingFiles.add('my-deploy.yaml')
   394  
   395          new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
   396              [
   397                  image: 'node',
   398                  pullImage: true
   399               ],
   400              '002',
   401              'https://example.org/cm',
   402              '001',
   403              'myApp',
   404              'the description',
   405              'aPackage',
   406              'node2',
   407              ['@ui5/cli', '@sap/ux-ui5-tooling', '@ui5/logger', '@ui5/fs', '@dummy/foo'],
   408              ['--verbose'],
   409              'my-deploy.yaml',
   410              'me',
   411          )
   412  
   413          // more details already checked with test "testUploadFileToTransportSucceedsCTSDeployConfigYamlExists"
   414          assert script.shell[0].contains("fiori deploy -c \"my-deploy.yaml\" -t 002 -u https://example.org/cm")
   415      }
   416  
   417      @Test
   418      public void testUploadFileToTransportSucceedsEmptyDeployToolDependenciesCTS() {
   419  
   420          new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
   421              [
   422                  image: 'fioriDeployImage',
   423                  pullImage: true
   424               ],
   425              '002',
   426              'https://example.org/cm',
   427              '001',
   428              'myApp',
   429              'aPackage',
   430              'the description',
   431              'node2',
   432              [],
   433              [],
   434              'ui5-deploy.yaml',
   435              'me',
   436          )
   437  
   438          assert ! script.shell[0].contains('npm install')
   439          assert ! script.shell[0].contains('su')
   440  
   441          assert script.shell[0].contains("fiori deploy")
   442  
   443          assert dockerExecuteRule.getDockerParams().dockerImage == 'fioriDeployImage'
   444          assert dockerExecuteRule.getDockerParams().dockerPullImage == true
   445          assert dockerExecuteRule.getDockerParams().dockerEnvVars == [ABAP_USER: "user", ABAP_PASSWORD: 'password']
   446          // we don't start with the root user since there is no need to install something (globally)
   447          assert dockerExecuteRule.getDockerParams().dockerOptions == []
   448      }
   449  
   450      @Test
   451      public void testUploadFileToTransportShellFailsCTS() {
   452  
   453          thrown.expect(AbortException)
   454          thrown.expectMessage('script returned exit code 1')
   455  
   456          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '.*fiori deploy.*',
   457              { throw new AbortException('script returned exit code 1') })
   458  
   459          new ChangeManagement(nullScript).uploadFileToTransportRequestCTS(
   460              [
   461                  image: 'node',
   462                  pullImage: true
   463              ],
   464              '002',
   465              'https://example.org/cm',
   466              '001',
   467              'myApp',
   468              'aPackage',
   469              'the description',
   470              'node',
   471              '@ui5/cli @sap/ux-ui5-tooling @ui5/logger @ui5/fs',
   472              [],
   473              'ui5-deploy.yaml',
   474              'me',
   475          )
   476      }
   477  
   478      @Test
   479      public void testUploadFileToTransportSucceedsRFC() {
   480  
   481          new ChangeManagement(nullScript).uploadFileToTransportRequestRFC(
   482              [image:'rfc', options: [], pullImage: true],
   483              '002', //transportRequestId
   484              '001', // applicationId
   485              'https://example.org/mypath/deployArtifact.zip',
   486              'https://example.org/rfc',
   487              'me',
   488              '00', //developmentInstance
   489              '001', // developmentClient
   490              'Lorem ipsum', // applicationDescription
   491              'XYZ', // abapPackage
   492              'UTF-9', //codePage
   493              true, // accept unix style EOL
   494              true, // failUploadOnWarning
   495              false, // verbose
   496              )
   497  
   498  
   499              assert dockerExecuteRule.dockerParams.dockerImage == 'rfc'
   500              assert dockerExecuteRule.dockerParams.dockerPullImage == true
   501  
   502              assert dockerExecuteRule.dockerParams.dockerEnvVars ==
   503              [
   504                  ABAP_DEVELOPMENT_INSTANCE: '00',
   505                  ABAP_DEVELOPMENT_CLIENT: '001',
   506                  ABAP_APPLICATION_NAME: '001',
   507                  ABAP_APPLICATION_DESC: 'Lorem ipsum',
   508                  ABAP_PACKAGE: 'XYZ',
   509                  ZIP_FILE_URL: 'https://example.org/mypath/deployArtifact.zip',
   510                  ABAP_DEVELOPMENT_SERVER: 'https://example.org/rfc',
   511                  ABAP_DEVELOPMENT_USER: 'user',
   512                  ABAP_DEVELOPMENT_PASSWORD: 'password',
   513                  CODE_PAGE: 'UTF-9',
   514                  ABAP_ACCEPT_UNIX_STYLE_EOL: 'X',
   515                  FAIL_UPLOAD_ON_WARNING: 'true',
   516                  VERBOSE: 'false'
   517              ]
   518  
   519              assertThat(script.shell, contains('cts uploadToABAP:002'))
   520      }
   521  
   522      @Test
   523      public void testUploadFileToTransportFailsRFC() {
   524  
   525          thrown.expect(ChangeManagementException)
   526          thrown.expectMessage('Cannot upload file into transport request. Return code from rfc client: 1.')
   527  
   528          script.setReturnValue('cts uploadToABAP:002', 1)
   529  
   530          new ChangeManagement(nullScript).uploadFileToTransportRequestRFC(
   531              [:],
   532              '002', //transportRequestId
   533              '001', // applicationId
   534              'https://example.org/mypath/deployArtifact.zip',
   535              'https://example.org/rfc',
   536              'me',
   537              '00', //developmentInstance
   538              '001', // developmentClient
   539              'Lorem ipsum', // applicationDescription
   540              'XYZ', // abapPackage
   541              'UTF-9', // codePage
   542              true, // accept unix style EOL
   543              true, // failUploadOnWarning
   544              false, // verbose
   545              )
   546      }
   547  
   548      @Test
   549      public void testUploadFileToTransportFailsSOLMAN() {
   550  
   551          thrown.expect(ChangeManagementException)
   552          thrown.expectMessage("Cannot upload file into transport request. " +
   553              "Return code from cm client: 1.")
   554  
   555          script.setReturnValue(JenkinsShellCallRule.Type.REGEX,, 'upload-file-to-transport', 1)
   556  
   557          new ChangeManagement(nullScript).uploadFileToTransportRequestSOLMAN(
   558              [:],
   559              '001',
   560              '002',
   561              'XXX',
   562              '/path',
   563              'https://example.org/cm',
   564              'me')
   565      }
   566  
   567      @Test
   568      public void testReleaseTransportRequestSucceedsSOLMAN() {
   569  
   570          // the regex provided below is an implicit check that the command line is fine.
   571          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t SOLMAN release-transport.*-cID 001.*-tID 002', 0)
   572  
   573          new ChangeManagement(nullScript).releaseTransportRequestSOLMAN(
   574              [
   575                  image: 'ppiper/cm-client',
   576                  imagePull: true,
   577              ],
   578              '001',
   579              '002',
   580              'https://example.org',
   581              'me',
   582              'openSesame')
   583  
   584          // no assert required here, since the regex registered above to the script rule is an implicit check for
   585          // the command line.
   586  
   587          dockerExecuteRule.getDockerParams().dockerImage == 'ppiper/cm-client'
   588          dockerExecuteRule.getDockerParams().pullImage == true
   589      }
   590  
   591      @Test
   592      public void testReleaseTransportRequestSucceedsCTS() {
   593  
   594          // the regex provided below is an implicit check that the command line is fine.
   595          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, '-t CTS export-transport.*-tID 002', 0)
   596  
   597          new ChangeManagement(nullScript).releaseTransportRequestCTS(
   598              [
   599                  image: 'ppiper/cm-client',
   600                  pullImage: true,
   601              ],
   602              '002',
   603              'https://example.org',
   604              'me',
   605              'openSesame')
   606  
   607          // no assert required here, since the regex registered above to the script rule is an implicit check for
   608          // the command line.
   609  
   610          assert dockerExecuteRule.getDockerParams().dockerImage == 'ppiper/cm-client'
   611          assert dockerExecuteRule.getDockerParams().dockerPullImage == true
   612      }
   613  
   614      @Test
   615      public void testReleaseTransportRequestSucceedsRFC() {
   616  
   617          new ChangeManagement(nullScript).releaseTransportRequestRFC(
   618              [:],
   619              '002',
   620              'https://example.org',
   621              '002',
   622              '001',
   623              'me',
   624              true)
   625  
   626          assert dockerExecuteRule.dockerParams.dockerEnvVars == [
   627              ABAP_DEVELOPMENT_SERVER: 'https://example.org',
   628              ABAP_DEVELOPMENT_USER: 'user',
   629              ABAP_DEVELOPMENT_PASSWORD: 'password',
   630              ABAP_DEVELOPMENT_CLIENT: '001',
   631              ABAP_DEVELOPMENT_INSTANCE: '002',
   632              VERBOSE: true,
   633          ]
   634  
   635          assertThat(script.shell, hasItem('cts releaseTransport:002'))
   636      }
   637  
   638      @Test
   639      public void testReleaseTransportRequestFailsSOLMAN() {
   640  
   641          thrown.expect(ChangeManagementException)
   642          thrown.expectMessage("Cannot release Transport Request '002'. Return code from cmclient: 1.")
   643  
   644          // the regex provided below is an implicit check that the command line is fine.
   645          script.setReturnValue(JenkinsShellCallRule.Type.REGEX, 'release-transport.*-cID 001.*-tID 002', 1)
   646  
   647          new ChangeManagement(nullScript).releaseTransportRequestSOLMAN(
   648              [
   649                  image: 'ppiper/cm-client',
   650                  imagePull: true,
   651              ],
   652              '001',
   653              '002',
   654              'https://example.org',
   655              'me')
   656      }
   657  
   658      private GitUtils gitUtilsMock(boolean insideWorkTree, String[] changeIds) {
   659          return new GitUtils() {
   660              public boolean insideWorkTree() {
   661                  return insideWorkTree
   662              }
   663  
   664              public String[] extractLogLines(
   665                           String filter = '',
   666                           String from = 'origin/master',
   667                           String to = 'HEAD',
   668                           String format = '%b') {
   669                  return changeIds
   670              }
   671          }
   672      }
   673  }