mirror of https://github.com/go-redis/redis.git
221 lines
6.0 KiB
Go
221 lines
6.0 KiB
Go
|
package hscan
|
||
|
|
||
|
import (
|
||
|
"math"
|
||
|
"strconv"
|
||
|
"testing"
|
||
|
"time"
|
||
|
|
||
|
. "github.com/bsm/ginkgo/v2"
|
||
|
. "github.com/bsm/gomega"
|
||
|
|
||
|
"github.com/redis/go-redis/v9/internal/util"
|
||
|
)
|
||
|
|
||
|
type data struct {
|
||
|
Omit string `redis:"-"`
|
||
|
Empty string
|
||
|
|
||
|
String string `redis:"string"`
|
||
|
Bytes []byte `redis:"byte"`
|
||
|
Int int `redis:"int"`
|
||
|
Int8 int8 `redis:"int8"`
|
||
|
Int16 int16 `redis:"int16"`
|
||
|
Int32 int32 `redis:"int32"`
|
||
|
Int64 int64 `redis:"int64"`
|
||
|
Uint uint `redis:"uint"`
|
||
|
Uint8 uint8 `redis:"uint8"`
|
||
|
Uint16 uint16 `redis:"uint16"`
|
||
|
Uint32 uint32 `redis:"uint32"`
|
||
|
Uint64 uint64 `redis:"uint64"`
|
||
|
Float float32 `redis:"float"`
|
||
|
Float64 float64 `redis:"float64"`
|
||
|
Bool bool `redis:"bool"`
|
||
|
BoolRef *bool `redis:"boolRef"`
|
||
|
}
|
||
|
|
||
|
type TimeRFC3339Nano struct {
|
||
|
time.Time
|
||
|
}
|
||
|
|
||
|
func (t *TimeRFC3339Nano) ScanRedis(s string) (err error) {
|
||
|
t.Time, err = time.Parse(time.RFC3339Nano, s)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
type TimeData struct {
|
||
|
Name string `redis:"name"`
|
||
|
Time *TimeRFC3339Nano `redis:"login"`
|
||
|
}
|
||
|
|
||
|
type i []interface{}
|
||
|
|
||
|
func TestGinkgoSuite(t *testing.T) {
|
||
|
RegisterFailHandler(Fail)
|
||
|
RunSpecs(t, "hscan")
|
||
|
}
|
||
|
|
||
|
var _ = Describe("Scan", func() {
|
||
|
It("catches bad args", func() {
|
||
|
var d data
|
||
|
|
||
|
Expect(Scan(&d, i{}, i{})).NotTo(HaveOccurred())
|
||
|
Expect(d).To(Equal(data{}))
|
||
|
|
||
|
Expect(Scan(&d, i{"key"}, i{})).To(HaveOccurred())
|
||
|
Expect(Scan(&d, i{"key"}, i{"1", "2"})).To(HaveOccurred())
|
||
|
Expect(Scan(nil, i{"key", "1"}, i{})).To(HaveOccurred())
|
||
|
|
||
|
var m map[string]interface{}
|
||
|
Expect(Scan(&m, i{"key"}, i{"1"})).To(HaveOccurred())
|
||
|
Expect(Scan(data{}, i{"key"}, i{"1"})).To(HaveOccurred())
|
||
|
Expect(Scan(data{}, i{"key", "string"}, i{nil, nil})).To(HaveOccurred())
|
||
|
})
|
||
|
|
||
|
It("number out of range", func() {
|
||
|
f := func(v uint64) string {
|
||
|
return strconv.FormatUint(v, 10) + "1"
|
||
|
}
|
||
|
keys := i{"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "float64"}
|
||
|
vals := i{
|
||
|
f(math.MaxInt8), f(math.MaxInt16), f(math.MaxInt32), f(math.MaxInt64),
|
||
|
f(math.MaxUint8), f(math.MaxUint16), f(math.MaxUint32), strconv.FormatUint(math.MaxUint64, 10) + "1",
|
||
|
"13.4028234663852886e+38", "11.79769313486231570e+308",
|
||
|
}
|
||
|
for k, v := range keys {
|
||
|
var d data
|
||
|
Expect(Scan(&d, i{v}, i{vals[k]})).To(HaveOccurred())
|
||
|
}
|
||
|
|
||
|
// success
|
||
|
f = func(v uint64) string {
|
||
|
return strconv.FormatUint(v, 10)
|
||
|
}
|
||
|
keys = i{"int8", "int16", "int32", "int64", "uint8", "uint16", "uint32", "uint64", "float", "float64"}
|
||
|
vals = i{
|
||
|
f(math.MaxInt8), f(math.MaxInt16), f(math.MaxInt32), f(math.MaxInt64),
|
||
|
f(math.MaxUint8), f(math.MaxUint16), f(math.MaxUint32), strconv.FormatUint(math.MaxUint64, 10),
|
||
|
"3.40282346638528859811704183484516925440e+38", "1.797693134862315708145274237317043567981e+308",
|
||
|
}
|
||
|
var d data
|
||
|
Expect(Scan(&d, keys, vals)).NotTo(HaveOccurred())
|
||
|
Expect(d).To(Equal(data{
|
||
|
Int8: math.MaxInt8,
|
||
|
Int16: math.MaxInt16,
|
||
|
Int32: math.MaxInt32,
|
||
|
Int64: math.MaxInt64,
|
||
|
Uint8: math.MaxUint8,
|
||
|
Uint16: math.MaxUint16,
|
||
|
Uint32: math.MaxUint32,
|
||
|
Uint64: math.MaxUint64,
|
||
|
Float: math.MaxFloat32,
|
||
|
Float64: math.MaxFloat64,
|
||
|
}))
|
||
|
})
|
||
|
|
||
|
It("scans good values", func() {
|
||
|
var d data
|
||
|
|
||
|
// non-tagged fields.
|
||
|
Expect(Scan(&d, i{"key"}, i{"value"})).NotTo(HaveOccurred())
|
||
|
Expect(d).To(Equal(data{}))
|
||
|
|
||
|
keys := i{"string", "byte", "int", "int64", "uint", "uint64", "float", "float64", "bool", "boolRef"}
|
||
|
vals := i{
|
||
|
"str!", "bytes!", "123", "123456789123456789", "456", "987654321987654321",
|
||
|
"123.456", "123456789123456789.987654321987654321", "1", "1",
|
||
|
}
|
||
|
Expect(Scan(&d, keys, vals)).NotTo(HaveOccurred())
|
||
|
Expect(d).To(Equal(data{
|
||
|
String: "str!",
|
||
|
Bytes: []byte("bytes!"),
|
||
|
Int: 123,
|
||
|
Int64: 123456789123456789,
|
||
|
Uint: 456,
|
||
|
Uint64: 987654321987654321,
|
||
|
Float: 123.456,
|
||
|
Float64: 1.2345678912345678e+17,
|
||
|
Bool: true,
|
||
|
BoolRef: util.ToPtr(true),
|
||
|
}))
|
||
|
|
||
|
// Scan a different type with the same values to test that
|
||
|
// the struct spec maps don't conflict.
|
||
|
type data2 struct {
|
||
|
String string `redis:"string"`
|
||
|
Bytes []byte `redis:"byte"`
|
||
|
Int int `redis:"int"`
|
||
|
Uint uint `redis:"uint"`
|
||
|
Float float32 `redis:"float"`
|
||
|
Bool bool `redis:"bool"`
|
||
|
}
|
||
|
var d2 data2
|
||
|
Expect(Scan(&d2, keys, vals)).NotTo(HaveOccurred())
|
||
|
Expect(d2).To(Equal(data2{
|
||
|
String: "str!",
|
||
|
Bytes: []byte("bytes!"),
|
||
|
Int: 123,
|
||
|
Uint: 456,
|
||
|
Float: 123.456,
|
||
|
Bool: true,
|
||
|
}))
|
||
|
|
||
|
Expect(Scan(&d, i{"string", "float", "bool"}, i{"", "1", "t"})).NotTo(HaveOccurred())
|
||
|
Expect(d).To(Equal(data{
|
||
|
String: "",
|
||
|
Bytes: []byte("bytes!"),
|
||
|
Int: 123,
|
||
|
Int64: 123456789123456789,
|
||
|
Uint: 456,
|
||
|
Uint64: 987654321987654321,
|
||
|
Float: 1.0,
|
||
|
Float64: 1.2345678912345678e+17,
|
||
|
Bool: true,
|
||
|
BoolRef: util.ToPtr(true),
|
||
|
}))
|
||
|
})
|
||
|
|
||
|
It("omits untagged fields", func() {
|
||
|
var d data
|
||
|
|
||
|
Expect(Scan(&d, i{"empty", "omit", "string"}, i{"value", "value", "str!"})).NotTo(HaveOccurred())
|
||
|
Expect(d).To(Equal(data{
|
||
|
String: "str!",
|
||
|
}))
|
||
|
})
|
||
|
|
||
|
It("catches bad values", func() {
|
||
|
var d data
|
||
|
|
||
|
Expect(Scan(&d, i{"int"}, i{"a"})).To(HaveOccurred())
|
||
|
Expect(Scan(&d, i{"uint"}, i{"a"})).To(HaveOccurred())
|
||
|
Expect(Scan(&d, i{"uint"}, i{""})).To(HaveOccurred())
|
||
|
Expect(Scan(&d, i{"float"}, i{"b"})).To(HaveOccurred())
|
||
|
Expect(Scan(&d, i{"bool"}, i{"-1"})).To(HaveOccurred())
|
||
|
Expect(Scan(&d, i{"bool"}, i{""})).To(HaveOccurred())
|
||
|
Expect(Scan(&d, i{"bool"}, i{"123"})).To(HaveOccurred())
|
||
|
})
|
||
|
|
||
|
It("Implements Scanner", func() {
|
||
|
var td TimeData
|
||
|
|
||
|
now := time.Now()
|
||
|
Expect(Scan(&td, i{"name", "login"}, i{"hello", now.Format(time.RFC3339Nano)})).NotTo(HaveOccurred())
|
||
|
Expect(td.Name).To(Equal("hello"))
|
||
|
Expect(td.Time.UnixNano()).To(Equal(now.UnixNano()))
|
||
|
Expect(td.Time.Format(time.RFC3339Nano)).To(Equal(now.Format(time.RFC3339Nano)))
|
||
|
})
|
||
|
|
||
|
It("should time.Time RFC3339Nano", func() {
|
||
|
type TimeTime struct {
|
||
|
Time time.Time `redis:"time"`
|
||
|
}
|
||
|
|
||
|
now := time.Now()
|
||
|
|
||
|
var tt TimeTime
|
||
|
Expect(Scan(&tt, i{"time"}, i{now.Format(time.RFC3339Nano)})).NotTo(HaveOccurred())
|
||
|
Expect(now.Unix()).To(Equal(tt.Time.Unix()))
|
||
|
})
|
||
|
})
|