...

Source file src/github.com/redis/go-redis/v9/internal/hscan/hscan_test.go

Documentation: github.com/redis/go-redis/v9/internal/hscan

     1  package hscan
     2  
     3  import (
     4  	"math"
     5  	"strconv"
     6  	"testing"
     7  	"time"
     8  
     9  	. "github.com/bsm/ginkgo/v2"
    10  	. "github.com/bsm/gomega"
    11  
    12  	"github.com/redis/go-redis/v9/internal/util"
    13  )
    14  
    15  type data struct {
    16  	Omit  string `redis:"-"`
    17  	Empty string
    18  
    19  	String  string  `redis:"string"`
    20  	Bytes   []byte  `redis:"byte"`
    21  	Int     int     `redis:"int"`
    22  	Int8    int8    `redis:"int8"`
    23  	Int16   int16   `redis:"int16"`
    24  	Int32   int32   `redis:"int32"`
    25  	Int64   int64   `redis:"int64"`
    26  	Uint    uint    `redis:"uint"`
    27  	Uint8   uint8   `redis:"uint8"`
    28  	Uint16  uint16  `redis:"uint16"`
    29  	Uint32  uint32  `redis:"uint32"`
    30  	Uint64  uint64  `redis:"uint64"`
    31  	Float   float32 `redis:"float"`
    32  	Float64 float64 `redis:"float64"`
    33  	Bool    bool    `redis:"bool"`
    34  	BoolRef *bool   `redis:"boolRef"`
    35  }
    36  
    37  type TimeRFC3339Nano struct {
    38  	time.Time
    39  }
    40  
    41  func (t *TimeRFC3339Nano) ScanRedis(s string) (err error) {
    42  	t.Time, err = time.Parse(time.RFC3339Nano, s)
    43  	return
    44  }
    45  
    46  type TimeData struct {
    47  	Name string           `redis:"name"`
    48  	Time *TimeRFC3339Nano `redis:"login"`
    49  }
    50  
    51  type i []interface{}
    52  
    53  func TestGinkgoSuite(t *testing.T) {
    54  	RegisterFailHandler(Fail)
    55  	RunSpecs(t, "hscan")
    56  }
    57  
    58  var _ = Describe("Scan", func() {
    59  	It("catches bad args", func() {
    60  		var d data
    61  
    62  		Expect(Scan(&d, i{}, i{})).NotTo(HaveOccurred())
    63  		Expect(d).To(Equal(data{}))
    64  
    65  		Expect(Scan(&d, i{"key"}, i{})).To(HaveOccurred())
    66  		Expect(Scan(&d, i{"key"}, i{"1", "2"})).To(HaveOccurred())
    67  		Expect(Scan(nil, i{"key", "1"}, i{})).To(HaveOccurred())
    68  
    69  		var m map[string]interface{}
    70  		Expect(Scan(&m, i{"key"}, i{"1"})).To(HaveOccurred())
    71  		Expect(Scan(data{}, i{"key"}, i{"1"})).To(HaveOccurred())
    72  		Expect(Scan(data{}, i{"key", "string"}, i{nil, nil})).To(HaveOccurred())
    73  	})
    74  
    75  	It("number out of range", func() {
    76  		f := func(v uint64) string {
    77  			return strconv.FormatUint(v, 10) + "1"
    78  		}
    79  		keys := i{"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "float64"}
    80  		vals := i{
    81  			f(math.MaxInt8), f(math.MaxInt16), f(math.MaxInt32), f(math.MaxInt64),
    82  			f(math.MaxUint8), f(math.MaxUint16), f(math.MaxUint32), strconv.FormatUint(math.MaxUint64, 10) + "1",
    83  			"13.4028234663852886e+38", "11.79769313486231570e+308",
    84  		}
    85  		for k, v := range keys {
    86  			var d data
    87  			Expect(Scan(&d, i{v}, i{vals[k]})).To(HaveOccurred())
    88  		}
    89  
    90  		// success
    91  		f = func(v uint64) string {
    92  			return strconv.FormatUint(v, 10)
    93  		}
    94  		keys = i{"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "float64"}
    95  		vals = i{
    96  			f(math.MaxInt8), f(math.MaxInt16), f(math.MaxInt32), f(math.MaxInt64),
    97  			f(math.MaxUint8), f(math.MaxUint16), f(math.MaxUint32), strconv.FormatUint(math.MaxUint64, 10),
    98  			"3.40282346638528859811704183484516925440e+38", "1.797693134862315708145274237317043567981e+308",
    99  		}
   100  		var d data
   101  		Expect(Scan(&d, keys, vals)).NotTo(HaveOccurred())
   102  		Expect(d).To(Equal(data{
   103  			Int8:    math.MaxInt8,
   104  			Int16:   math.MaxInt16,
   105  			Int32:   math.MaxInt32,
   106  			Int64:   math.MaxInt64,
   107  			Uint8:   math.MaxUint8,
   108  			Uint16:  math.MaxUint16,
   109  			Uint32:  math.MaxUint32,
   110  			Uint64:  math.MaxUint64,
   111  			Float:   math.MaxFloat32,
   112  			Float64: math.MaxFloat64,
   113  		}))
   114  	})
   115  
   116  	It("scans good values", func() {
   117  		var d data
   118  
   119  		// non-tagged fields.
   120  		Expect(Scan(&d, i{"key"}, i{"value"})).NotTo(HaveOccurred())
   121  		Expect(d).To(Equal(data{}))
   122  
   123  		keys := i{"string", "byte", "int", "int64", "uint", "uint64", "float", "float64", "bool", "boolRef"}
   124  		vals := i{
   125  			"str!", "bytes!", "123", "123456789123456789", "456", "987654321987654321",
   126  			"123.456", "123456789123456789.987654321987654321", "1", "1",
   127  		}
   128  		Expect(Scan(&d, keys, vals)).NotTo(HaveOccurred())
   129  		Expect(d).To(Equal(data{
   130  			String:  "str!",
   131  			Bytes:   []byte("bytes!"),
   132  			Int:     123,
   133  			Int64:   123456789123456789,
   134  			Uint:    456,
   135  			Uint64:  987654321987654321,
   136  			Float:   123.456,
   137  			Float64: 1.2345678912345678e+17,
   138  			Bool:    true,
   139  			BoolRef: util.ToPtr(true),
   140  		}))
   141  
   142  		// Scan a different type with the same values to test that
   143  		// the struct spec maps don't conflict.
   144  		type data2 struct {
   145  			String string  `redis:"string"`
   146  			Bytes  []byte  `redis:"byte"`
   147  			Int    int     `redis:"int"`
   148  			Uint   uint    `redis:"uint"`
   149  			Float  float32 `redis:"float"`
   150  			Bool   bool    `redis:"bool"`
   151  		}
   152  		var d2 data2
   153  		Expect(Scan(&d2, keys, vals)).NotTo(HaveOccurred())
   154  		Expect(d2).To(Equal(data2{
   155  			String: "str!",
   156  			Bytes:  []byte("bytes!"),
   157  			Int:    123,
   158  			Uint:   456,
   159  			Float:  123.456,
   160  			Bool:   true,
   161  		}))
   162  
   163  		Expect(Scan(&d, i{"string", "float", "bool"}, i{"", "1", "t"})).NotTo(HaveOccurred())
   164  		Expect(d).To(Equal(data{
   165  			String:  "",
   166  			Bytes:   []byte("bytes!"),
   167  			Int:     123,
   168  			Int64:   123456789123456789,
   169  			Uint:    456,
   170  			Uint64:  987654321987654321,
   171  			Float:   1.0,
   172  			Float64: 1.2345678912345678e+17,
   173  			Bool:    true,
   174  			BoolRef: util.ToPtr(true),
   175  		}))
   176  	})
   177  
   178  	It("omits untagged fields", func() {
   179  		var d data
   180  
   181  		Expect(Scan(&d, i{"empty", "omit", "string"}, i{"value", "value", "str!"})).NotTo(HaveOccurred())
   182  		Expect(d).To(Equal(data{
   183  			String: "str!",
   184  		}))
   185  	})
   186  
   187  	It("catches bad values", func() {
   188  		var d data
   189  
   190  		Expect(Scan(&d, i{"int"}, i{"a"})).To(HaveOccurred())
   191  		Expect(Scan(&d, i{"uint"}, i{"a"})).To(HaveOccurred())
   192  		Expect(Scan(&d, i{"uint"}, i{""})).To(HaveOccurred())
   193  		Expect(Scan(&d, i{"float"}, i{"b"})).To(HaveOccurred())
   194  		Expect(Scan(&d, i{"bool"}, i{"-1"})).To(HaveOccurred())
   195  		Expect(Scan(&d, i{"bool"}, i{""})).To(HaveOccurred())
   196  		Expect(Scan(&d, i{"bool"}, i{"123"})).To(HaveOccurred())
   197  	})
   198  
   199  	It("Implements Scanner", func() {
   200  		var td TimeData
   201  
   202  		now := time.Now()
   203  		Expect(Scan(&td, i{"name", "login"}, i{"hello", now.Format(time.RFC3339Nano)})).NotTo(HaveOccurred())
   204  		Expect(td.Name).To(Equal("hello"))
   205  		Expect(td.Time.UnixNano()).To(Equal(now.UnixNano()))
   206  		Expect(td.Time.Format(time.RFC3339Nano)).To(Equal(now.Format(time.RFC3339Nano)))
   207  	})
   208  
   209  	It("should time.Time RFC3339Nano", func() {
   210  		type TimeTime struct {
   211  			Time time.Time `redis:"time"`
   212  		}
   213  
   214  		now := time.Now()
   215  
   216  		var tt TimeTime
   217  		Expect(Scan(&tt, i{"time"}, i{now.Format(time.RFC3339Nano)})).NotTo(HaveOccurred())
   218  		Expect(now.Unix()).To(Equal(tt.Time.Unix()))
   219  	})
   220  })
   221  

View as plain text