github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/cmd/hkserver/commands/cmdtestlib.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package commands
     5  
     6  import (
     7  	"bytes"
     8  	"flag"
     9  	"fmt"
    10  	"io"
    11  	"io/ioutil"
    12  	"os"
    13  	"os/exec"
    14  	"path/filepath"
    15  	"strings"
    16  	"testing"
    17  
    18  	"github.com/stretchr/testify/require"
    19  
    20  	"github.com/masterhung0112/hk_server/v5/api4"
    21  	"github.com/masterhung0112/hk_server/v5/model"
    22  	"github.com/masterhung0112/hk_server/v5/store/storetest/mocks"
    23  	"github.com/masterhung0112/hk_server/v5/testlib"
    24  )
    25  
    26  var coverprofileCounters map[string]int = make(map[string]int)
    27  
    28  var mainHelper *testlib.MainHelper
    29  
    30  type testHelper struct {
    31  	*api4.TestHelper
    32  
    33  	config            *model.Config
    34  	tempDir           string
    35  	configFilePath    string
    36  	disableAutoConfig bool
    37  }
    38  
    39  // Setup creates an instance of testHelper.
    40  func Setup(t testing.TB) *testHelper {
    41  	dir, err := testlib.SetupTestResources()
    42  	if err != nil {
    43  		panic("failed to create temporary directory: " + err.Error())
    44  	}
    45  
    46  	api4TestHelper := api4.Setup(t)
    47  
    48  	testHelper := &testHelper{
    49  		TestHelper:     api4TestHelper,
    50  		tempDir:        dir,
    51  		configFilePath: filepath.Join(dir, "config-helper.json"),
    52  	}
    53  
    54  	config := &model.Config{}
    55  	config.SetDefaults()
    56  	testHelper.SetConfig(config)
    57  
    58  	return testHelper
    59  }
    60  
    61  // Setup creates an instance of testHelper.
    62  func SetupWithStoreMock(t testing.TB) *testHelper {
    63  	dir, err := testlib.SetupTestResources()
    64  	if err != nil {
    65  		panic("failed to create temporary directory: " + err.Error())
    66  	}
    67  
    68  	api4TestHelper := api4.SetupWithStoreMock(t)
    69  	systemStore := mocks.SystemStore{}
    70  	systemStore.On("Get").Return(make(model.StringMap), nil)
    71  	licenseStore := mocks.LicenseStore{}
    72  	licenseStore.On("Get", "").Return(&model.LicenseRecord{}, nil)
    73  	api4TestHelper.App.Srv().Store.(*mocks.Store).On("System").Return(&systemStore)
    74  	api4TestHelper.App.Srv().Store.(*mocks.Store).On("License").Return(&licenseStore)
    75  
    76  	testHelper := &testHelper{
    77  		TestHelper:     api4TestHelper,
    78  		tempDir:        dir,
    79  		configFilePath: filepath.Join(dir, "config-helper.json"),
    80  	}
    81  
    82  	config := &model.Config{}
    83  	config.SetDefaults()
    84  	testHelper.SetConfig(config)
    85  
    86  	return testHelper
    87  }
    88  
    89  // InitBasic simply proxies to api4.InitBasic, while still returning a testHelper.
    90  func (h *testHelper) InitBasic() *testHelper {
    91  	h.TestHelper.InitBasic()
    92  	return h
    93  }
    94  
    95  // TemporaryDirectory returns the temporary directory created for user by the test helper.
    96  func (h *testHelper) TemporaryDirectory() string {
    97  	return h.tempDir
    98  }
    99  
   100  // Config returns the configuration passed to a running command.
   101  func (h *testHelper) Config() *model.Config {
   102  	return h.config.Clone()
   103  }
   104  
   105  // ConfigPath returns the path to the temporary config file passed to a running command.
   106  func (h *testHelper) ConfigPath() string {
   107  	return h.configFilePath
   108  }
   109  
   110  // SetConfig replaces the configuration passed to a running command.
   111  func (h *testHelper) SetConfig(config *model.Config) {
   112  	if !testing.Short() {
   113  		config.SqlSettings = *mainHelper.GetSQLSettings()
   114  	}
   115  
   116  	// Disable strict password requirements for test
   117  	*config.PasswordSettings.MinimumLength = 5
   118  	*config.PasswordSettings.Lowercase = false
   119  	*config.PasswordSettings.Uppercase = false
   120  	*config.PasswordSettings.Symbol = false
   121  	*config.PasswordSettings.Number = false
   122  
   123  	h.config = config
   124  
   125  	if err := ioutil.WriteFile(h.configFilePath, []byte(config.ToJson()), 0600); err != nil {
   126  		panic("failed to write file " + h.configFilePath + ": " + err.Error())
   127  	}
   128  }
   129  
   130  // SetAutoConfig configures whether the --config flag is automatically passed to a running command.
   131  func (h *testHelper) SetAutoConfig(autoConfig bool) {
   132  	h.disableAutoConfig = !autoConfig
   133  }
   134  
   135  // TearDown cleans up temporary files and assets created during the life of the test helper.
   136  func (h *testHelper) TearDown() {
   137  	h.TestHelper.TearDown()
   138  	os.RemoveAll(h.tempDir)
   139  }
   140  
   141  func (h *testHelper) execArgs(t *testing.T, args []string) []string {
   142  	ret := []string{"-test.v", "-test.run", "ExecCommand"}
   143  	if coverprofile := flag.Lookup("test.coverprofile").Value.String(); coverprofile != "" {
   144  		dir := filepath.Dir(coverprofile)
   145  		base := filepath.Base(coverprofile)
   146  		baseParts := strings.SplitN(base, ".", 2)
   147  		name := strings.Replace(t.Name(), "/", "_", -1)
   148  		coverprofileCounters[name] = coverprofileCounters[name] + 1
   149  		baseParts[0] = fmt.Sprintf("%v-%v-%v", baseParts[0], name, coverprofileCounters[name])
   150  		ret = append(ret, "-test.coverprofile", filepath.Join(dir, strings.Join(baseParts, ".")))
   151  	}
   152  
   153  	ret = append(ret, "--", "--disableconfigwatch")
   154  
   155  	// Unless the test passes a `--config` of its own, create a temporary one from the default
   156  	// configuration with the current test database applied.
   157  	hasConfig := h.disableAutoConfig
   158  	for _, arg := range args {
   159  		if arg == "--config" {
   160  			hasConfig = true
   161  			break
   162  		}
   163  	}
   164  
   165  	if !hasConfig {
   166  		ret = append(ret, "--config", h.configFilePath)
   167  	}
   168  
   169  	ret = append(ret, args...)
   170  
   171  	return ret
   172  }
   173  
   174  func (h *testHelper) cmd(t *testing.T, args []string) *exec.Cmd {
   175  	path, err := os.Executable()
   176  	require.NoError(t, err)
   177  	cmd := exec.Command(path, h.execArgs(t, args)...)
   178  
   179  	cmd.Env = []string{}
   180  	for _, env := range os.Environ() {
   181  		// Ignore MM_SQLSETTINGS_DATASOURCE from the environment, since we override.
   182  		if strings.HasPrefix(env, "MM_SQLSETTINGS_DATASOURCE=") {
   183  			continue
   184  		}
   185  
   186  		cmd.Env = append(cmd.Env, env)
   187  	}
   188  
   189  	return cmd
   190  }
   191  
   192  // CheckCommand invokes the test binary, returning the output modified for assertion testing.
   193  func (h *testHelper) CheckCommand(t *testing.T, args ...string) string {
   194  	output, err := h.cmd(t, args).CombinedOutput()
   195  	require.NoError(t, err, string(output))
   196  	return strings.TrimSpace(strings.TrimSuffix(strings.TrimSpace(string(output)), "PASS"))
   197  }
   198  
   199  // RunCommand invokes the test binary, returning only any error.
   200  func (h *testHelper) RunCommand(t *testing.T, args ...string) error {
   201  	return h.cmd(t, args).Run()
   202  }
   203  
   204  // RunCommandWithOutput is a variant of RunCommand that returns the unmodified output and any error.
   205  func (h *testHelper) RunCommandWithOutput(t *testing.T, args ...string) (string, error) {
   206  	cmd := h.cmd(t, args)
   207  
   208  	var buf bytes.Buffer
   209  	reader, writer := io.Pipe()
   210  	cmd.Stdout = writer
   211  	cmd.Stderr = writer
   212  
   213  	done := make(chan bool)
   214  	go func() {
   215  		io.Copy(&buf, reader)
   216  		close(done)
   217  	}()
   218  
   219  	err := cmd.Run()
   220  	writer.Close()
   221  	<-done
   222  
   223  	return buf.String(), err
   224  }