github.com/wfusion/gofusion@v1.1.14/test/suite.go (about)

     1  package test
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"io"
     7  	"os"
     8  	"path/filepath"
     9  	"runtime"
    10  	"runtime/debug"
    11  	"strings"
    12  
    13  	"github.com/stretchr/testify/suite"
    14  
    15  	"github.com/wfusion/gofusion/common/constant"
    16  	"github.com/wfusion/gofusion/common/env"
    17  	"github.com/wfusion/gofusion/common/utils"
    18  	"github.com/wfusion/gofusion/config"
    19  	"github.com/wfusion/gofusion/log"
    20  )
    21  
    22  type Suite struct {
    23  	suite.Suite
    24  }
    25  
    26  func (t *Suite) SetupSuite() {
    27  	t.Catch(func() {
    28  		log.Info(context.Background(), "============ setup test suite ============")
    29  	})
    30  }
    31  
    32  func (t *Suite) TearDownSuite() {
    33  	t.Catch(func() {
    34  		log.Info(context.Background(), "============ tear down test suite ============")
    35  	})
    36  }
    37  
    38  func (t *Suite) Catch(f func()) {
    39  	defer func() {
    40  		if r := recover(); r != nil {
    41  			t.FailNow(fmt.Sprintf("panic: %s =>\n%s", r, debug.Stack()))
    42  		}
    43  	}()
    44  
    45  	f()
    46  }
    47  
    48  func (t *Suite) RawCopy(filenames []string, stackSkip int) (cleanFn func()) {
    49  	stackSkip++
    50  	t.clearAllFiles(filenames, stackSkip)
    51  	for _, filename := range filenames {
    52  		t.copyFile(filename, filename, stackSkip)
    53  	}
    54  	return func() {
    55  		t.clearAllFiles(filenames, stackSkip)
    56  	}
    57  }
    58  
    59  func (t *Suite) Copy(src []string, testName string, stackSkip int) (cleanFn func()) {
    60  	_, filename, _, ok := runtime.Caller(stackSkip)
    61  	t.True(ok)
    62  	component := t.componentName(filename)
    63  	fileMapping, others := t.mappingFilenames(t.appName(component, testName), src)
    64  	allFilenames := append(others, utils.MapValues(fileMapping)...)
    65  
    66  	stackSkip++
    67  	// t.clearAllFiles(allFilenames, stackSkip)
    68  	for src, dst := range fileMapping {
    69  		t.copyFile(dst, src, stackSkip)
    70  	}
    71  	for _, filename := range others {
    72  		t.copyFile(filename, filename, stackSkip)
    73  	}
    74  	return func() {
    75  		t.clearAllFiles(allFilenames, stackSkip)
    76  	}
    77  }
    78  
    79  func (t *Suite) Init(src []string, testName string, stackSkip int) (cleanFn func()) {
    80  	_, filename, _, ok := runtime.Caller(stackSkip)
    81  	t.True(ok)
    82  	componentName := t.componentName(filename)
    83  	appName := t.appName(componentName, testName)
    84  	fileMapping, _ := t.mappingFilenames(appName, src)
    85  	cfgNames := utils.MapValues(fileMapping)
    86  	for i := 0; i < len(cfgNames); i++ {
    87  		cfgNames[i] = env.WorkDir + "/configs/" + cfgNames[i]
    88  	}
    89  
    90  	appCfg := &struct{}{}
    91  	gracefullyExitFn := config.New(appName).Init(&appCfg, config.Files(cfgNames))
    92  	return func() {
    93  		gracefullyExitFn()
    94  	}
    95  }
    96  
    97  func (t *Suite) ConfigFiles() []string {
    98  	return []string{
    99  		"app.local.yml",
   100  		"app.yml",
   101  	}
   102  }
   103  
   104  func (t *Suite) AllConfigFiles() []string {
   105  	return []string{
   106  		"app.local.yml",
   107  		"app.yml",
   108  		"app.json",
   109  		"app.toml",
   110  	}
   111  }
   112  
   113  func (t *Suite) appName(componentName, testName string) string {
   114  	return fmt.Sprintf("%s.%s", componentName, testName)
   115  }
   116  
   117  func (t *Suite) isConfigFile(name string) (ok bool) {
   118  	return strings.Contains(name, "app")
   119  }
   120  
   121  func (t *Suite) mappingFilenames(appName string, filenames []string) (cfgMapping map[string]string, others []string) {
   122  	others = make([]string, 0, len(filenames))
   123  	cfgMapping = make(map[string]string, len(filenames))
   124  	for _, filename := range filenames {
   125  		if t.isConfigFile(filename) {
   126  			cfgMapping[filename] = appName + "." + filename
   127  		} else {
   128  			others = append(others, filename)
   129  		}
   130  	}
   131  	return
   132  }
   133  
   134  func (t *Suite) componentName(filename string) (name string) {
   135  	fpath := filepath.Clean("github.com/wfusion/gofusion/test")
   136  	moduleDir := filepath.Dir(filename)
   137  	component := moduleDir[strings.Index(moduleDir, fpath):]
   138  	return component[len(fpath)+1:]
   139  }
   140  
   141  func (t *Suite) clearAllFiles(filenames []string, stackSkip int) {
   142  	// locate project conf path & current conf path
   143  	_, filename, _, ok := runtime.Caller(stackSkip)
   144  	t.Require().True(ok)
   145  	projectRoot := filepath.Dir(filename)
   146  
   147  	projectConfDir := filepath.Join(strings.TrimSuffix(projectRoot, constant.PathSeparator+"cases"), "configs")
   148  	currentConfDir := filepath.Join(env.WorkDir, "configs")
   149  	if utils.IsStrBlank(currentConfDir) || projectConfDir == currentConfDir {
   150  		return
   151  	}
   152  
   153  	files, err := filepath.Glob(currentConfDir + "/*")
   154  	t.Require().NoError(err)
   155  
   156  	toBeDeleted := func(filePath string) (ok bool) {
   157  		filename := filepath.Base(filePath)
   158  		for _, name := range filenames {
   159  			if strings.EqualFold(filename, name) {
   160  				return true
   161  			}
   162  		}
   163  		return
   164  	}
   165  
   166  	for _, filePath := range files {
   167  		f, err := os.Stat(filePath)
   168  		if err != nil {
   169  			continue
   170  		}
   171  		if !toBeDeleted(filePath) {
   172  			continue
   173  		}
   174  		if f.IsDir() {
   175  			t.Require().NoError(os.RemoveAll(filePath))
   176  		} else {
   177  			t.Require().NoError(os.Remove(filePath))
   178  		}
   179  	}
   180  }
   181  
   182  func (t *Suite) copyFile(to, from string, stackSkip int) {
   183  	_, filename, _, ok := runtime.Caller(stackSkip)
   184  	t.Require().True(ok)
   185  	projectRoot := filepath.Dir(filename)
   186  
   187  	projectConfDir := filepath.Join(strings.TrimSuffix(projectRoot, "/cases"), "configs")
   188  	currentConfDir := filepath.Join(env.WorkDir, "configs")
   189  	if utils.IsStrBlank(currentConfDir) || projectConfDir == currentConfDir {
   190  		return
   191  	}
   192  
   193  	// create current conf dir
   194  	err := os.MkdirAll(currentConfDir, os.ModePerm)
   195  	if err != nil {
   196  		t.Require().ErrorIs(err, os.ErrExist)
   197  	}
   198  
   199  	// copy files
   200  	copyFileFn := func(dst, src string) {
   201  		from, err := os.Open(src)
   202  		t.Require().NoError(err)
   203  		defer func() { t.Nil(from.Close()) }()
   204  
   205  		to, err := os.Create(dst)
   206  		t.Require().NoError(err)
   207  		defer func() { t.Nil(to.Close()) }()
   208  
   209  		_, err = io.Copy(to, from)
   210  		t.Require().NoError(err)
   211  		t.T().Logf("copy file from %s to %s success\n", src, dst)
   212  	}
   213  
   214  	currentConfPath := filepath.Join(currentConfDir, to)
   215  	projectConfPath := filepath.Join(projectConfDir, from)
   216  
   217  	var f os.FileInfo
   218  	if f, err = os.Stat(projectConfPath); err == nil && !f.IsDir() {
   219  		copyFileFn(currentConfPath, projectConfPath)
   220  		return
   221  	}
   222  	t.Require().ErrorIs(err, os.ErrNotExist)
   223  
   224  	files, err := filepath.Glob(projectConfPath)
   225  	t.Require().NoError(err)
   226  	for _, filePath := range files {
   227  		filePath = strings.TrimPrefix(filePath, projectConfDir)
   228  
   229  		// mkdir -p
   230  		subDir := filepath.Dir(filepath.Join(currentConfDir, filePath))
   231  		if err := os.MkdirAll(subDir, os.ModePerm); err != nil {
   232  			t.Require().ErrorIs(err, os.ErrExist)
   233  		}
   234  
   235  		// skip dir
   236  		projectConfPath := filepath.Join(projectConfDir, filePath)
   237  		currentConfPath := filepath.Join(currentConfDir, filePath)
   238  		if f, err = os.Stat(projectConfPath); err == nil && !f.IsDir() {
   239  			copyFileFn(projectConfPath, currentConfPath)
   240  			continue
   241  		}
   242  		t.Require().ErrorIs(err, os.ErrNotExist)
   243  	}
   244  }