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  }