github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/tests/tests.go (about)

     1  /*
     2   * Copyright 2023 Wang Min Xiang
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   * 	http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   */
    17  
    18  package tests
    19  
    20  import (
    21  	"fmt"
    22  	"github.com/aacfactory/configures"
    23  	"github.com/aacfactory/errors"
    24  	"github.com/aacfactory/fns/barriers"
    25  	"github.com/aacfactory/fns/clusters"
    26  	"github.com/aacfactory/fns/commons/switchs"
    27  	"github.com/aacfactory/fns/commons/versions"
    28  	"github.com/aacfactory/fns/configs"
    29  	"github.com/aacfactory/fns/context"
    30  	"github.com/aacfactory/fns/logs"
    31  	"github.com/aacfactory/fns/runtime"
    32  	"github.com/aacfactory/fns/services"
    33  	"github.com/aacfactory/fns/shareds"
    34  	"github.com/aacfactory/fns/transports"
    35  	"github.com/aacfactory/fns/transports/fast"
    36  	"github.com/aacfactory/workers"
    37  	"os"
    38  	"path/filepath"
    39  	"reflect"
    40  	"strings"
    41  	"time"
    42  )
    43  
    44  type machine struct {
    45  	rt      *runtime.Runtime
    46  	manager services.EndpointsManager
    47  }
    48  
    49  func (m *machine) Shutdown() {
    50  	ctx := context.TODO()
    51  	m.manager.Shutdown(ctx)
    52  }
    53  
    54  var (
    55  	app *machine = nil
    56  )
    57  
    58  type Options struct {
    59  	deps                  []services.Service
    60  	config                *configs.Config
    61  	configRetrieverOption configures.RetrieverOption
    62  	configActive          string
    63  	transport             transports.Transport
    64  }
    65  
    66  type Option func(options *Options) (err error)
    67  
    68  func WithDependence(dep ...services.Service) Option {
    69  	return func(options *Options) (err error) {
    70  		options.deps = append(options.deps, dep...)
    71  		return
    72  	}
    73  }
    74  
    75  func Config() configs.Config {
    76  	return configs.New()
    77  }
    78  
    79  func WithConfig(config configs.Config) Option {
    80  	return func(options *Options) (err error) {
    81  		options.config = &config
    82  		return
    83  	}
    84  }
    85  
    86  func WithConfigActive(active string) Option {
    87  	return func(options *Options) (err error) {
    88  		options.configActive = active
    89  		return
    90  	}
    91  }
    92  
    93  func WithConfigRetriever(path string, format string, active string, prefix string, splitter byte) Option {
    94  	return func(options *Options) (err error) {
    95  		path = strings.TrimSpace(path)
    96  		if path == "" {
    97  			err = fmt.Errorf("path is empty")
    98  			return
    99  		}
   100  		active = strings.TrimSpace(active)
   101  		format = strings.ToUpper(strings.TrimSpace(format))
   102  		store := configures.NewFileStore(path, prefix, splitter)
   103  		options.configRetrieverOption = configures.RetrieverOption{
   104  			Active: active,
   105  			Format: format,
   106  			Store:  store,
   107  		}
   108  		return
   109  	}
   110  }
   111  
   112  func WithTransport(transport transports.Transport) Option {
   113  	return func(options *Options) (err error) {
   114  		options.transport = transport
   115  		return
   116  	}
   117  }
   118  
   119  func getConfigDir(src string) (dir string, err error) {
   120  	if src == "" {
   121  		err = errors.Warning("config dir is not found")
   122  		return
   123  	}
   124  	if !filepath.IsAbs(src) {
   125  		src, err = filepath.Abs(src)
   126  		if err != nil {
   127  			return
   128  		}
   129  	}
   130  	dirs, readErr := os.ReadDir(src)
   131  	if readErr != nil {
   132  		err = readErr
   133  		return
   134  	}
   135  	for _, entry := range dirs {
   136  		if entry.IsDir() && entry.Name() == "configs" {
   137  			dir = filepath.Join(src, "configs")
   138  			return
   139  		}
   140  	}
   141  	parentDir := filepath.Dir(src)
   142  	if parentDir == src {
   143  		err = errors.Warning("config dir is not found")
   144  		return
   145  	}
   146  	dir, err = getConfigDir(parentDir)
   147  	return
   148  }
   149  
   150  // Setup
   151  // use local config
   152  func Setup(service services.Service, options ...Option) (err error) {
   153  	opt := Options{
   154  		deps:                  nil,
   155  		config:                nil,
   156  		configRetrieverOption: configures.RetrieverOption{},
   157  		configActive:          "local",
   158  		transport:             fast.New(),
   159  	}
   160  	for _, option := range options {
   161  		err = option(&opt)
   162  		if err != nil {
   163  			err = errors.Warning("fns: setup testing failed").WithCause(err)
   164  			return
   165  		}
   166  	}
   167  	if service == nil {
   168  		err = errors.Warning("fns: setup testing failed").WithCause(fmt.Errorf("service is nil"))
   169  		return
   170  	}
   171  	appId := "tests"
   172  	appVersion := versions.Origin()
   173  	// config
   174  	config := opt.config
   175  	if config == nil {
   176  		if reflect.ValueOf(opt.configRetrieverOption).IsZero() {
   177  			configDir, configDirErr := getConfigDir(".")
   178  			if configDirErr != nil {
   179  				err = errors.Warning("fns: setup testing failed").WithCause(configDirErr)
   180  				return
   181  			}
   182  			opt.configRetrieverOption = configures.RetrieverOption{
   183  				Active: opt.configActive,
   184  				Format: "YAML",
   185  				Store:  configures.NewFileStore(configDir, "fns", '-'),
   186  			}
   187  		}
   188  		configRetriever, configRetrieverErr := configures.NewRetriever(opt.configRetrieverOption)
   189  		if configRetrieverErr != nil {
   190  			err = errors.Warning("fns: setup testing failed").WithCause(configRetrieverErr)
   191  			return
   192  		}
   193  		configure, configureErr := configRetriever.Get()
   194  		if configureErr != nil {
   195  			err = errors.Warning("fns: setup testing failed").WithCause(configureErr)
   196  			return
   197  		}
   198  		config = &configs.Config{}
   199  		configErr := configure.As(config)
   200  		if configErr != nil {
   201  			err = errors.Warning("fns: setup testing failed").WithCause(configErr)
   202  			return
   203  		}
   204  	}
   205  
   206  	// log
   207  	logger, loggerErr := logs.New(config.Log, nil)
   208  	if loggerErr != nil {
   209  		err = errors.Warning("fns: setup testing failed").WithCause(loggerErr)
   210  		return
   211  	}
   212  	// worker
   213  	workerOptions := make([]workers.Option, 0, 1)
   214  	if workersMax := config.Runtime.Workers.Max; workersMax > 0 {
   215  		workerOptions = append(workerOptions, workers.MaxWorkers(workersMax))
   216  	}
   217  	if workersMaxIdleSeconds := config.Runtime.Workers.MaxIdleSeconds; workersMaxIdleSeconds > 0 {
   218  		workerOptions = append(workerOptions, workers.MaxIdleWorkerDuration(time.Duration(workersMaxIdleSeconds)*time.Second))
   219  	}
   220  	worker := workers.New(workerOptions...)
   221  	// status
   222  	status := &switchs.Switch{}
   223  	// manager
   224  	var manager services.EndpointsManager
   225  
   226  	local := services.New(appId, appVersion, logger.With("fns", "endpoints"), config.Services, worker)
   227  
   228  	// barrier
   229  	var barrier barriers.Barrier
   230  	// shared
   231  	var shared shareds.Shared
   232  
   233  	// cluster
   234  	if clusterConfig := config.Cluster; clusterConfig.Name != "" {
   235  		transport := opt.transport
   236  		transportErr := transport.Construct(transports.Options{
   237  			Log:    logger.With("transport", transport.Name()),
   238  			Config: config.Transport,
   239  			Handler: transports.HandlerFunc(func(writer transports.ResponseWriter, request transports.Request) {
   240  			}),
   241  		})
   242  		if transportErr != nil {
   243  			err = errors.Warning("fns: setup testing failed").WithCause(transportErr)
   244  			return
   245  		}
   246  		var clusterErr error
   247  		manager, shared, barrier, _, clusterErr = clusters.New(clusters.Options{
   248  			Id:      appId,
   249  			Version: appVersion,
   250  			Port:    transport.Port(),
   251  			Log:     logger.With("fns", "cluster"),
   252  			Worker:  worker,
   253  			Local:   local,
   254  			Dialer:  opt.transport,
   255  			Config:  clusterConfig,
   256  		})
   257  		if clusterErr != nil {
   258  			err = errors.Warning("fns: setup testing failed").WithCause(clusterErr)
   259  			return
   260  		}
   261  	} else {
   262  		var sharedErr error
   263  		shared, sharedErr = shareds.Local(logger.With("shared", "local"), config.Runtime.Shared)
   264  		if sharedErr != nil {
   265  			err = errors.Warning("fns: setup testing failed").WithCause(sharedErr)
   266  			return
   267  		}
   268  		barrier = barriers.New()
   269  		manager = local
   270  	}
   271  
   272  	addErr := manager.Add(service)
   273  	if addErr != nil {
   274  		err = errors.Warning("fns: setup testing failed").WithCause(addErr)
   275  		return
   276  	}
   277  	for _, dep := range opt.deps {
   278  		depErr := manager.Add(dep)
   279  		if depErr != nil {
   280  			err = errors.Warning("fns: setup testing failed").WithCause(depErr)
   281  			return
   282  		}
   283  	}
   284  
   285  	// runtime
   286  	rt := runtime.New(
   287  		appId, "tests", appVersion,
   288  		status, logger, worker,
   289  		manager,
   290  		barrier, shared,
   291  	)
   292  
   293  	app = &machine{
   294  		rt:      rt,
   295  		manager: manager,
   296  	}
   297  
   298  	return
   299  }
   300  
   301  func Teardown() {
   302  	if app != nil {
   303  		app.Shutdown()
   304  	}
   305  }
   306  
   307  func TODO() context.Context {
   308  	ctx := runtime.With(context.TODO(), app.rt)
   309  	logs.With(ctx, app.rt.RootLog().With("tests", "testing"))
   310  	return ctx
   311  }