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 }