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  }