github.com/beanworks/dcm@v0.0.0-20230726194615-49d2d0417e04/src/dcm_test.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"errors"
     6  	"io"
     7  	"io/ioutil"
     8  	"os"
     9  	"path"
    10  	"testing"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  // ========== Mocked CmdMock struct as test helper for Dcm ==========
    17  
    18  type CmdMock struct {
    19  	// CmdMock extends Cmd
    20  	Cmd
    21  
    22  	// Fields from CmdMock
    23  	name, dir string
    24  	args, env []string
    25  }
    26  
    27  func (c *CmdMock) Exec(name string, args ...string) Executable {
    28  	c.name = name
    29  	c.args = args
    30  	return c
    31  }
    32  
    33  func (c *CmdMock) Setdir(dir string) Executable {
    34  	c.dir = dir
    35  	return c
    36  }
    37  
    38  func (c *CmdMock) Setenv(env []string) Executable {
    39  	c.env = env
    40  	return c
    41  }
    42  
    43  func (c *CmdMock) Getenv() []string {
    44  	return c.env
    45  }
    46  
    47  func (c *CmdMock) Run() error {
    48  	switch c.name {
    49  	case "git":
    50  		if len(c.args) == 3 && c.args[0] == "clone" &&
    51  			c.args[1] == "test-dcm-setup-error" {
    52  			return errors.New("exit status 1")
    53  		}
    54  		if len(c.args) == 2 && c.args[0] == "checkout" &&
    55  			c.args[1] == "test-dcm-setup-error" {
    56  			return errors.New("exit status 1")
    57  		}
    58  		if len(c.args) == 3 && c.args[0] == "rev-parse" &&
    59  			c.dir == "/test/dcm/git/rev-parse/error" {
    60  			return errors.New("exit status 1")
    61  		}
    62  		if len(c.args) == 2 && c.args[0] == "checkout" {
    63  			switch c.args[1] {
    64  			case "master", "test-dcm-update-error":
    65  				return errors.New("exit status 1")
    66  			}
    67  		}
    68  		if len(c.args) == 1 && c.args[0] == "pull" &&
    69  			c.dir == "/test/dcm/git/pull/error" {
    70  			return errors.New("exit status 1")
    71  		}
    72  	case "docker-compose":
    73  		if c.dir == "/test/dcm/run/execute/error" {
    74  			return errors.New("exit status 1")
    75  		}
    76  	case "/bin/bash":
    77  		if len(c.args) == 1 &&
    78  			c.args[0] == "test/dcm/run/init/error" {
    79  			return errors.New("exit status 1")
    80  		}
    81  	case "docker":
    82  		if len(c.args) == 4 && c.args[0] == "exec" &&
    83  			c.args[2] == "dcmtest_failed_to_run_docker_exec_1" {
    84  			return errors.New("exit status 1")
    85  		}
    86  		if len(c.args) == 2 && c.args[0] == "rmi" &&
    87  			c.args[1] == "dcmtest_bad" {
    88  			return errors.New("exit status 1")
    89  		}
    90  		if len(c.args) == 2 && c.args[0] == "kill" &&
    91  			c.args[1] == "dcmtest_docker_kill_error_1" {
    92  			return errors.New("exit status 1")
    93  		}
    94  		if len(c.args) == 3 && c.args[0] == "rm" &&
    95  			c.args[2] == "dcmtest_docker_rm_error_1" {
    96  			return errors.New("exit status 1")
    97  		}
    98  	}
    99  	return nil
   100  }
   101  
   102  func (c *CmdMock) Out() ([]byte, error) {
   103  	switch c.name {
   104  	case "docker":
   105  		if len(c.args) == 3 && c.args[0] == "ps" {
   106  			switch c.args[2] {
   107  			case "name=dcmtest_empty_container_id_":
   108  				return []byte(""), nil
   109  			case "name=dcmtest_ok_":
   110  				return []byte("dcmtest_ok_1"), nil
   111  			case "name=dcmtest_failed_to_run_docker_exec_":
   112  				return []byte("dcmtest_failed_to_run_docker_exec_1"), nil
   113  			case "name=dcmtest_docker_kill_error_":
   114  				return []byte("dcmtest_docker_kill_error_1"), nil
   115  			case "name=dcmtest_docker_rm_error_":
   116  				return []byte("dcmtest_docker_rm_error_1"), nil
   117  			default:
   118  				return []byte("error"), errors.New("exit status 1")
   119  			}
   120  		}
   121  		if len(c.args) == 1 && c.args[0] == "images" {
   122  			switch c.dir {
   123  			case "/test/docker/images/error":
   124  				return []byte("error"), errors.New("exit status 1")
   125  			case "/test/docker/images/remove/error":
   126  				return []byte("dcmtest_bad foobar bazqux"), nil
   127  			default:
   128  				return []byte("dcmtest_ok foobar bazqux"), nil
   129  			}
   130  		}
   131  	}
   132  	return []byte(""), nil
   133  }
   134  
   135  // ========== Here starts the real tests for Dcm ==========
   136  
   137  func TestCommand(t *testing.T) {
   138  	var (
   139  		code int
   140  		err  error
   141  	)
   142  
   143  	dir, err := ioutil.TempDir("", "dcm")
   144  	require.Nil(t, err)
   145  	defer os.Remove(dir)
   146  
   147  	dcm := NewDcm(NewConfig(), []string{})
   148  	dcm.Config.Project = "dcmtest"
   149  	dcm.Config.Dir = dir
   150  	dcm.Cmd = &CmdMock{}
   151  	dcm.Cmd.Setdir("/test/dcm/git/rev-parse/ok")
   152  
   153  	tests := []struct {
   154  		name string
   155  		args []string
   156  		code int
   157  	}{
   158  		{
   159  			name: "No args passed, print usage, and return code 1",
   160  			args: []string{},
   161  			code: 1,
   162  		},
   163  		{
   164  			name: "test command `dcm help`",
   165  			args: []string{"help"},
   166  			code: 0,
   167  		},
   168  		{
   169  			name: "test command `dcm setup`",
   170  			args: []string{"setup"},
   171  			code: 0,
   172  		},
   173  		{
   174  			name: "test command `dcm run`",
   175  			args: []string{"run"},
   176  			code: 0,
   177  		},
   178  		{
   179  			name: "test command `dcm build`",
   180  			args: []string{"build"},
   181  			code: 0,
   182  		},
   183  		{
   184  			name: "test command `dcm dir`",
   185  			args: []string{"dir"},
   186  			code: 0,
   187  		},
   188  		{
   189  			name: "test command `dcm shell`",
   190  			args: []string{"shell", "ok"},
   191  			code: 0,
   192  		},
   193  		{
   194  			name: "test command `dcm branch`",
   195  			args: []string{"branch", "dcm"},
   196  			code: 0,
   197  		},
   198  		{
   199  			name: "test command `dcm update`",
   200  			args: []string{"update"},
   201  			code: 0,
   202  		},
   203  		{
   204  			name: "dcm command `dcm purge`",
   205  			args: []string{"purge"},
   206  			code: 0,
   207  		},
   208  		{
   209  			name: "dcm command `dcm list`",
   210  			args: []string{"list"},
   211  			code: 0,
   212  		},
   213  		{
   214  			name: "Invalid args passed, print usage, and return code 127",
   215  			args: []string{"invalid"},
   216  			code: 127,
   217  		},
   218  	}
   219  
   220  	for n, test := range tests {
   221  		dcm.Args = test.args
   222  		code, err = dcm.Command()
   223  		assert.Equal(t, code, test.code, "[%d: %s] Incorrect error code returned", n, test.name)
   224  		assert.Nil(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   225  	}
   226  }
   227  
   228  func TestSetup(t *testing.T) {
   229  	fixtures := []struct {
   230  		name   string
   231  		config yamlConfig
   232  		code   int
   233  		err    error
   234  	}{
   235  		{
   236  			name: "Negative case: failed to read git repository config",
   237  			config: yamlConfig{
   238  				"service": yamlConfig{"build": "./build/dir"},
   239  			},
   240  			code: 1,
   241  			err:  errors.New("Error reading git repository config for service [service]"),
   242  		},
   243  		{
   244  			name: "Negative case: failed to clone git repository",
   245  			config: yamlConfig{
   246  				"service": yamlConfig{
   247  					"labels": yamlConfig{"dcm.repository": "test-dcm-setup-error"},
   248  				},
   249  			},
   250  			code: 1,
   251  			err:  errors.New("Error cloning git repository for service [service]: exit status 1"),
   252  		},
   253  		{
   254  			name: "Negative case: failed to switch to pre-configured git branch",
   255  			config: yamlConfig{
   256  				"service": yamlConfig{
   257  					"labels": yamlConfig{
   258  						"dcm.repository": "test-dcm-setup-ok",
   259  						"dcm.branch":     "test-dcm-setup-error",
   260  					},
   261  				},
   262  			},
   263  			code: 1,
   264  			err:  errors.New("exit status 1"),
   265  		},
   266  		{
   267  			name: "Positive case: success with docker hub image",
   268  			config: yamlConfig{
   269  				"service": yamlConfig{
   270  					"image": "docker-hub-image",
   271  				},
   272  			},
   273  			code: 0,
   274  			err:  nil,
   275  		},
   276  		{
   277  			name: "Positive case: success with local build",
   278  			config: yamlConfig{
   279  				"service": yamlConfig{
   280  					"labels": yamlConfig{
   281  						"dcm.repository": "test-dcm-setup-ok",
   282  						"dcm.branch":     "test-dcm-setup-ok",
   283  					},
   284  				},
   285  			},
   286  			code: 0,
   287  			err:  nil,
   288  		},
   289  	}
   290  
   291  	td, err := ioutil.TempDir("", "dcm")
   292  	require.Nil(t, err)
   293  	defer os.Remove(td)
   294  
   295  	dcm := NewDcm(NewConfig(), []string{})
   296  	dcm.Cmd = &CmdMock{}
   297  	dcm.Config.Srv = td
   298  
   299  	for n, test := range fixtures {
   300  		dcm.Config.Config = test.config
   301  		code, err := dcm.Setup()
   302  		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
   303  		if test.err != nil {
   304  			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
   305  		} else {
   306  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   307  		}
   308  	}
   309  }
   310  
   311  func TestDoForEachService(t *testing.T) {
   312  	var (
   313  		doSrv doForService
   314  		code  int
   315  		err   error
   316  	)
   317  
   318  	dcm := NewDcm(NewConfig(), []string{})
   319  
   320  	// Bad fixture data
   321  	fixtureBad := yamlConfig{
   322  		"srv1": "config1",
   323  		"srv2": "config2",
   324  	}
   325  	// Good fixture data
   326  	fixtureGood := yamlConfig{
   327  		"srv1": yamlConfig{
   328  			"config": "value",
   329  		},
   330  		"srv2": yamlConfig{
   331  			"config": "value",
   332  		},
   333  	}
   334  
   335  	// Negative case: silently fail when encountering bad config
   336  	dcm.Config.Config = fixtureBad
   337  	doSrv = func(service string, configs yamlConfig) (int, error) {
   338  		return 0, nil
   339  	}
   340  	code, err = dcm.doForEachService(doSrv)
   341  	assert.Equal(t, 1, code)
   342  	assert.Error(t, err)
   343  
   344  	// Negative case: fail with error
   345  	dcm.Config.Config = fixtureGood
   346  	doSrv = func(service string, configs yamlConfig) (int, error) {
   347  		return 1, errors.New("Error")
   348  	}
   349  	code, err = dcm.doForEachService(doSrv)
   350  	assert.Equal(t, 1, code)
   351  	assert.Error(t, err)
   352  
   353  	// Positive case: success
   354  	dcm.Config.Config = fixtureGood
   355  	doSrv = func(service string, configs yamlConfig) (int, error) {
   356  		return 0, nil
   357  	}
   358  	code, err = dcm.doForEachService(doSrv)
   359  	assert.Equal(t, 0, code)
   360  	assert.NoError(t, err)
   361  }
   362  
   363  func TestRun(t *testing.T) {
   364  	var (
   365  		code int
   366  		err  error
   367  	)
   368  
   369  	dcm := NewDcm(NewConfig(), []string{})
   370  	dcm.Cmd = &CmdMock{}
   371  
   372  	tests := []struct {
   373  		name string
   374  		args []string
   375  		code int
   376  	}{
   377  		{
   378  			name: "No args passed, run `dcm run up` as default option",
   379  			args: []string{},
   380  			code: 0,
   381  		},
   382  		{
   383  			name: "dcm command `dcm run execute`",
   384  			args: []string{"execute"},
   385  			code: 0,
   386  		},
   387  		{
   388  			name: "dcm command `dcm run init`",
   389  			args: []string{"init"},
   390  			code: 0,
   391  		},
   392  		{
   393  			name: "dcm command `dcm run pre-init`",
   394  			args: []string{"pre-init"},
   395  			code: 0,
   396  		},
   397  		{
   398  			name: "dcm command `dcm run build`",
   399  			args: []string{"build"},
   400  			code: 0,
   401  		},
   402  		{
   403  			name: "dcm command `dcm run start`",
   404  			args: []string{"start"},
   405  			code: 0,
   406  		},
   407  		{
   408  			name: "dcm command `dcm run stop`",
   409  			args: []string{"stop"},
   410  			code: 0,
   411  		},
   412  		{
   413  			name: "dcm command `dcm run restart`",
   414  			args: []string{"restart"},
   415  			code: 0,
   416  		},
   417  		{
   418  			name: "dcm command `dcm run up`",
   419  			args: []string{"up"},
   420  			code: 0,
   421  		},
   422  	}
   423  
   424  	for n, test := range tests {
   425  		code, err = dcm.Run(test.args...)
   426  		assert.Equal(t, code, test.code, "[%d: %s] Incorrect error code returned", n, test.name)
   427  		assert.Nil(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   428  	}
   429  }
   430  
   431  func TestRunExecute(t *testing.T) {
   432  	fixtures := []struct {
   433  		name, dir string
   434  		code      int
   435  	}{
   436  		{
   437  			name: "Negative case: failed to run docker-compose command",
   438  			dir:  "/test/dcm/run/execute/error",
   439  			code: 1,
   440  		},
   441  		{
   442  			name: "Positive case: success",
   443  			dir:  "/test/dcm/run/execute/ok",
   444  			code: 0,
   445  		},
   446  	}
   447  
   448  	dcm := NewDcm(NewConfig(), []string{})
   449  	dcm.Cmd = &CmdMock{}
   450  
   451  	for n, test := range fixtures {
   452  		dcm.Config.Dir = test.dir
   453  		code, err := dcm.runExecute()
   454  		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
   455  		if test.code == 1 {
   456  			assert.Error(t, err, "[%d: %s] Incorrect error returned", n, test.name)
   457  		} else {
   458  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   459  		}
   460  	}
   461  }
   462  
   463  func TestRunInit(t *testing.T) {
   464  	fixtures := []struct {
   465  		name   string
   466  		config yamlConfig
   467  		code   int
   468  		err    error
   469  	}{
   470  		{
   471  			name: "Negative case: config has no init script",
   472  			config: yamlConfig{
   473  				"service": yamlConfig{
   474  					"labels": yamlConfig{
   475  						"dcm.test": "test",
   476  					},
   477  				},
   478  			},
   479  			code: 0,
   480  			err:  nil,
   481  		},
   482  		{
   483  			name: "Negative case: failed to execute init script",
   484  			config: yamlConfig{
   485  				"service": yamlConfig{
   486  					"labels": yamlConfig{
   487  						"dcm.initscript": "test/dcm/run/init/error",
   488  					},
   489  				},
   490  			},
   491  			code: 1,
   492  			err:  errors.New("Error executing init script [test/dcm/run/init/error] for service [service]: exit status 1"),
   493  		},
   494  		{
   495  			name: "Positive case: success",
   496  			config: yamlConfig{
   497  				"service": yamlConfig{
   498  					"labels": yamlConfig{
   499  						"dcm.initscript": "test/dcm/run/init/ok",
   500  					},
   501  				},
   502  			},
   503  			code: 0,
   504  			err:  nil,
   505  		},
   506  	}
   507  
   508  	dcm := NewDcm(NewConfig(), []string{})
   509  	dcm.Cmd = &CmdMock{}
   510  
   511  	for n, test := range fixtures {
   512  		dcm.Config.Config = test.config
   513  		code, err := dcm.runInit()
   514  		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
   515  		if test.err != nil {
   516  			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
   517  		} else {
   518  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   519  		}
   520  	}
   521  }
   522  
   523  func TestRunPreInit(t *testing.T) {
   524  	fixtures := []struct {
   525  		name   string
   526  		config yamlConfig
   527  		code   int
   528  		err    error
   529  	}{
   530  		{
   531  			name: "Negative case: config has no pre-init script",
   532  			config: yamlConfig{
   533  				"service": yamlConfig{
   534  					"labels": yamlConfig{
   535  						"dcm.test": "test",
   536  					},
   537  				},
   538  			},
   539  			code: 0,
   540  			err:  nil,
   541  		},
   542  		{
   543  			name: "Negative case: failed to execute pre-init script",
   544  			config: yamlConfig{
   545  				"service": yamlConfig{
   546  					"labels": yamlConfig{
   547  						"dcm.pre_initscript": "test/dcm/run/init/error",
   548  					},
   549  				},
   550  			},
   551  			code: 1,
   552  			err:  errors.New("Error executing pre-init script [test/dcm/run/init/error] for service [service]: exit status 1"),
   553  		},
   554  		{
   555  			name: "Positive case: success",
   556  			config: yamlConfig{
   557  				"service": yamlConfig{
   558  					"labels": yamlConfig{
   559  						"dcm.pre_initscript": "test/dcm/run/init/ok",
   560  					},
   561  				},
   562  			},
   563  			code: 0,
   564  			err:  nil,
   565  		},
   566  	}
   567  
   568  	dcm := NewDcm(NewConfig(), []string{})
   569  	dcm.Cmd = &CmdMock{}
   570  
   571  	for n, test := range fixtures {
   572  		dcm.Config.Config = test.config
   573  		code, err := dcm.runInit()
   574  		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
   575  		if test.err != nil {
   576  			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
   577  		} else {
   578  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   579  		}
   580  	}
   581  }
   582  
   583  func TestDir(t *testing.T) {
   584  	dir, err := ioutil.TempDir("", "dcm")
   585  	require.Nil(t, err)
   586  	srv, err := ioutil.TempDir(dir, "service")
   587  	require.Nil(t, err)
   588  	defer os.RemoveAll(dir)
   589  
   590  	dcm := NewDcm(NewConfig(), []string{})
   591  	var out string
   592  
   593  	// Test Dir() without args
   594  	out = helperTestOsStdout(t, func() {
   595  		dcm.Config.Dir = dir
   596  		dcm.Dir()
   597  	})
   598  	assert.Equal(t, dir, out)
   599  
   600  	// Test Dir() with args, not exists, fall back to dcm.Config.Dir
   601  	out = helperTestOsStdout(t, func() {
   602  		dcm.Config.Srv = dir
   603  		dcm.Dir("not_exists")
   604  	})
   605  	assert.Equal(t, dir, out)
   606  
   607  	// Test Dir() with args
   608  	out = helperTestOsStdout(t, func() {
   609  		dcm.Config.Srv = dir
   610  		dcm.Dir(path.Base(srv))
   611  	})
   612  	assert.Equal(t, srv, out)
   613  }
   614  
   615  func helperTestOsStdout(t *testing.T, fn func()) (out string) {
   616  	// Capture stdout
   617  	stdout := os.Stdout
   618  	r, w, err := os.Pipe()
   619  	require.Nil(t, err)
   620  	os.Stdout = w
   621  	outC := make(chan string)
   622  
   623  	go func() {
   624  		var buf bytes.Buffer
   625  		_, err := io.Copy(&buf, r)
   626  		r.Close()
   627  		require.Nil(t, err)
   628  		outC <- buf.String()
   629  	}()
   630  
   631  	fn()
   632  
   633  	w.Close()
   634  	os.Stdout = stdout
   635  	out = <-outC
   636  
   637  	return
   638  }
   639  
   640  func TestShell(t *testing.T) {
   641  	var (
   642  		code int
   643  		err  error
   644  	)
   645  
   646  	dcm := NewDcm(NewConfig(), []string{})
   647  	dcm.Cmd = &CmdMock{}
   648  	dcm.Config.Project = "dcmtest"
   649  
   650  	// Negative case: failed when there is no arg passed
   651  	code, err = dcm.Shell()
   652  	assert.Equal(t, 1, code)
   653  	assert.EqualError(t, err, "Error: no service name specified.")
   654  
   655  	// Negative case: failed to get docker container id
   656  	code, err = dcm.Shell("failed_to_get_container_id")
   657  	assert.Equal(t, 1, code)
   658  	assert.EqualError(t, err, "exit status 1: error")
   659  
   660  	// Negative case: failed to run docker exec command
   661  	code, err = dcm.Shell("failed_to_run_docker_exec")
   662  	assert.Equal(t, 1, code)
   663  	assert.EqualError(t, err, "exit status 1")
   664  
   665  	// Positive case: success
   666  	code, err = dcm.Shell("ok")
   667  	assert.Equal(t, 0, code)
   668  	assert.NoError(t, err)
   669  }
   670  
   671  func TestGetContainerId(t *testing.T) {
   672  	fixtures := []struct {
   673  		name, service, cid string
   674  		err                error
   675  	}{
   676  		{
   677  			name:    "Negative case: failed to get docker container id",
   678  			service: "docker_ps_error",
   679  			cid:     "",
   680  			err:     errors.New("exit status 1: error"),
   681  		},
   682  		{
   683  			name:    "Negative case: got an empty container id",
   684  			service: "empty_container_id",
   685  			cid:     "",
   686  			err:     nil,
   687  		},
   688  		{
   689  			name:    "Positive case: success",
   690  			service: "ok",
   691  			cid:     "dcmtest_ok_1",
   692  			err:     nil,
   693  		},
   694  	}
   695  
   696  	dcm := NewDcm(NewConfig(), []string{})
   697  	dcm.Cmd = &CmdMock{}
   698  	dcm.Config.Project = "dcmtest"
   699  
   700  	for n, test := range fixtures {
   701  		cid, err := dcm.getContainerId(test.service, "-qf")
   702  		assert.Equal(t, test.cid, cid, "[%d: %s] Incorrect docker container ID returned", n, test.name)
   703  		if test.err != nil {
   704  			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
   705  		} else {
   706  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   707  		}
   708  	}
   709  }
   710  
   711  func TestGetImageRepository(t *testing.T) {
   712  	fixtures := []struct {
   713  		name, dir, service, repo string
   714  		err                      error
   715  	}{
   716  		{
   717  			name:    "Negative case: failed to execute docker images",
   718  			dir:     "/test/docker/images/error",
   719  			service: "empty_image_repo",
   720  			repo:    "",
   721  			err:     errors.New("exit status 1: error"),
   722  		},
   723  		{
   724  			name:    "Negative case: service image repo name is not in docker images list",
   725  			dir:     "/test/docker/images/ok",
   726  			service: "empty_image_repo",
   727  			repo:    "",
   728  			err:     nil,
   729  		},
   730  		{
   731  			name:    "Positive case: success",
   732  			dir:     "/test/docker/images/ok",
   733  			service: "ok",
   734  			repo:    "dcmtest_ok",
   735  			err:     nil,
   736  		},
   737  	}
   738  
   739  	dcm := NewDcm(NewConfig(), []string{})
   740  	dcm.Cmd = &CmdMock{}
   741  	dcm.Config.Project = "dcmtest"
   742  
   743  	for n, test := range fixtures {
   744  		dcm.Cmd.Setdir(test.dir)
   745  		repo, err := dcm.getImageRepository(test.service)
   746  		assert.Equal(t, test.repo, repo, "[%d: %s] Incorrect docker image repository name returned", n, test.name)
   747  		if test.err != nil {
   748  			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
   749  		} else {
   750  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   751  		}
   752  	}
   753  }
   754  
   755  func TestBranchForOne(t *testing.T) {
   756  	var (
   757  		code int
   758  		err  error
   759  	)
   760  
   761  	dir, err := ioutil.TempDir("", "dcm")
   762  	require.Nil(t, err)
   763  	srv, err := ioutil.TempDir(dir, "service")
   764  	require.Nil(t, err)
   765  	defer os.RemoveAll(dir)
   766  
   767  	dcm := NewDcm(NewConfig(), []string{})
   768  	dcm.Cmd = &CmdMock{}
   769  
   770  	// Negative case: get dcm branch failed at os.Chdir()
   771  	dcm.Config.Dir = "/fake/dcm/dir"
   772  	code, err = dcm.branchForOne("dcm")
   773  	assert.Equal(t, 0, code)
   774  	assert.EqualError(t, err, "chdir /fake/dcm/dir: no such file or directory")
   775  
   776  	// Negative case: git failed to get dcm branch
   777  	dcm.Config.Dir = dir
   778  	dcm.Cmd.Setdir("/test/dcm/git/rev-parse/error")
   779  	code, err = dcm.branchForOne("dcm")
   780  	assert.Equal(t, 0, code)
   781  	assert.EqualError(t, err, "exit status 1")
   782  
   783  	// Negative case: service not exists
   784  	dcm.Config.Srv = "/fake/dcm/srv"
   785  	code, err = dcm.branchForOne("invalid")
   786  	assert.Equal(t, 0, code)
   787  	assert.EqualError(t, err, "Service not exists.")
   788  
   789  	// Negative case: get service branch failed at os.Chdir()
   790  	dcm.Config.Srv = "/fake/dcm/srv"
   791  	dcm.Config.Config = yamlConfig{"service": yamlConfig{}}
   792  	code, err = dcm.branchForOne("service")
   793  	assert.Equal(t, 0, code)
   794  	assert.EqualError(t, err, "chdir /fake/dcm/srv/service: no such file or directory")
   795  
   796  	// Negative case: git failed to get service branch
   797  	dcm.Config.Srv = dir
   798  	dcm.Config.Config = yamlConfig{path.Base(srv): yamlConfig{}}
   799  	dcm.Cmd.Setdir("/test/dcm/git/rev-parse/error")
   800  	code, err = dcm.branchForOne(path.Base(srv))
   801  	assert.Equal(t, 0, code)
   802  	assert.EqualError(t, err, "exit status 1")
   803  
   804  	// Positive case: success with a service using docker hub image
   805  	dcm.Config.Config = yamlConfig{"service": yamlConfig{"image": "docker-hub-image"}}
   806  	code, err = dcm.branchForOne("service")
   807  	assert.Equal(t, 0, code)
   808  	assert.NoError(t, err)
   809  
   810  	// Positive case: success with dcm branch
   811  	dcm.Config.Dir = dir
   812  	dcm.Cmd.Setdir("/test/dcm/git/rev-parse/ok")
   813  	code, err = dcm.branchForOne("dcm")
   814  	assert.Equal(t, 0, code)
   815  	assert.NoError(t, err)
   816  }
   817  
   818  func TestUpdateForOne(t *testing.T) {
   819  	dir, err := ioutil.TempDir("", "dcm")
   820  	require.Nil(t, err)
   821  	srv, err := ioutil.TempDir(dir, "service")
   822  	require.Nil(t, err)
   823  	defer os.RemoveAll(dir)
   824  
   825  	service := path.Base(srv)
   826  
   827  	dcm := NewDcm(NewConfig(), []string{})
   828  	dcm.Cmd = &CmdMock{}
   829  
   830  	fixtures := []struct {
   831  		name, srv, dir string
   832  		config         yamlConfig
   833  		service        string
   834  		code           int
   835  		err            error
   836  	}{
   837  		{
   838  			name:    "Negative case: service not exists",
   839  			dir:     "",
   840  			srv:     "",
   841  			config:  yamlConfig{},
   842  			service: "invalid",
   843  			code:    0,
   844  			err:     errors.New("Service not exists."),
   845  		},
   846  		{
   847  			name: "Negative case: service not updateable",
   848  			dir:  "",
   849  			srv:  "",
   850  			config: yamlConfig{
   851  				"service": yamlConfig{
   852  					"labels": yamlConfig{
   853  						"dcm.updateable": "false",
   854  					},
   855  				},
   856  			},
   857  			service: "service",
   858  			code:    0,
   859  			err:     errors.New("Service not updateable. Skipping the update."),
   860  		},
   861  		{
   862  			name: "Negative case: failed to os.Chdir()",
   863  			dir:  "/test/dcm/update",
   864  			srv:  "/test/dcm/dir/srv/testproj",
   865  			config: yamlConfig{
   866  				"invalid": yamlConfig{
   867  					"folder": "test",
   868  				},
   869  			},
   870  			service: "invalid",
   871  			code:    0,
   872  			err:     errors.New("chdir /test/dcm/dir/srv/testproj/invalid: no such file or directory"),
   873  		},
   874  		{
   875  			name: "Negative case: cannot read default branch config, use master instead, and got `git checkout` error",
   876  			dir:  "/test/dcm/update",
   877  			srv:  dir,
   878  			config: yamlConfig{
   879  				service: yamlConfig{
   880  					"labels": yamlConfig{
   881  						"dcm.some.other": "label",
   882  					},
   883  				},
   884  			},
   885  			service: service,
   886  			code:    0,
   887  			err:     errors.New("exit status 1"),
   888  		},
   889  		{
   890  			name: "Negative case: failed to execute `git checkout`",
   891  			dir:  "/test/dcm/update",
   892  			srv:  dir,
   893  			config: yamlConfig{
   894  				service: yamlConfig{
   895  					"labels": yamlConfig{
   896  						"dcm.branch": "test-dcm-update-error",
   897  					},
   898  				},
   899  			},
   900  			service: service,
   901  			code:    0,
   902  			err:     errors.New("exit status 1"),
   903  		},
   904  		{
   905  			name: "Negative case: failed to execute `git pull`",
   906  			dir:  "/test/dcm/git/pull/error",
   907  			srv:  dir,
   908  			config: yamlConfig{
   909  				service: yamlConfig{
   910  					"labels": yamlConfig{
   911  						"dcm.branch": "test-dcm-update-ok",
   912  					},
   913  				},
   914  			},
   915  			service: service,
   916  			code:    0,
   917  			err:     errors.New("exit status 1"),
   918  		},
   919  		{
   920  			name: "Positive case: success with docker hub image",
   921  			dir:  "",
   922  			srv:  "",
   923  			config: yamlConfig{
   924  				"service": yamlConfig{
   925  					"image": "docker-hub-image",
   926  				},
   927  			},
   928  			service: "service",
   929  			code:    0,
   930  			err:     nil,
   931  		},
   932  		{
   933  			name: "Positive case: success with local build",
   934  			dir:  "/test/dcm/update",
   935  			srv:  dir,
   936  			config: yamlConfig{
   937  				service: yamlConfig{
   938  					"labels": yamlConfig{
   939  						"dcm.branch": "test-dcm-update-ok",
   940  					},
   941  				},
   942  			},
   943  			service: service,
   944  			code:    0,
   945  			err:     nil,
   946  		},
   947  	}
   948  
   949  	for n, test := range fixtures {
   950  		dcm.Cmd.Setdir(test.dir)
   951  		dcm.Config.Srv = test.srv
   952  		dcm.Config.Config = test.config
   953  		code, err := dcm.updateForOne(test.service)
   954  		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
   955  		if test.err != nil {
   956  			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
   957  		} else {
   958  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
   959  		}
   960  	}
   961  }
   962  
   963  func TestPurge(t *testing.T) {
   964  	var (
   965  		code int
   966  		err  error
   967  	)
   968  
   969  	dcm := NewDcm(NewConfig(), []string{})
   970  	dcm.Cmd = &CmdMock{}
   971  
   972  	tests := []struct {
   973  		name string
   974  		args []string
   975  		code int
   976  	}{
   977  		{
   978  			name: "No args passed, run `dcm purge containers` as default option",
   979  			args: []string{},
   980  			code: 0,
   981  		},
   982  		{
   983  			name: "dcm command `dcm purge images`",
   984  			args: []string{"images"},
   985  			code: 0,
   986  		},
   987  		{
   988  			name: "dcm command `dcm run containers`",
   989  			args: []string{"containers"},
   990  			code: 0,
   991  		},
   992  		{
   993  			name: "dcm command `dcm run all`",
   994  			args: []string{"all"},
   995  			code: 0,
   996  		},
   997  	}
   998  
   999  	for n, test := range tests {
  1000  		code, err = dcm.Purge(test.args...)
  1001  		assert.Equal(t, code, test.code, "[%d: %s] Incorrect error code returned", n, test.name)
  1002  		assert.Nil(t, err, "[%d: %s] Non-nil error returned", n, test.name)
  1003  	}
  1004  }
  1005  
  1006  func TestPurgeImages(t *testing.T) {
  1007  	fixtures := []struct {
  1008  		name, dir string
  1009  		config    yamlConfig
  1010  		code      int
  1011  		err       error
  1012  	}{
  1013  		{
  1014  			name: "Negative case: failed to get image repo name",
  1015  			dir:  "/test/docker/images/error",
  1016  			config: yamlConfig{
  1017  				"service": yamlConfig{
  1018  					"test": "purgeImages",
  1019  				},
  1020  			},
  1021  			code: 0,
  1022  			err:  nil,
  1023  		},
  1024  		{
  1025  			name: "Negative case: failed to execute `docker rmi`",
  1026  			dir:  "/test/docker/images/remove/error",
  1027  			config: yamlConfig{
  1028  				"bad": yamlConfig{
  1029  					"test": "purgeImages",
  1030  				},
  1031  			},
  1032  			code: 0,
  1033  			err:  nil,
  1034  		},
  1035  		{
  1036  			name: "Positive case: success",
  1037  			dir:  "/test/docker/images/ok",
  1038  			config: yamlConfig{
  1039  				"ok": yamlConfig{
  1040  					"test": "purgeImages",
  1041  				},
  1042  			},
  1043  			code: 0,
  1044  			err:  nil,
  1045  		},
  1046  	}
  1047  
  1048  	dcm := NewDcm(NewConfig(), []string{})
  1049  	dcm.Cmd = &CmdMock{}
  1050  	dcm.Config.Project = "dcmtest"
  1051  
  1052  	for n, test := range fixtures {
  1053  		dcm.Cmd.Setdir(test.dir)
  1054  		dcm.Config.Config = test.config
  1055  		code, err := dcm.purgeImages()
  1056  		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
  1057  		if test.err != nil {
  1058  			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
  1059  		} else {
  1060  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
  1061  		}
  1062  	}
  1063  }
  1064  
  1065  func TestPurgeContainers(t *testing.T) {
  1066  	fixtures := []struct {
  1067  		name   string
  1068  		config yamlConfig
  1069  		code   int
  1070  		err    error
  1071  	}{
  1072  		{
  1073  			name: "Negative case: failed to get container id",
  1074  			config: yamlConfig{
  1075  				"docker_ps_error": yamlConfig{
  1076  					"test": "purgeContainers",
  1077  				},
  1078  			},
  1079  			code: 0,
  1080  			err:  nil,
  1081  		},
  1082  		{
  1083  			name: "Negative case: failed to execute `docker kill`",
  1084  			config: yamlConfig{
  1085  				"docker_kill_error": yamlConfig{
  1086  					"test": "purgeContainers",
  1087  				},
  1088  			},
  1089  			code: 0,
  1090  			err:  nil,
  1091  		},
  1092  		{
  1093  			name: "Negative case: failed to execute `docker rm`",
  1094  			config: yamlConfig{
  1095  				"docker_rm_error": yamlConfig{
  1096  					"test": "purgeContainers",
  1097  				},
  1098  			},
  1099  			code: 0,
  1100  			err:  nil,
  1101  		},
  1102  		{
  1103  			name: "Positive case: success",
  1104  			config: yamlConfig{
  1105  				"ok": yamlConfig{
  1106  					"test": "purgeContainers",
  1107  				},
  1108  			},
  1109  			code: 0,
  1110  			err:  nil,
  1111  		},
  1112  	}
  1113  
  1114  	dcm := NewDcm(NewConfig(), []string{})
  1115  	dcm.Cmd = &CmdMock{}
  1116  	dcm.Config.Project = "dcmtest"
  1117  
  1118  	for n, test := range fixtures {
  1119  		dcm.Config.Config = test.config
  1120  		code, err := dcm.purgeContainers()
  1121  		assert.Equal(t, test.code, code, "[%d: %s] Incorrect error code returned", n, test.name)
  1122  		if test.err != nil {
  1123  			assert.EqualError(t, err, test.err.Error(), "[%d: %s] Incorrect error returned", n, test.name)
  1124  		} else {
  1125  			assert.NoError(t, err, "[%d: %s] Non-nil error returned", n, test.name)
  1126  		}
  1127  	}
  1128  }
  1129  
  1130  func TestList(t *testing.T) {
  1131  	out := helperTestOsStdout(t, func() {
  1132  		dcm := NewDcm(NewConfig(), []string{})
  1133  		dcm.Config.Config = yamlConfig{
  1134  			"service": yamlConfig{},
  1135  		}
  1136  		dcm.List()
  1137  	})
  1138  	assert.Equal(t, "service\n", out)
  1139  }
  1140  
  1141  func TestUsage(t *testing.T) {
  1142  	out := helperTestOsStdout(t, func() {
  1143  		dcm := NewDcm(NewConfig(), []string{})
  1144  		dcm.Usage()
  1145  	})
  1146  
  1147  	assert.Contains(t, out, "DCM (Docker-Compose Manager)\n")
  1148  	assert.Contains(t, out, "Usage:\n")
  1149  	assert.Contains(t, out, "Example:\n")
  1150  }