github.com/influx6/npkg@v0.8.8/nreflect/reflection_test.go (about)

     1  package nreflect_test
     2  
     3  import (
     4  	"errors"
     5  	"fmt"
     6  	"reflect"
     7  	"testing"
     8  	"time"
     9  
    10  	nreflect "github.com/influx6/npkg/nreflect"
    11  )
    12  
    13  type bull string
    14  
    15  type speaker interface {
    16  	Speak() string
    17  }
    18  
    19  // mosnter provides a basic struct test case type.
    20  type monster struct {
    21  	Name  string
    22  	Items []bull
    23  }
    24  
    25  // Speak returns the sound the monster makes.
    26  func (m *monster) Speak() string {
    27  	return "Raaaaaaarggg!"
    28  }
    29  
    30  func get(t *testing.T, sm speaker) {
    31  	name, embedded, err := nreflect.ExternalTypeNames(sm)
    32  	if err != nil {
    33  		t.Fatalf("Should be able to retrieve field names arguments lists\n")
    34  	}
    35  	t.Logf("PageName: %s", name)
    36  	t.Logf("Fields: %+q", embedded)
    37  	t.Logf("Should be able to retrieve function arguments lists")
    38  }
    39  
    40  type Addrs struct {
    41  	Addr string
    42  }
    43  
    44  type addrFunc func(Addrs) error
    45  
    46  var errorType = reflect.TypeOf((*error)(nil)).Elem()
    47  
    48  func TestIsSettableType(t *testing.T) {
    49  	m2 := errors.New("invalid error")
    50  	if !nreflect.IsSettableType(errorType, reflect.TypeOf(m2)) {
    51  		t.Fatalf("Should have error matching type")
    52  	}
    53  }
    54  
    55  func TestIsSettable(t *testing.T) {
    56  	m2 := errors.New("invalid error")
    57  	if !nreflect.IsSettable(errorType, reflect.ValueOf(m2)) {
    58  		t.Fatalf("Should have error matching type")
    59  	}
    60  
    61  	m1 := mo("invalid error")
    62  	if !nreflect.IsSettable(errorType, reflect.ValueOf(m1)) {
    63  		t.Fatalf("Should have error matching type")
    64  	}
    65  }
    66  
    67  type mo string
    68  
    69  func (m mo) Error() string {
    70  	return string(m)
    71  }
    72  
    73  func TestValidateFunc_Bad(t *testing.T) {
    74  	var testFunc = func(v string) string {
    75  		return "Hello " + v
    76  	}
    77  
    78  	err := nreflect.ValidateFunc(testFunc, []nreflect.TypeValidation{
    79  		func(types []reflect.Type) error {
    80  			if len(types) == 1 {
    81  				return nil
    82  			}
    83  			return errors.New("bad")
    84  		},
    85  	}, []nreflect.TypeValidation{
    86  		func(types []reflect.Type) error {
    87  			if len(types) == 0 {
    88  				return nil
    89  			}
    90  			return errors.New("bad")
    91  		},
    92  	})
    93  
    94  	if err == nil {
    95  		t.Fatalf("Should have function invalid to conditions")
    96  	}
    97  }
    98  
    99  func TestValidateFunc(t *testing.T) {
   100  	var testFunc = func(v string) string {
   101  		return "Hello " + v
   102  	}
   103  
   104  	err := nreflect.ValidateFunc(testFunc, []nreflect.TypeValidation{
   105  		func(types []reflect.Type) error {
   106  			if len(types) == 1 {
   107  				return nil
   108  			}
   109  			return errors.New("bad")
   110  		},
   111  	}, []nreflect.TypeValidation{
   112  		func(types []reflect.Type) error {
   113  			if len(types) == 1 {
   114  				return nil
   115  			}
   116  			return errors.New("bad")
   117  		},
   118  	})
   119  
   120  	if err != nil {
   121  		t.Fatalf("Should have function valid to conditions")
   122  	}
   123  }
   124  
   125  func TestFunctionApply_OneArgument(t *testing.T) {
   126  	var testFunc = func(v string) string {
   127  		return "Hello " + v
   128  	}
   129  
   130  	res, err := nreflect.CallFunc(testFunc, "Alex")
   131  	if err != nil {
   132  		t.Fatalf("Should have executed function")
   133  	}
   134  	t.Logf("Should have executed function")
   135  
   136  	if !reflect.DeepEqual(res, []interface{}{"Hello Alex"}) {
   137  		t.Logf("Received: %q", res)
   138  		t.Fatalf("Expected value unmatched")
   139  	}
   140  }
   141  
   142  func TestFunctionApply_ThreeArgumentWithError(t *testing.T) {
   143  	bad := errors.New("bad")
   144  	var testFunc = func(v string, i int, d bool) ([]interface{}, error) {
   145  		return []interface{}{v, i, d}, bad
   146  	}
   147  
   148  	res, err := nreflect.CallFunc(testFunc, "Alex", 1, false)
   149  	if err != nil {
   150  		t.Fatalf("Should have executed function")
   151  	}
   152  	t.Logf("Should have executed function")
   153  
   154  	if !reflect.DeepEqual(res, []interface{}{[]interface{}{"Alex", 1, false}, bad}) {
   155  		t.Logf("Expected: %q", []interface{}{[]interface{}{"Alex", 1, false}, bad})
   156  		t.Logf("Received: %q", res)
   157  		t.Fatalf("Expected value unmatched")
   158  	}
   159  }
   160  
   161  func TestFunctionApply_ThreeArgumentWithVariadic(t *testing.T) {
   162  	var testFunc = func(v string, i int, d ...bool) []interface{} {
   163  		return []interface{}{v, i, d}
   164  	}
   165  
   166  	res, err := nreflect.CallFunc(testFunc, "Alex", 1, []bool{false})
   167  	if err != nil {
   168  		t.Fatalf("Should have executed function")
   169  	}
   170  	t.Logf("Should have executed function")
   171  
   172  	if !reflect.DeepEqual(res, []interface{}{[]interface{}{"Alex", 1, []bool{false}}}) {
   173  		t.Logf("Expected: %q", []interface{}{[]interface{}{"Alex", 1, []bool{false}}})
   174  		t.Logf("Received: %q", res)
   175  		t.Fatalf("Expected value unmatched")
   176  	}
   177  }
   178  
   179  func TestFunctionApply_ThreeArgument(t *testing.T) {
   180  	var testFunc = func(v string, i int, d bool) string {
   181  		return "Hello " + v
   182  	}
   183  
   184  	res, err := nreflect.CallFunc(testFunc, "Alex", 1, false)
   185  	if err != nil {
   186  		t.Fatalf("Should have executed function")
   187  	}
   188  	t.Logf("Should have executed function")
   189  
   190  	if !reflect.DeepEqual(res, []interface{}{"Hello Alex"}) {
   191  		t.Logf("Received: %q", res)
   192  		t.Fatalf("Expected value unmatched")
   193  	}
   194  }
   195  
   196  func TestMatchFunction(t *testing.T) {
   197  	var addr1 = func(_ Addrs) error { return nil }
   198  	var addr2 = func(_ Addrs) error { return nil }
   199  
   200  	if !nreflect.MatchFunction(addr1, addr2) {
   201  		t.Fatalf("Should have matched argument types successfully")
   202  	}
   203  	t.Logf("Should have matched argument types successfully")
   204  
   205  	if !nreflect.MatchFunction(&addr1, &addr2) {
   206  		t.Fatalf("Should have matched argument types successfully")
   207  	}
   208  	t.Logf("Should have matched argument types successfully")
   209  
   210  	if nreflect.MatchFunction(&addr1, addr2) {
   211  		t.Fatalf("Should have failed matched argument types successfully")
   212  	}
   213  	t.Logf("Should have failed matched argument types successfully")
   214  }
   215  
   216  func TestMatchElement(t *testing.T) {
   217  	if !nreflect.MatchElement(Addrs{}, Addrs{}, false) {
   218  		t.Fatalf("Should have matched argument types successfully")
   219  	}
   220  	t.Logf("Should have matched argument types successfully")
   221  
   222  	if !nreflect.MatchElement(new(Addrs), new(Addrs), false) {
   223  		t.Fatalf("Should have matched argument types successfully")
   224  	}
   225  	t.Logf("Should have matched argument types successfully")
   226  
   227  	if nreflect.MatchElement(new(Addrs), Addrs{}, false) {
   228  		t.Fatalf("Should have failed matched argument types successfully")
   229  	}
   230  	t.Logf("Should have failed matched argument types successfully")
   231  }
   232  
   233  func TestStructMapperWithSlice(t *testing.T) {
   234  	mapper := nreflect.NewStructMapper()
   235  
   236  	profile := struct {
   237  		List []Addrs
   238  	}{
   239  		List: []Addrs{{Addr: "Tokura 20"}},
   240  	}
   241  
   242  	mapped, err := mapper.MapFrom("json", profile)
   243  	if err != nil {
   244  		t.Fatalf("Should have successfully converted struct")
   245  	}
   246  	t.Logf("Should have successfully converted struct")
   247  
   248  	t.Logf("Map of Struct: %+q", mapped)
   249  
   250  	profile2 := struct {
   251  		List []Addrs
   252  	}{}
   253  
   254  	if err := mapper.MapTo("json", &profile2, mapped); err != nil {
   255  		t.Fatalf("Should have successfully mapped data back to struct")
   256  	}
   257  	t.Logf("Should have successfully mapped data back to struct")
   258  
   259  	if len(profile.List) != len(profile2.List) {
   260  		t.Fatalf("Mapped struct should have same length: %d - %d ", len(profile.List), len(profile2.List))
   261  	}
   262  	t.Logf("Mapped struct should have same length: %d - %d ", len(profile.List), len(profile2.List))
   263  
   264  	for ind, item := range profile.List {
   265  		nxItem := profile2.List[ind]
   266  		if item.Addr != nxItem.Addr {
   267  			t.Fatalf("Item at %d should have equal value %+q -> %+q", ind, item.Addr, nxItem.Addr)
   268  		}
   269  	}
   270  
   271  	t.Logf("All items should be exactly the same")
   272  }
   273  
   274  func TestStructMapperWthFieldStruct(t *testing.T) {
   275  	layout := "Mon Jan 2 2006 15:04:05 -0700 MST"
   276  	timeType := reflect.TypeOf((*time.Time)(nil))
   277  
   278  	mapper := nreflect.NewStructMapper()
   279  	mapper.AddAdapter(timeType, nreflect.TimeMapper(layout))
   280  	mapper.AddInverseAdapter(timeType, nreflect.TimeInverseMapper(layout))
   281  
   282  	profile := struct {
   283  		Addr Addrs
   284  		Name string    `json:"name"`
   285  		Date time.Time `json:"date"`
   286  	}{
   287  		Addr: Addrs{Addr: "Tokura 20"},
   288  		Name: "Johnson",
   289  		Date: time.Now(),
   290  	}
   291  
   292  	mapped, err := mapper.MapFrom("json", profile)
   293  	if err != nil {
   294  		t.Fatalf("Should have successfully converted struct")
   295  	}
   296  	t.Logf("Should have successfully converted struct")
   297  
   298  	t.Logf("Map of Struct: %+q", mapped)
   299  
   300  	profile2 := struct {
   301  		Addr Addrs
   302  		Name string    `json:"name"`
   303  		Date time.Time `json:"date"`
   304  	}{}
   305  
   306  	if err := mapper.MapTo("json", &profile2, mapped); err != nil {
   307  		t.Fatalf("Should have successfully mapped data back to struct")
   308  	}
   309  	t.Logf("Should have successfully mapped data back to struct")
   310  
   311  	if profile2.Addr.Addr != profile.Addr.Addr {
   312  		t.Fatalf("Mapped struct should have same %q value", "Addr.Addr")
   313  	}
   314  	t.Logf("Mapped struct should have same %q value", "Addr.Addr")
   315  }
   316  
   317  func TestGetFieldByTagAndValue(t *testing.T) {
   318  	profile := struct {
   319  		Addrs
   320  		Name string    `json:"name"`
   321  		Date time.Time `json:"date"`
   322  	}{
   323  		Addrs: Addrs{Addr: "Tokura 20"},
   324  		Name:  "Johnson",
   325  		Date:  time.Now(),
   326  	}
   327  
   328  	_, err := nreflect.GetFieldByTagAndValue(profile, "json", "name")
   329  	if err != nil {
   330  		t.Fatalf("Should have successfully converted struct")
   331  	}
   332  }
   333  
   334  func TestStructMapperWthEmbeddedStruct(t *testing.T) {
   335  	layout := "Mon Jan 2 2006 15:04:05 -0700 MST"
   336  	timeType := reflect.TypeOf((*time.Time)(nil))
   337  
   338  	mapper := nreflect.NewStructMapper()
   339  	mapper.AddAdapter(timeType, nreflect.TimeMapper(layout))
   340  	mapper.AddInverseAdapter(timeType, nreflect.TimeInverseMapper(layout))
   341  
   342  	profile := struct {
   343  		Addrs
   344  		Name string    `json:"name"`
   345  		Date time.Time `json:"date"`
   346  	}{
   347  		Addrs: Addrs{Addr: "Tokura 20"},
   348  		Name:  "Johnson",
   349  		Date:  time.Now(),
   350  	}
   351  
   352  	mapped, err := mapper.MapFrom("json", profile)
   353  	if err != nil {
   354  		t.Fatalf("Should have successfully converted struct")
   355  	}
   356  	t.Logf("Should have successfully converted struct")
   357  
   358  	t.Logf("Map of Struct: %+q", mapped)
   359  
   360  	profile2 := struct {
   361  		Addrs
   362  		Name string    `json:"name"`
   363  		Date time.Time `json:"date"`
   364  	}{}
   365  
   366  	if err := mapper.MapTo("json", &profile2, mapped); err != nil {
   367  		t.Fatalf("Should have successfully mapped data back to struct")
   368  	}
   369  	t.Logf("Should have successfully mapped data back to struct")
   370  
   371  	if profile2.Addr != profile.Addr {
   372  		t.Fatalf("Mapped struct should have same %q value", "Addr.Addr")
   373  	}
   374  	t.Logf("Mapped struct should have same %q value", "Addr.Addr")
   375  }
   376  
   377  func TestStructMapper(t *testing.T) {
   378  	layout := "Mon Jan 2 2006 15:04:05 -0700 MST"
   379  	timeType := reflect.TypeOf((*time.Time)(nil))
   380  
   381  	mapper := nreflect.NewStructMapper()
   382  	mapper.AddAdapter(timeType, nreflect.TimeMapper(layout))
   383  	mapper.AddInverseAdapter(timeType, nreflect.TimeInverseMapper(layout))
   384  
   385  	profile := struct {
   386  		Addr        string
   387  		CountryName string
   388  		Name        string    `json:"name"`
   389  		Date        time.Time `json:"date"`
   390  	}{
   391  		Addr:        "Tokura 20",
   392  		Name:        "Johnson",
   393  		CountryName: "Nigeria",
   394  		Date:        time.Now(),
   395  	}
   396  
   397  	mapped, err := mapper.MapFrom("json", profile)
   398  	if err != nil {
   399  		t.Fatalf("Should have successfully converted struct")
   400  	}
   401  	t.Logf("Should have successfully converted struct")
   402  
   403  	t.Logf("Map of Struct: %+q", mapped)
   404  
   405  	if _, ok := mapped["name"]; !ok {
   406  		t.Fatalf("Map should have %q field", "name")
   407  	}
   408  	t.Logf("Map should have %q field", "name")
   409  
   410  	if _, ok := mapped["date"]; !ok {
   411  		t.Fatalf("Map should have %q field", "date")
   412  	}
   413  	t.Logf("Map should have %q field", "date")
   414  
   415  	if _, ok := mapped["addr"]; !ok {
   416  		t.Fatalf("Map should have %q field", "addr")
   417  	}
   418  	t.Logf("Map should have %q field", "addr")
   419  
   420  	if _, ok := mapped["data"].(string); ok {
   421  		t.Fatalf("Map should have field %q be a string", "date")
   422  	}
   423  	t.Logf("Map should have field %q be a string", "date")
   424  
   425  	profile2 := struct {
   426  		Addr        string
   427  		CountryName string
   428  		Name        string    `json:"name"`
   429  		Date        time.Time `json:"date"`
   430  	}{}
   431  
   432  	if err := mapper.MapTo("json", &profile2, mapped); err != nil {
   433  		t.Fatalf("Should have successfully mapped data back to struct")
   434  	}
   435  	t.Logf("Should have successfully mapped data back to struct")
   436  
   437  	t.Logf("Mapped Struct: %+q", profile2)
   438  
   439  	if profile2.Name != profile.Name {
   440  		t.Fatalf("Mapped struct should have same %q value", "PageName")
   441  	}
   442  	t.Logf("Mapped struct should have same %q value", "PageName")
   443  
   444  	if profile2.Date.Format(layout) != profile.Date.Format(layout) {
   445  		t.Fatalf("Mapped struct should have same %q value", "Date")
   446  	}
   447  	t.Logf("Mapped struct should have same %q value", "Date")
   448  
   449  	if profile2.CountryName != profile.CountryName {
   450  		t.Fatalf("Mapped struct should have same %q value", "CountryName")
   451  	}
   452  	t.Logf("Mapped struct should have same %q value", "CountryName")
   453  
   454  	if profile2.Addr != profile.Addr {
   455  		t.Fatalf("Mapped struct should have same %q value", "Addr")
   456  	}
   457  	t.Logf("Mapped struct should have same %q value", "Addr")
   458  }
   459  
   460  // TestGetArgumentsType validates nreflect API GetArgumentsType functions
   461  // results.
   462  func TestGetArgumentsType(t *testing.T) {
   463  	f := func(m monster) string {
   464  		return fmt.Sprintf("Monster[%s] is ready!", m.Name)
   465  	}
   466  
   467  	args, err := nreflect.GetFuncArgumentsType(f)
   468  	if err != nil {
   469  		t.Fatalf("Should be able to retrieve function arguments lists")
   470  	}
   471  	t.Logf("Should be able to retrieve function arguments lists")
   472  
   473  	name, embedded, err := nreflect.ExternalTypeNames(monster{Name: "Bob"})
   474  	if err != nil {
   475  		t.Fatalf("Should be able to retrieve field names arguments lists")
   476  	}
   477  	t.Logf("PageName: %s", name)
   478  	t.Logf("Fields: %+q", embedded)
   479  	t.Logf("Should be able to retrieve function arguments lists")
   480  
   481  	get(t, &monster{Name: "Bob"})
   482  
   483  	newVals := nreflect.MakeArgumentsValues(args)
   484  	if nlen, alen := len(newVals), len(args); nlen != alen {
   485  		t.Fatalf("Should have matching new values lists for arguments")
   486  	}
   487  	t.Logf("Should have matching new values lists for arguments")
   488  
   489  	mstring := reflect.TypeOf((*monster)(nil)).Elem()
   490  
   491  	if mstring.Kind() != newVals[0].Kind() {
   492  		t.Fatalf("Should be able to match argument kind")
   493  	}
   494  	t.Logf("Should be able to match argument kind")
   495  
   496  }
   497  
   498  func TestMatchFUncArgumentTypeWithValues(t *testing.T) {
   499  	f := func(m monster) string {
   500  		return fmt.Sprintf("Monster[%s] is ready!", m.Name)
   501  	}
   502  
   503  	var vals []reflect.Value
   504  	vals = append(vals, reflect.ValueOf(monster{Name: "FireHouse"}))
   505  
   506  	if index := nreflect.MatchFuncArgumentTypeWithValues(f, vals); index != -1 {
   507  		t.Fatalf("Should have matching new values lists for arguments: %d", index)
   508  	}
   509  	t.Logf("Should have matching new values lists for arguments")
   510  }