1
0
bind/scalar_test.go

262 lines
5.3 KiB
Go
Raw Normal View History

2025-08-27 21:03:25 +02:00
package bind_test
import (
"code.squareroundforest.org/arpio/bind"
2025-09-04 00:33:05 +02:00
"reflect"
2025-08-27 21:03:25 +02:00
"slices"
"testing"
)
type valuer interface {
value() int
}
type counter interface {
count() int
}
type countedSlice []int
type valueInt int
func (s countedSlice) count() int {
return len(s)
}
func (i *valueInt) value() int {
return int(*i)
}
func TestScalar(t *testing.T) {
t.Run("bind scalar", func(t *testing.T) {
t.Run("no value", func(t *testing.T) {
var i int
if bind.BindScalar(&i) {
t.Fatal()
}
})
t.Run("not scalar", func(t *testing.T) {
var s struct{ Foo int }
if bind.BindScalar(&s, "42") {
t.Fatal()
}
})
t.Run("cannot scan", func(t *testing.T) {
var i int
if bind.BindScalar(&i, "foo") {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("receiver cannot be set", func(t *testing.T) {
var i int
if bind.BindScalar(i, "42") {
t.Fatal()
}
})
t.Run("one value set", func(t *testing.T) {
var i int
if !bind.BindScalar(&i, "42") || i != 42 {
t.Fatal(i)
}
})
t.Run("multiple values, not slice", func(t *testing.T) {
var i int
if bind.BindScalar(&i, "21", "42", "84") {
t.Fatal()
}
})
t.Run("multiple values, small slice", func(t *testing.T) {
s := make([]int, 2)
if bind.BindScalar(&s, "21", "42", "84") {
t.Fatal()
}
})
t.Run("multiple values set", func(t *testing.T) {
s := make([]int, 3)
if !bind.BindScalar(&s, "21", "42", "84") || !slices.Equal(s, []int{21, 42, 84}) {
t.Fatal()
}
})
t.Run("multiple values set, larger slice", func(t *testing.T) {
s := make([]int, 4)
if !bind.BindScalar(&s, "21", "42", "84") || !slices.Equal(s, []int{21, 42, 84, 0}) {
t.Fatal()
}
})
t.Run("multiple values, cannot scan", func(t *testing.T) {
s := make([]int, 3)
if bind.BindScalar(&s, "21", "42", "foo") {
t.Fatal()
}
})
t.Run("set item of a slice", func(t *testing.T) {
s := []int{21, 42, 84}
if !bind.BindScalar(&s, "27") || !slices.Equal(s, []int{27, 42, 84}) {
t.Fatal(s)
}
})
t.Run("interface receiver", func(t *testing.T) {
var i any
if !bind.BindScalar(&i, 42) || i != 42 {
t.Fatal(i)
}
})
t.Run("interface receiver has value", func(t *testing.T) {
var (
i int
iface any
)
i = 21
iface = &i
if ok := bind.BindScalar(&iface, "42"); !ok || iface != "42" || i != 21 {
t.Fatal(ok, iface)
}
})
t.Run("wrapped with non-empty interface", func(t *testing.T) {
// this could work in theory, but fails on checking the type of the receiver
// we leave the test undefiend, and checking only for no panic
var iface valuer
i := valueInt(21)
p := &i
iface = p
ok := bind.BindScalar(&iface, "42")
if ok != (i == 42) {
t.Fatal()
}
})
t.Run("slice wrapped by empty interface", func(t *testing.T) {
var iface any
s := make([]int, 3)
iface = s
if ok := bind.BindScalar(&iface, "21", "42", "84"); !ok || !slices.Equal(s, []int{21, 42, 84}) {
t.Fatal()
}
})
2025-08-28 05:04:06 +02:00
t.Run("value has circular reference", func(t *testing.T) {
var r any
var v any
p := &v
*p = p
if bind.BindScalar(&r, p) {
t.Fatal()
}
})
2025-08-31 17:11:09 +02:00
t.Run("nil value", func(t *testing.T) {
var v any
v = 42
if !bind.BindScalar(&v, nil) || v != nil {
t.Fatal()
}
})
2025-09-03 23:52:33 +02:00
t.Run("nil value and non-nillable receiver", func(t *testing.T) {
var v int
if bind.BindScalar(&v, nil) {
t.Fatal()
}
})
2025-08-31 17:11:09 +02:00
t.Run("nil receiver", func(t *testing.T) {
if bind.BindScalar(nil, 42) {
t.Fatal()
}
})
2025-08-27 21:03:25 +02:00
})
t.Run("bind scalar with create", func(t *testing.T) {
t.Run("no value", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if _, ok := bind.CreateAndBindScalar[int](); ok {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("does not accept scalar", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if _, ok := bind.CreateAndBindScalar[struct{ Foo int }]("42"); ok {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("empty interface", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if v, ok := bind.CreateAndBindScalar[any]("42"); !ok || v != "42" {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("scalar", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if v, ok := bind.CreateAndBindScalar[int]("42"); !ok || v != 42 {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("slice", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if v, ok := bind.CreateAndBindScalar[[]int]("21", "42", "84"); !ok || !slices.Equal(v, []int{21, 42, 84}) {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("slice with non-scalar type", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if _, ok := bind.CreateAndBindScalar[[]func(int)]("21", "42", "84"); ok {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("pointer to non-scalar", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if _, ok := bind.CreateAndBindScalar[*func()]("42"); ok {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("pointer", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if v, ok := bind.CreateAndBindScalar[*int]("42"); !ok || *v != 42 {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
t.Run("unscannable", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
if _, ok := bind.CreateAndBindScalar[int]("foo"); ok {
2025-08-27 21:03:25 +02:00
t.Fatal()
}
})
2025-08-28 05:04:06 +02:00
t.Run("receiver has circular type", func(t *testing.T) {
type s []s
2025-09-04 00:33:05 +02:00
if _, ok := bind.CreateAndBindScalar[s]("foo"); ok {
2025-08-28 05:04:06 +02:00
t.Fatal()
}
})
2025-08-31 01:23:21 +02:00
2025-08-28 05:04:06 +02:00
t.Run("value has circular reference", func(t *testing.T) {
var v any
p := &v
*p = p
2025-09-04 00:33:05 +02:00
if _, ok := bind.CreateAndBindScalar[any](p); ok {
2025-08-28 05:04:06 +02:00
t.Fatal()
}
})
2025-08-27 21:03:25 +02:00
})
2025-09-04 00:33:05 +02:00
t.Run("bind with reflection type", func(t *testing.T) {
v, ok := bind.CreateAndBindScalarFor(reflect.TypeFor[int](), 42)
if !ok || v.Interface() != 42 {
t.Fatal()
}
})
2025-08-27 21:03:25 +02:00
}