github.com/moleculer-go/moleculer@v0.3.3/service/service_internal_test.go (about) 1 package service 2 3 import ( 4 "errors" 5 "fmt" 6 "os" 7 "reflect" 8 9 "github.com/moleculer-go/cupaloy/v2" 10 "github.com/moleculer-go/moleculer" 11 "github.com/moleculer-go/moleculer/payload" 12 13 . "github.com/onsi/ginkgo" 14 . "github.com/onsi/gomega" 15 log "github.com/sirupsen/logrus" 16 ) 17 18 var logger = log.WithField("Unit Test", true) 19 var snap = cupaloy.New(cupaloy.FailOnUpdate(os.Getenv("UPDATE_SNAPSHOTS") == "true")) 20 21 var _ = Describe("Service", func() { 22 23 It("isInternalEvent() should reconized internal events", func() { 24 25 Expect(isInternalEvent(Event{ 26 name: "$services.changed", 27 serviceName: "source service", 28 })).Should(BeTrue()) 29 30 Expect(isInternalEvent(Event{ 31 name: "someservice.act$on", 32 serviceName: "source service", 33 })).Should(BeFalse()) 34 35 }) 36 37 rotateFunc := func(ctx moleculer.Context, params moleculer.Payload) interface{} { 38 return "Hellow Leleu ;) I'm rotating ..." 39 } 40 41 rotatesEventFunc := func(ctx moleculer.Context, params moleculer.Payload) { 42 fmt.Println("spining spining spining") 43 } 44 45 mixinTideFunc := func(ctx moleculer.Context, params moleculer.Payload) interface{} { 46 return "tide influence in the oceans" 47 } 48 49 mixinRotatesFunc := func(ctx moleculer.Context, params moleculer.Payload) { 50 fmt.Println("update tide in relation to the moon") 51 } 52 53 mixinMoonIsCloseFunc := func(ctx moleculer.Context, params moleculer.Payload) { 54 fmt.Println("rise the tide !") 55 } 56 57 moonMixIn := moleculer.Mixin{ 58 Name: "moon", 59 Settings: map[string]interface{}{ 60 "craters": true, 61 }, 62 Metadata: map[string]interface{}{ 63 "resolution": "high", 64 }, 65 Hooks: map[string]interface{}{ 66 "earth": "true", 67 }, 68 Actions: []moleculer.Action{ 69 moleculer.Action{ 70 Name: "tide", 71 Handler: mixinTideFunc, 72 }, 73 }, 74 Events: []moleculer.Event{ 75 moleculer.Event{ 76 Name: "earth.rotates", 77 Handler: mixinRotatesFunc, 78 }, 79 moleculer.Event{ 80 Name: "moon.isClose", 81 Handler: mixinMoonIsCloseFunc, 82 }, 83 }, 84 } 85 86 serviceSchema := moleculer.ServiceSchema{ 87 Name: "earth", 88 Version: "0.2", 89 Settings: map[string]interface{}{ 90 "dinosauros": true, 91 }, 92 Metadata: map[string]interface{}{ 93 "star-system": "sun", 94 }, 95 Hooks: map[string]interface{}{ 96 "solar-system": "true", 97 }, 98 Mixins: []moleculer.Mixin{moonMixIn}, 99 Actions: []moleculer.Action{ 100 moleculer.Action{ 101 Name: "rotate", 102 Handler: rotateFunc, 103 }, 104 }, 105 Events: []moleculer.Event{ 106 moleculer.Event{ 107 Name: "earth.rotates", 108 Handler: rotatesEventFunc, 109 }, 110 }, 111 } 112 113 It("Should merge and overwrite existing actions", func() { 114 merged := extendActions(serviceSchema, &moonMixIn) 115 actions := merged.Actions 116 Expect(actions).Should(HaveLen(2)) 117 Expect(snap.Snapshot(actions)).Should(Succeed()) 118 }) 119 120 It("Should merge and overwrite existing events", func() { 121 merged := concatenateEvents(serviceSchema, &moonMixIn) 122 Expect(merged.Events).Should(HaveLen(2)) 123 Expect(snap.Snapshot(merged.Events)).Should(Succeed()) 124 }) 125 126 It("Should merge and overwrite existing settings", func() { 127 128 mergedServiceSettings := extendSettings(serviceSchema, &moonMixIn) 129 Expect(mergedServiceSettings.Settings).Should(Equal(map[string]interface{}{ 130 "dinosauros": true, 131 "craters": true, 132 }, 133 )) 134 }) 135 136 It("Should merge and overwrite existing metadata", func() { 137 138 mergedServiceMetadata := extendMetadata(serviceSchema, &moonMixIn) 139 Expect(mergedServiceMetadata.Metadata).Should(Equal(map[string]interface{}{ 140 "star-system": "sun", 141 "resolution": "high", 142 }, 143 )) 144 }) 145 146 It("Should merge and overwrite existing hooks", func() { 147 mergedServiceHooks := extendHooks(serviceSchema, &moonMixIn) 148 Expect(mergedServiceHooks.Hooks).Should(Equal(map[string]interface{}{ 149 "solar-system": "true", 150 "earth": "true", 151 }, 152 )) 153 }) 154 155 It("Should apply mixins collectively", func() { 156 merged := applyMixins(serviceSchema) 157 Expect(merged.Actions).Should(HaveLen(2)) 158 Expect(merged.Events).Should(HaveLen(2)) 159 Expect(snap.Snapshot(merged)).Should(Succeed()) 160 }) 161 162 It("actionName() should return the method name in camel case", func() { 163 Expect(actionName("SetLogRate")).Should(Equal("setLogRate")) 164 Expect(actionName("someName")).Should(Equal("someName")) 165 Expect(actionName("Name")).Should(Equal("name")) 166 }) 167 168 It("handlerTemplate() should find a template for a complete action", func() { 169 v := reflect.ValueOf(&ServiceTestObj{}) 170 m := v.MethodByName("CompleteAction") 171 Expect(m.IsValid()).Should(BeTrue()) 172 ah := handlerTemplate(m) 173 Expect(ah).ShouldNot(BeNil()) 174 Expect(ah(nil, nil)).Should(Equal("CompleteAction Invoked!")) 175 176 m = v.MethodByName("NonPointerCompleteAction") 177 Expect(m.IsValid()).Should(BeTrue()) 178 ah = handlerTemplate(m) 179 Expect(ah).ShouldNot(BeNil()) 180 Expect(ah(nil, nil)).Should(Equal("NonPointerCompleteAction Invoked!")) 181 }) 182 183 It("handlerTemplate() should find a template for a complete action no return", func() { 184 v := reflect.ValueOf(&ServiceTestObj{}) 185 m := v.MethodByName("CompleteActionNoReturn") 186 Expect(m.IsValid()).Should(BeTrue()) 187 ah := handlerTemplate(m) 188 Expect(ah).ShouldNot(BeNil()) 189 Expect(ah(nil, nil)).Should(BeNil()) 190 }) 191 192 It("handlerTemplate() should find a template for an action with just context args", func() { 193 v := reflect.ValueOf(&ServiceTestObj{}) 194 m := v.MethodByName("JustContext") 195 Expect(m.IsValid()).Should(BeTrue()) 196 ah := handlerTemplate(m) 197 Expect(ah).ShouldNot(BeNil()) 198 Expect(ah(nil, nil)).Should(Equal("JustContext Invoked!")) 199 }) 200 201 It("handlerTemplate() should find a template for an action with just context args and no return", func() { 202 v := reflect.ValueOf(&ServiceTestObj{}) 203 m := v.MethodByName("JustContextNoReturn") 204 Expect(m.IsValid()).Should(BeTrue()) 205 ah := handlerTemplate(m) 206 Expect(ah).ShouldNot(BeNil()) 207 Expect(ah(nil, nil)).Should(BeNil()) 208 }) 209 210 It("handlerTemplate() should find a template for an action with just params args", func() { 211 v := reflect.ValueOf(&ServiceTestObj{}) 212 m := v.MethodByName("JustParams") 213 Expect(m.IsValid()).Should(BeTrue()) 214 ah := handlerTemplate(m) 215 Expect(ah).ShouldNot(BeNil()) 216 Expect(ah(nil, nil)).Should(Equal("JustParams Invoked!")) 217 }) 218 219 It("handlerTemplate() should find a template for an action with just params args and no return", func() { 220 v := reflect.ValueOf(&ServiceTestObj{}) 221 m := v.MethodByName("JustParamsNoReturn") 222 Expect(m.IsValid()).Should(BeTrue()) 223 ah := handlerTemplate(m) 224 Expect(ah).ShouldNot(BeNil()) 225 Expect(ah(nil, nil)).Should(BeNil()) 226 }) 227 228 It("handlerTemplate() should find a template for an action with no args", func() { 229 v := reflect.ValueOf(&ServiceTestObj{}) 230 m := v.MethodByName("NoArgs") 231 Expect(m.IsValid()).Should(BeTrue()) 232 ah := handlerTemplate(m) 233 Expect(ah).ShouldNot(BeNil()) 234 Expect(ah(nil, nil)).Should(Equal("NoArgs Invoked!")) 235 }) 236 237 It("handlerTemplate() should find a template for an action with just params args and no return", func() { 238 v := reflect.ValueOf(&ServiceTestObj{}) 239 m := v.MethodByName("NoArgsNoReturn") 240 Expect(m.IsValid()).Should(BeTrue()) 241 ah := handlerTemplate(m) 242 Expect(ah).ShouldNot(BeNil()) 243 Expect(ah(nil, nil)).Should(BeNil()) 244 }) 245 246 It("getName() should invoke the Name() method on pointer and non pointer methods", func() { 247 name, err := getName(NoPointers{}) 248 Expect(err).Should(BeNil()) 249 Expect(name).Should(Equal("My Name")) 250 251 name, err = getName(&NoPointers{}) 252 Expect(err).Should(BeNil()) 253 Expect(name).Should(Equal("My Name")) 254 255 name, err = getName(&ServiceTestObj{}) 256 Expect(err).Should(BeNil()) 257 Expect(name).Should(Equal("My Name")) 258 259 }) 260 261 It("getParamTypes() should return a list of ordered parameter names", func() { 262 v := reflect.ValueOf(&ServiceTestObj{}) 263 m := v.MethodByName("Add") 264 Expect(m.IsValid()).Should(BeTrue()) 265 ptypes := getParamTypes(m) 266 Expect(ptypes).ShouldNot(BeNil()) 267 Expect(len(ptypes)).Should(Equal(2)) 268 Expect(ptypes).Should(Equal([]string{"int", "int"})) 269 }) 270 271 It("buildArgs() should return a list of refelct.Value arguments", func() { 272 ptypes := []string{"int", "string", "int"} 273 list := []moleculer.Payload{ 274 payload.New(1), 275 payload.New("hi"), 276 payload.New(42), 277 } 278 r := buildArgs(ptypes, payload.New(list)) 279 Expect(len(r)).Should(Equal(3)) 280 Expect(r[0].Interface()).Should(Equal(1)) 281 Expect(r[1].Interface()).Should(Equal("hi")) 282 Expect(r[2].Interface()).Should(Equal(42)) 283 }) 284 285 It("validateArgs() should return error when param is invalid", func() { 286 ptypes := []string{"int", "string", "int"} 287 matchError1 := Equal("This action requires arguments to be sent in an array. #3 arguments - types: [int string int]") 288 matchError2 := Equal("This action requires #3 arguments - types: [int string int]") 289 290 Expect(validateArgs(ptypes, payload.New(1)).Error()).Should(matchError1) 291 Expect(validateArgs(ptypes, payload.New("a")).Error()).Should(matchError1) 292 Expect(validateArgs(ptypes, payload.New(nil)).Error()).Should(matchError1) 293 Expect(validateArgs(ptypes, payload.New([]interface{}{})).Error()).Should(matchError2) 294 Expect(validateArgs(ptypes, payload.New([]interface{}{1})).Error()).Should(matchError2) 295 Expect(validateArgs(ptypes, payload.New([]interface{}{1, 2})).Error()).Should(matchError2) 296 }) 297 298 It("validateArgs() should return nil when param is valid", func() { 299 ptypes := []string{"int", "string", "int"} 300 Expect(validateArgs(ptypes, payload.New([]interface{}{1, "a", 2}))).Should(BeNil()) 301 }) 302 303 It("checkReturn() should return nil when its args is nil", func() { 304 var in []reflect.Value = nil 305 Expect(checkReturn(in)).Should(BeNil()) 306 }) 307 308 It("checkReturn() should return nil when its args is empty", func() { 309 in := []reflect.Value{} 310 Expect(checkReturn(in)).Should(BeNil()) 311 }) 312 313 It("checkReturn() should return the only value in the args", func() { 314 in := []reflect.Value{reflect.ValueOf("hellow")} 315 Expect(checkReturn(in)).Should(Equal("hellow")) 316 }) 317 318 It("checkReturn() should return the error, when the last params is an error not nil", func() { 319 err := errors.New("some error...") 320 //second is error 321 Expect(checkReturn([]reflect.Value{reflect.ValueOf(nil), reflect.ValueOf(err)})).Should(Equal(err)) 322 //third is error 323 Expect(checkReturn([]reflect.Value{reflect.ValueOf(nil), reflect.ValueOf(nil), reflect.ValueOf(err)})).Should(Equal(err)) 324 //forth is error 325 Expect(checkReturn([]reflect.Value{reflect.ValueOf(nil), reflect.ValueOf(nil), reflect.ValueOf(nil), reflect.ValueOf(err)})).Should(Equal(err)) 326 }) 327 328 It("checkReturn() should return convert multiple items returned into an array", func() { 329 in := []reflect.Value{reflect.ValueOf("hellow"), reflect.ValueOf("worderfull"), reflect.ValueOf("world")} 330 r := checkReturn(in) 331 Expect(r).ShouldNot(BeNil()) 332 Expect(r.(moleculer.Payload).StringArray()).Should(Equal([]string{"hellow", "worderfull", "world"})) 333 }) 334 335 It("variableArgsHandler() should create an action handler that can deal with variable arguments.", func() { 336 svc := &ServiceTestObj{} 337 v := reflect.ValueOf(svc) 338 m := v.MethodByName("Add") 339 ah := variableArgsHandler(m) 340 p := payload.New([]int{10, 23}) 341 r := ah(nil, p) 342 Expect(r).Should(Equal(33)) 343 344 m = v.MethodByName("NoArgsNoReturn") 345 ah = variableArgsHandler(m) 346 r = ah(nil, payload.New(nil)) 347 Expect(r).Should(BeNil()) 348 Expect(svc.NoArgsNoReturnCalled).Should(BeTrue()) 349 350 m = v.MethodByName("NoArgs") 351 ah = variableArgsHandler(m) 352 r = ah(nil, payload.New(nil)) 353 Expect(r).Should(Equal("NoArgs Invoked!")) 354 355 m = v.MethodByName("JustParamsNoReturn") 356 ah = variableArgsHandler(m) 357 r = ah(nil, payload.Empty()) 358 Expect(r).Should(BeNil()) 359 Expect(svc.JustParamsNoReturnCalled).Should(BeTrue()) 360 }) 361 362 It("wrapAction() should create an action with the given method.", func() { 363 svc := &ServiceTestObj{} 364 v := reflect.ValueOf(svc) 365 mv := v.MethodByName("Add") 366 m, _ := v.Type().MethodByName("Add") 367 368 a := wrapAction(m, mv) 369 Expect(a.Name).Should(Equal("add")) 370 p := payload.New([]int{10, 23}) 371 r := a.Handler(nil, p) 372 Expect(r).Should(Equal(33)) 373 374 mv = v.MethodByName("JustParamsNoReturn") 375 m, _ = v.Type().MethodByName("JustParamsNoReturn") 376 a = wrapAction(m, mv) 377 Expect(a.Name).Should(Equal("justParamsNoReturn")) 378 }) 379 380 It("extractActions() should extract all actions for an object.", func() { 381 acts := extractActions(&ServiceTestObj{}) 382 Expect(len(acts)).Should(Equal(10)) 383 Expect(snap.Snapshot(acts)).Should(Succeed()) 384 }) 385 386 It("validActionName() should check if a method name is valid for Action.", func() { 387 Expect(validActionName("Name")).Should(BeFalse()) 388 Expect(validActionName("Version")).Should(BeFalse()) 389 Expect(validActionName("Dependencies")).Should(BeFalse()) 390 Expect(validActionName("Settings")).Should(BeFalse()) 391 Expect(validActionName("Metadata")).Should(BeFalse()) 392 Expect(validActionName("Mixins")).Should(BeFalse()) 393 Expect(validActionName("Events")).Should(BeFalse()) 394 395 Expect(validActionName("ChangeName")).Should(BeTrue()) 396 Expect(validActionName("JustContext")).Should(BeTrue()) 397 Expect(validActionName("SomeName")).Should(BeTrue()) 398 Expect(validActionName("Ahah")).Should(BeTrue()) 399 400 }) 401 402 It("extractCreated() should return func with params and wrap func with no params.", func() { 403 svc := &ServiceTestObj{} 404 fn := extractCreated(svc) 405 Expect(fn).ShouldNot(BeNil()) 406 fn(moleculer.ServiceSchema{}, nil) 407 Expect(svc.CreatedCalled).Should(BeTrue()) 408 409 svc2 := &ServiceTestNoParams{} 410 fn = extractCreated(svc2) 411 Expect(fn).ShouldNot(BeNil()) 412 fn(moleculer.ServiceSchema{}, nil) 413 Expect(svc2.CreatedCalled).Should(BeTrue()) 414 415 svc3 := &EmptyObj{} 416 fn = extractCreated(svc3) 417 Expect(fn).Should(BeNil()) 418 }) 419 420 It("extractStarted() should return func with params and wrap func with no params.", func() { 421 svc := &ServiceTestObj{} 422 fn := extractStarted(svc) 423 Expect(fn).ShouldNot(BeNil()) 424 fn(nil, moleculer.ServiceSchema{}) 425 Expect(svc.StartedCalled).Should(BeTrue()) 426 427 svc2 := &ServiceTestNoParams{} 428 fn = extractStarted(svc2) 429 Expect(fn).ShouldNot(BeNil()) 430 fn(nil, moleculer.ServiceSchema{}) 431 Expect(svc2.StartedCalled).Should(BeTrue()) 432 433 svc3 := &EmptyObj{} 434 fn = extractStarted(svc3) 435 Expect(fn).Should(BeNil()) 436 }) 437 438 It("extractStopped() should return func with params and wrap func with no params.", func() { 439 svc := &ServiceTestObj{} 440 fn := extractStopped(svc) 441 Expect(fn).ShouldNot(BeNil()) 442 fn(nil, moleculer.ServiceSchema{}) 443 Expect(svc.StoppedCalled).Should(BeTrue()) 444 445 svc2 := &ServiceTestNoParams{} 446 fn = extractStopped(svc2) 447 Expect(fn).ShouldNot(BeNil()) 448 fn(nil, moleculer.ServiceSchema{}) 449 Expect(svc2.StoppedCalled).Should(BeTrue()) 450 451 svc3 := &EmptyObj{} 452 fn = extractStopped(svc3) 453 Expect(fn).Should(BeNil()) 454 }) 455 456 It("ParseVersion()", func() { 457 intVer := 1 458 Expect(ParseVersion(intVer)).Should(Equal("1")) 459 460 floatVer := 1.1 461 Expect(ParseVersion(floatVer)).Should(Equal("1.1")) 462 463 strVer := "v0.1.1" 464 Expect(ParseVersion(strVer)).Should(Equal("v0.1.1")) 465 }) 466 467 }) 468 469 type NoPointers struct { 470 } 471 472 func (svc NoPointers) Name() string { 473 return "My Name" 474 } 475 476 type ServiceTestObj struct { 477 NoArgsNoReturnCalled bool 478 JustParamsNoReturnCalled bool 479 CreatedCalled bool 480 StartedCalled bool 481 StoppedCalled bool 482 } 483 484 func (svc *ServiceTestObj) Created(moleculer.ServiceSchema, *log.Entry) { 485 svc.CreatedCalled = true 486 } 487 488 func (svc *ServiceTestObj) Started(moleculer.BrokerContext, moleculer.ServiceSchema) { 489 svc.StartedCalled = true 490 } 491 492 func (svc *ServiceTestObj) Stopped(moleculer.BrokerContext, moleculer.ServiceSchema) { 493 svc.StoppedCalled = true 494 } 495 496 func (svc *ServiceTestObj) Name() string { 497 return "My Name" 498 } 499 500 func (svc *ServiceTestObj) Add(firstValue int, secondValue int) int { 501 return firstValue + secondValue 502 } 503 504 func (svc *ServiceTestObj) NoArgsNoReturn() { 505 svc.NoArgsNoReturnCalled = true 506 } 507 508 func (svc *ServiceTestObj) NoArgs() interface{} { 509 return "NoArgs Invoked!" 510 } 511 512 func (svc *ServiceTestObj) JustParamsNoReturn(p moleculer.Payload) { 513 svc.JustParamsNoReturnCalled = true 514 } 515 516 func (svc *ServiceTestObj) JustParams(p moleculer.Payload) interface{} { 517 return "JustParams Invoked!" 518 } 519 520 func (svc *ServiceTestObj) JustContextNoReturn(context moleculer.Context) { 521 522 } 523 524 func (svc *ServiceTestObj) JustContext(context moleculer.Context) interface{} { 525 return "JustContext Invoked!" 526 } 527 528 func (svc *ServiceTestObj) CompleteActionNoReturn(ctx moleculer.Context, params moleculer.Payload) { 529 530 } 531 532 func (svc *ServiceTestObj) CompleteAction(context moleculer.Context, params moleculer.Payload) interface{} { 533 return "CompleteAction Invoked!" 534 } 535 536 func (svc ServiceTestObj) NonPointerCompleteAction(context moleculer.Context, params moleculer.Payload) interface{} { 537 return "NonPointerCompleteAction Invoked!" 538 } 539 540 type ServiceTestNoParams struct { 541 CreatedCalled bool 542 StartedCalled bool 543 StoppedCalled bool 544 } 545 546 func (svc *ServiceTestNoParams) Created(moleculer.ServiceSchema, *log.Entry) { 547 svc.CreatedCalled = true 548 } 549 550 func (svc *ServiceTestNoParams) Started(moleculer.BrokerContext, moleculer.ServiceSchema) { 551 svc.StartedCalled = true 552 } 553 554 func (svc *ServiceTestNoParams) Stopped(moleculer.BrokerContext, moleculer.ServiceSchema) { 555 svc.StoppedCalled = true 556 } 557 558 type EmptyObj struct { 559 }