github.com/section/sectionctl@v1.12.3/commands/deploy_test.go (about) 1 package commands 2 3 import ( 4 "archive/tar" 5 "compress/gzip" 6 "fmt" 7 "io" 8 "io/ioutil" 9 "net/http" 10 "net/http/httptest" 11 "net/url" 12 "os" 13 "path/filepath" 14 "strconv" 15 "testing" 16 17 "github.com/alecthomas/kong" 18 "github.com/section/sectionctl/api" 19 "github.com/stretchr/testify/assert" 20 ) 21 22 23 type MockGitService struct { 24 Called bool 25 } 26 27 func (g *MockGitService) UpdateGitViaGit(ctx *kong.Context, c *DeployCmd,response UploadResponse,logWriters *LogWriters) error { 28 g.Called = true 29 return nil 30 } 31 32 func helperLoadBytes(t *testing.T, name string) []byte { 33 path := filepath.Join("testdata", name) // relative path 34 bytes, err := ioutil.ReadFile(path) 35 if err != nil { 36 t.Fatal(err) 37 } 38 return bytes 39 } 40 41 func TestCommandsDeployBuildFilelistIgnoresFiles(t *testing.T) { 42 assert := assert.New(t) 43 44 // Setup 45 dir := filepath.Join("testdata", "deploy", "tree") 46 ignores := []string{".git", "foo"} 47 48 // Invoke 49 paths, err := BuildFilelist(dir, ignores) 50 51 // Test 52 assert.NoError(err) 53 assert.Greater(len(paths), 0) 54 for _, p := range paths { 55 for _, i := range ignores { 56 assert.NotContains(p, i) 57 } 58 } 59 } 60 61 func TestCommandsDeployBuildFilelistErrorsOnNonExistentDirectory(t *testing.T) { 62 assert := assert.New(t) 63 64 // Setup 65 testCases := []string{ 66 filepath.Join("testdata", "deploy", "non-existent-tree"), 67 filepath.Join("testdata", "deploy", "file"), 68 } 69 var ignores []string 70 71 for _, tc := range testCases { 72 t.Run(tc, func(t *testing.T) { 73 // Invoke 74 paths, err := BuildFilelist(tc, ignores) 75 76 // Test 77 assert.Error(err) 78 assert.Zero(len(paths)) 79 }) 80 } 81 } 82 83 func TestCommandsDeployCreateTarballAlwaysPutsAppAtRoot(t *testing.T) { 84 assert := assert.New(t) 85 86 // Setup 87 var testCases = []struct { 88 cwd string // change into this directory 89 target string // bundle up files from this directory 90 }{ 91 {".", filepath.Join("testdata", "deploy", "valid-nodejs-app")}, 92 {filepath.Join("testdata", "deploy", "valid-nodejs-app"), "."}, 93 } 94 var ignores []string 95 96 for _, tc := range testCases { 97 t.Run(tc.target, func(t *testing.T) { 98 tempFile, err := ioutil.TempFile("", "sectionctl-deploy") 99 assert.NoError(err) 100 101 // Record where we were at the beginning of the test 102 owd, err := os.Getwd() 103 assert.NoError(err) 104 105 err = os.Chdir(tc.cwd) 106 assert.NoError(err) 107 108 // Build the file list 109 paths, err := BuildFilelist(tc.target, ignores) 110 assert.NoError(err) 111 112 // Create the tarball 113 err = CreateTarball(tempFile, paths) 114 assert.NoError(err) 115 116 _, err = tempFile.Seek(0, 0) 117 assert.NoError(err) 118 119 // Extract the tarball 120 tempDir, err := ioutil.TempDir("", "sectionctl-deploy") 121 assert.NoError(err) 122 err = untar(tempFile, tempDir) 123 124 // Test 125 assert.NoError(err) 126 path := filepath.Join(tempDir, "package.json") 127 _, err = os.Stat(path) 128 assert.NoError(err) 129 130 // Change back to where we were at the beginning of the test 131 err = os.Chdir(owd) 132 assert.NoError(err) 133 }) 134 } 135 } 136 137 func untar(src io.Reader, dst string) (err error) { 138 // ungzip 139 zr, err := gzip.NewReader(src) 140 if err != nil { 141 return err 142 } 143 // untar 144 tr := tar.NewReader(zr) 145 146 // uncompress each element 147 for { 148 header, err := tr.Next() 149 if err == io.EOF { 150 break // End of archive 151 } 152 if err != nil { 153 return err 154 } 155 156 // add dst + re-format slashes according to system 157 target := filepath.Join(dst, header.Name) 158 // if no join is needed, replace with ToSlash: 159 // target = filepath.ToSlash(header.Name) 160 161 // check the type 162 switch header.Typeflag { 163 164 // if its a dir and it doesn't exist create it (with 0755 permission) 165 case tar.TypeDir: 166 if _, err := os.Stat(target); err != nil { 167 if err := os.MkdirAll(target, 0755); err != nil { 168 return err 169 } 170 } 171 // if it's a file create it (with same permission) 172 case tar.TypeReg: 173 fileToWrite, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, os.FileMode(header.Mode)) 174 if err != nil { 175 return err 176 } 177 // copy over contents 178 if _, err := io.Copy(fileToWrite, tr); err != nil { 179 return err 180 } 181 // manually close here after each file operation; defering would cause each file close 182 // to wait until all operations have completed. 183 fileToWrite.Close() 184 } 185 } 186 return nil 187 } 188 189 func TestCommandsDeployValidatesPresenceOfNodeApp(t *testing.T) { 190 assert := assert.New(t) 191 192 // Setup 193 var testCases = []struct { 194 path string 195 valid bool 196 }{ 197 {filepath.Join("testdata", "deploy", "tree"), false}, 198 {filepath.Join("testdata", "deploy", "valid-nodejs-app"), true}, 199 } 200 201 for _, tc := range testCases { 202 t.Run(tc.path, func(t *testing.T) { 203 // Invoke 204 errs := IsValidNodeApp(tc.path) 205 206 // Test 207 assert.Equal(len(errs) == 0, tc.valid) 208 t.Logf("err: %v", errs) 209 }) 210 } 211 } 212 213 func TestCommandsDeployUploadsTarball(t *testing.T) { 214 assert := assert.New(t) 215 216 // Setup 217 type req struct { 218 called bool 219 token string 220 accountID int 221 file []byte 222 } 223 var uploadReq req 224 ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 225 to := r.Header.Get("section-token") 226 assert.NotEmpty(to) 227 228 switch r.URL.Path { 229 case "/": 230 uploadReq.called = true 231 uploadReq.token = to 232 233 r.ParseMultipartForm(MaxFileSize) 234 235 file, _, err := r.FormFile("file") 236 assert.NoError(err) 237 b, err := ioutil.ReadAll(file) 238 assert.NoError(err) 239 uploadReq.file = b 240 241 aid, err := strconv.Atoi(r.FormValue("account_id")) 242 assert.NoError(err) 243 uploadReq.accountID = aid 244 245 w.WriteHeader(http.StatusOK) 246 fmt.Fprint(w, string(helperLoadBytes(t, "deploy/upload.response.with_success.json"))) 247 default: 248 assert.FailNowf("unhandled URL", "URL: %s", r.URL.Path) 249 } 250 251 })) 252 253 url, err := url.Parse(ts.URL) 254 assert.NoError(err) 255 256 dir := filepath.Join("testdata", "deploy", "valid-nodejs-app") 257 api.PrefixURI = url 258 api.Token = "s3cr3t" 259 260 mockGit := MockGitService{} 261 globalGitService = &mockGit 262 // Invoke 263 c := DeployCmd{ 264 Directory: dir, 265 ServerURL: url, 266 AccountID: 100, 267 AppID: 200, 268 Environment: "dev", 269 AppPath: "nodejs", 270 } 271 kongContext := kong.Context{} 272 logWriters := LogWriters{ConsoleWriter: io.Discard,FileWriter: io.Discard,ConsoleOnly: io.Discard,CarriageReturnWriter: io.Discard} 273 err = c.Run(&kongContext, &logWriters) 274 275 // Test 276 assert.NoError(err) 277 278 // upload request 279 assert.True(uploadReq.called) 280 assert.Equal(api.Token, uploadReq.token) 281 assert.NotZero(len(uploadReq.file)) 282 assert.Equal([]byte{0x1f, 0x8b}, uploadReq.file[0:2]) // gzip header 283 assert.Equal(c.AccountID, uploadReq.accountID) 284 285 assert.True(mockGit.Called) 286 }