github.com/jaylevin/jenkins-library@v1.230.4/cmd/xsDeploy_test.go (about) 1 package cmd 2 3 import ( 4 "bytes" 5 "errors" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "sync" 10 "testing" 11 12 "github.com/SAP/jenkins-library/pkg/mock" 13 "github.com/stretchr/testify/assert" 14 ) 15 16 type FileUtilsMock struct { 17 *mock.FilesMock 18 copiedFiles []string 19 } 20 21 func (f *FileUtilsMock) FileExists(path string) (bool, error) { 22 return path == "dummy.mtar" || path == ".xs_session", nil 23 } 24 25 func (f *FileUtilsMock) Copy(src, dest string) (int64, error) { 26 f.copiedFiles = append(f.copiedFiles, fmt.Sprintf("%s->%s", src, dest)) 27 return 0, nil 28 } 29 30 func TestDeploy(t *testing.T) { 31 myXsDeployOptions := xsDeployOptions{ 32 APIURL: "https://example.org:12345", 33 Username: "me", 34 Password: "secretPassword", 35 Org: "myOrg", 36 Space: "mySpace", 37 LoginOpts: "--skip-ssl-validation", 38 DeployOpts: "--dummy-deploy-opts", 39 XsSessionFile: ".xs_session", 40 Mode: "DEPLOY", 41 Action: "NONE", 42 MtaPath: "dummy.mtar", 43 OperationIDLogPattern: `^.*xs bg-deploy -i (.*) -a.*$`, 44 } 45 46 s := mock.ShellMockRunner{} 47 48 var removedFiles []string 49 50 cpeOut := xsDeployCommonPipelineEnvironment{} 51 fileUtilsMock := FileUtilsMock{} 52 53 fRemove := func(path string) error { 54 removedFiles = append(removedFiles, path) 55 return nil 56 } 57 58 var stdout string 59 60 t.Run("Standard deploy succeeds", func(t *testing.T) { 61 62 defer func() { 63 fileUtilsMock.copiedFiles = nil 64 removedFiles = nil 65 s.Calls = nil 66 stdout = "" 67 }() 68 69 rStdout, wStdout := io.Pipe() 70 71 var wg sync.WaitGroup 72 wg.Add(1) 73 74 go func() { 75 buf := new(bytes.Buffer) 76 io.Copy(buf, rStdout) 77 stdout = buf.String() 78 wg.Done() 79 }() 80 81 e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, wStdout) 82 83 wStdout.Close() 84 wg.Wait() 85 86 assert.NoError(t, e) 87 88 t.Run("Standard checks", func(t *testing.T) { 89 // Contains --> we do not check for the shebang 90 assert.Contains(t, s.Calls[0], "xs login -a https://example.org:12345 -u me -p 'secretPassword' -o myOrg -s mySpace --skip-ssl-validation") 91 assert.Contains(t, s.Calls[1], "xs deploy dummy.mtar --dummy-deploy-opts") 92 assert.Contains(t, s.Calls[2], "xs logout") 93 assert.Len(t, s.Calls, 3) 94 95 // xs session file needs to be removed at end during a normal deployment 96 assert.Len(t, removedFiles, 1) 97 assert.Contains(t, removedFiles, ".xs_session") 98 99 assert.Len(t, fileUtilsMock.copiedFiles, 2) 100 // We copy the xs session file to the workspace in order to be able to use the file later. 101 // This happens directly after login 102 // We copy the xs session file from the workspace to the home folder in order to be able to 103 // use that file. This is important in case we rely on a login which happened e 104 assert.Contains(t, fileUtilsMock.copiedFiles[0], "/.xs_session->.xs_session") 105 assert.Contains(t, fileUtilsMock.copiedFiles[1], ".xs_session->") 106 assert.Contains(t, fileUtilsMock.copiedFiles[1], "/.xs_session") 107 }) 108 109 t.Run("Password not exposed", func(t *testing.T) { 110 assert.NotEmpty(t, stdout) 111 assert.NotContains(t, stdout, myXsDeployOptions.Password) 112 }) 113 }) 114 115 t.Run("Standard deploy fails, deployable missing", func(t *testing.T) { 116 117 defer func() { 118 fileUtilsMock.copiedFiles = nil 119 removedFiles = nil 120 s.Calls = nil 121 }() 122 123 oldMtaPath := myXsDeployOptions.MtaPath 124 125 defer func() { 126 myXsDeployOptions.MtaPath = oldMtaPath 127 }() 128 129 // this file is not denoted in the file exists mock 130 myXsDeployOptions.MtaPath = "doesNotExist" 131 132 e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard) 133 assert.EqualError(t, e, "Deployable 'doesNotExist' does not exist") 134 }) 135 136 t.Run("Standard deploy fails, action provided", func(t *testing.T) { 137 138 defer func() { 139 fileUtilsMock.copiedFiles = nil 140 removedFiles = nil 141 s.Calls = nil 142 }() 143 144 myXsDeployOptions.Action = "RETRY" 145 defer func() { 146 myXsDeployOptions.Action = "NONE" 147 }() 148 149 e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard) 150 assert.EqualError(t, e, "Cannot perform action 'RETRY' in mode 'DEPLOY'. Only action 'NONE' is allowed.") 151 }) 152 153 t.Run("Standard deploy fails, error from underlying process", func(t *testing.T) { 154 155 defer func() { 156 fileUtilsMock.copiedFiles = nil 157 removedFiles = nil 158 s.Calls = nil 159 s.ShouldFailOnCommand = nil 160 }() 161 162 s.ShouldFailOnCommand = map[string]error{"#!/bin/bash\nxs login -a https://example.org:12345 -u me -p 'secretPassword' -o myOrg -s mySpace --skip-ssl-validation\n": errors.New("Error from underlying process")} 163 164 e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard) 165 assert.EqualError(t, e, "Error from underlying process") 166 }) 167 168 t.Run("BG deploy succeeds", func(t *testing.T) { 169 170 defer func() { 171 fileUtilsMock.copiedFiles = nil 172 removedFiles = nil 173 s.Calls = nil 174 s.StdoutReturn = make(map[string]string) 175 }() 176 177 s.StdoutReturn = make(map[string]string) 178 s.StdoutReturn[".*xs bg-deploy.*"] = "Use \"xs bg-deploy -i 1234 -a resume\" to resume the process.\n" 179 180 oldMode := myXsDeployOptions.Mode 181 182 defer func() { 183 myXsDeployOptions.Mode = oldMode 184 }() 185 186 myXsDeployOptions.Mode = "BG_DEPLOY" 187 188 e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard) 189 190 if assert.NoError(t, e) { 191 if assert.Len(t, s.Calls, 2) { // There are two entries --> no logout in this case. 192 assert.Contains(t, s.Calls[0], "xs login") 193 assert.Contains(t, s.Calls[1], "xs bg-deploy dummy.mtar --dummy-deploy-opts") 194 } 195 } 196 }) 197 198 t.Run("BG deploy fails, missing operationID", func(t *testing.T) { 199 200 s.StdoutReturn = make(map[string]string) 201 s.StdoutReturn[".*bg_deploy.*"] = "There is no operationID ...\n" 202 defer func() { 203 fileUtilsMock.copiedFiles = nil 204 removedFiles = nil 205 s.Calls = nil 206 s.StdoutReturn = make(map[string]string) 207 }() 208 209 oldMode := myXsDeployOptions.Mode 210 211 defer func() { 212 myXsDeployOptions.Mode = oldMode 213 }() 214 215 myXsDeployOptions.Mode = "BG_DEPLOY" 216 217 e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard) 218 assert.EqualError(t, e, "No operationID found") 219 }) 220 221 t.Run("BG deploy abort succeeds", func(t *testing.T) { 222 223 defer func() { 224 fileUtilsMock.copiedFiles = nil 225 removedFiles = nil 226 s.Calls = nil 227 }() 228 229 oldMode := myXsDeployOptions.Mode 230 oldAction := myXsDeployOptions.Action 231 232 defer func() { 233 myXsDeployOptions.Mode = oldMode 234 myXsDeployOptions.Action = oldAction 235 myXsDeployOptions.OperationID = "" 236 }() 237 238 myXsDeployOptions.Mode = "BG_DEPLOY" 239 myXsDeployOptions.Action = "ABORT" 240 myXsDeployOptions.OperationID = "12345" 241 242 e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard) 243 244 if assert.NoError(t, e) { 245 if assert.Len(t, s.Calls, 2) { // There is no login --> we have two calls 246 assert.Contains(t, s.Calls[0], "xs bg-deploy -i 12345 -a abort") 247 assert.Contains(t, s.Calls[1], "xs logout") 248 } 249 250 } 251 }) 252 253 t.Run("BG deploy abort fails due to missing operationId", func(t *testing.T) { 254 255 defer func() { 256 fileUtilsMock.copiedFiles = nil 257 removedFiles = nil 258 s.Calls = nil 259 }() 260 261 oldMode := myXsDeployOptions.Mode 262 oldAction := myXsDeployOptions.Action 263 264 defer func() { 265 myXsDeployOptions.Mode = oldMode 266 myXsDeployOptions.Action = oldAction 267 }() 268 269 myXsDeployOptions.Mode = "BG_DEPLOY" 270 myXsDeployOptions.Action = "ABORT" 271 272 e := runXsDeploy(myXsDeployOptions, &cpeOut, &s, &fileUtilsMock, fRemove, ioutil.Discard) 273 assert.EqualError(t, e, "OperationID was not provided. This is required for action 'ABORT'.") 274 }) 275 } 276 277 func TestRetrieveOperationID(t *testing.T) { 278 operationID := retrieveOperationID(` 279 Uploading 1 files: 280 myFolder/dummy.mtar 281 File upload finished 282 283 Detected MTA schema version: "3.1.0" 284 Detected deploy target as "myOrg mySpace" 285 Detected deployed MTA with ID "my_mta" and version "0.0.1" 286 Deployed MTA color: blue 287 New MTA color: green 288 Detected new MTA version: "0.0.1" 289 Deployed MTA version: 0.0.1 290 Service "xxx" is not modified and will not be updated 291 Creating application "db-green" from MTA module "xx"... 292 Uploading application "xx-green"... 293 Staging application "xx-green"... 294 Application "xx-green" staged 295 Executing task "deploy" on application "xx-green"... 296 Task execution status: succeeded 297 Process has entered validation phase. After testing your new deployment you can resume or abort the process. 298 Use "xs bg-deploy -i 1234 -a resume" to resume the process. 299 Use "xs bg-deploy -i 1234 -a abort" to abort the process. 300 Hint: Use the '--no-confirm' option of the bg-deploy command to skip this phase. 301 `, `^.*xs bg-deploy -i (.*) -a.*$`) 302 303 assert.Equal(t, "1234", operationID) 304 }