1
0
bind/type_test.go

452 lines
8.0 KiB
Go
Raw Permalink 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
"testing"
)
func TestTypeChecks(t *testing.T) {
t.Run("accepts scalar", func(t *testing.T) {
t.Run("bool", func(t *testing.T) {
if !bind.AcceptsScalar[bool]() {
t.Fatal()
}
})
t.Run("int", func(t *testing.T) {
if !bind.AcceptsScalar[int]() {
t.Fatal()
}
})
t.Run("uint", func(t *testing.T) {
if !bind.AcceptsScalar[uint]() {
t.Fatal()
}
})
t.Run("float", func(t *testing.T) {
if !bind.AcceptsScalar[int]() {
t.Fatal()
}
})
t.Run("string", func(t *testing.T) {
if !bind.AcceptsScalar[bool]() {
t.Fatal()
}
})
t.Run("any", func(t *testing.T) {
if !bind.AcceptsScalar[any]() {
t.Fatal()
}
})
t.Run("pointer", func(t *testing.T) {
type p *int
if !bind.AcceptsScalar[p]() {
t.Fatal()
}
})
t.Run("slice", func(t *testing.T) {
type s []int
if !bind.AcceptsScalar[s]() {
t.Fatal()
}
})
t.Run("pointer and slice combined", func(t *testing.T) {
type c *[]*[]int
if !bind.AcceptsScalar[c]() {
t.Fatal()
}
})
t.Run("struct", func(t *testing.T) {
type s struct{ Foo int }
if bind.AcceptsScalar[s]() {
t.Fatal()
}
})
t.Run("map", func(t *testing.T) {
if bind.AcceptsScalar[map[string]int]() {
t.Fatal()
}
})
t.Run("interface with methods", func(t *testing.T) {
type i interface{ Foo(int) }
if bind.AcceptsScalar[i]() {
t.Fatal()
}
})
t.Run("func", func(t *testing.T) {
if bind.AcceptsScalar[func(int)]() {
t.Fatal()
}
})
t.Run("chan", func(t *testing.T) {
if bind.AcceptsScalar[chan int]() {
t.Fatal()
}
})
})
t.Run("accepts fields", func(t *testing.T) {
t.Run("struct", func(t *testing.T) {
type s struct{ Foo int }
if !bind.AcceptsFields[s]() {
t.Fatal()
}
})
t.Run("map", func(t *testing.T) {
if !bind.AcceptsFields[map[string]int]() {
t.Fatal()
}
})
t.Run("map with interface fields", func(t *testing.T) {
if !bind.AcceptsFields[map[string]any]() {
t.Fatal()
}
})
t.Run("map with list fields", func(t *testing.T) {
if !bind.AcceptsFields[map[string][]int]() {
t.Fatal()
}
})
t.Run("pointer", func(t *testing.T) {
type p *struct{ Foo int }
if !bind.AcceptsFields[p]() {
t.Fatal()
}
})
t.Run("slice", func(t *testing.T) {
type s []struct{ Foo int }
if !bind.AcceptsFields[s]() {
t.Fatal()
}
})
t.Run("pointer and slice combined", func(t *testing.T) {
type c *[]*[]struct{ Foo int }
if !bind.AcceptsFields[c]() {
t.Fatal()
}
})
t.Run("wrong map key", func(t *testing.T) {
if bind.AcceptsFields[map[int]int]() {
t.Fatal()
}
})
t.Run("wrong map value", func(t *testing.T) {
if bind.AcceptsFields[map[string]struct{ Foo int }]() {
t.Fatal()
}
})
t.Run("scalar", func(t *testing.T) {
if bind.AcceptsFields[int]() {
t.Fatal()
}
})
t.Run("interface", func(t *testing.T) {
type i interface{ Foo(int) }
if bind.AcceptsFields[i]() {
t.Fatal()
}
})
t.Run("any type", func(t *testing.T) {
if bind.AcceptsFields[any]() {
t.Fatal()
}
})
t.Run("func", func(t *testing.T) {
if bind.AcceptsFields[func(int)]() {
t.Fatal()
}
})
t.Run("chan", func(t *testing.T) {
if bind.AcceptsFields[chan int]() {
t.Fatal()
}
})
})
t.Run("accepts list", func(t *testing.T) {
t.Run("scalars", func(t *testing.T) {
if !bind.AcceptsList[[]int]() {
t.Fatal()
}
})
t.Run("structs", func(t *testing.T) {
if !bind.AcceptsList[[]struct{ Foo int }]() {
t.Fatal()
}
})
t.Run("maps", func(t *testing.T) {
if !bind.AcceptsList[[]map[string]int]() {
t.Fatal()
}
})
t.Run("pointer to list", func(t *testing.T) {
if !bind.AcceptsList[*[]int]() {
t.Fatal()
}
})
t.Run("scalar", func(t *testing.T) {
if bind.AcceptsList[int]() {
t.Fatal()
}
})
t.Run("struct", func(t *testing.T) {
if bind.AcceptsList[struct{ Foo int }]() {
t.Fatal()
}
})
t.Run("map", func(t *testing.T) {
if bind.AcceptsList[map[string]int]() {
t.Fatal()
}
})
t.Run("map", func(t *testing.T) {
if bind.AcceptsList[map[string]int]() {
t.Fatal()
}
})
t.Run("struct pointer", func(t *testing.T) {
if bind.AcceptsList[*struct{ Foo int }]() {
t.Fatal()
}
})
t.Run("wrong elem type", func(t *testing.T) {
if bind.AcceptsList[map[string]struct{ Foo int }]() {
t.Fatal()
}
})
})
t.Run("bindable", func(t *testing.T) {
t.Run("scalar", func(t *testing.T) {
if !bind.Bindable[int]() {
t.Fatal()
}
})
t.Run("struct", func(t *testing.T) {
if !bind.Bindable[struct{ Foo int }]() {
t.Fatal()
}
})
t.Run("func", func(t *testing.T) {
if bind.Bindable[func()]() {
t.Fatal()
}
})
t.Run("chan", func(t *testing.T) {
if bind.Bindable[chan int]() {
t.Fatal()
}
})
2025-09-04 00:33:05 +02:00
t.Run("with reflection type", func(t *testing.T) {
if !bind.BindableType(reflect.TypeFor[int]()) {
t.Fatal()
}
})
2025-08-27 21:03:25 +02:00
})
2025-08-28 05:04:06 +02:00
t.Run("circular type", func(t *testing.T) {
t.Run("via pointer", func(t *testing.T) {
type p *p
if bind.Bindable[p]() {
t.Fatal()
}
})
t.Run("via slice", func(t *testing.T) {
type s []s
if bind.Bindable[s]() {
t.Fatal()
}
})
t.Run("pointer via struct field", func(t *testing.T) {
type s struct{ f *s }
if bind.Bindable[s]() {
t.Fatal()
}
})
t.Run("slice via struct field", func(t *testing.T) {
type s struct{ f []s }
if bind.Bindable[s]() {
t.Fatal()
}
})
})
t.Run("circular reference", func(t *testing.T) {
t.Run("via pointer", func(t *testing.T) {
p := new(any)
*p = p
if bind.BindScalar(p, "42") {
t.Fatal()
}
})
t.Run("via slice", func(t *testing.T) {
s := make([]any, 1)
s[0] = s
if bind.BindScalar(s, "42") {
t.Fatal()
}
})
t.Run("via interface and pointer", func(t *testing.T) {
var v any
v = &v
if bind.BindScalar(v, "42") {
t.Fatal()
}
})
t.Run("via struct field and pointer", func(t *testing.T) {
type s struct{ F *s }
var v s
v.F = &v
if len(bind.FieldValues(v)) > 0 {
t.Fatal()
}
})
t.Run("via struct field and slice", func(t *testing.T) {
type s struct{ F []s }
var v s
v.F = []s{v}
if len(bind.FieldValues(v)) > 0 {
t.Fatal()
}
})
t.Run("value with circular type", func(t *testing.T) {
type p *p
var v p
if len(bind.FieldValues(v)) > 0 {
t.Fatal()
}
})
})
2025-09-03 23:52:33 +02:00
t.Run("accept", func(t *testing.T) {
t.Run("scalar", func(t *testing.T) {
t.Run("yes", func(t *testing.T) {
if !bind.AcceptsScalar[int]() {
t.Fatal()
}
})
t.Run("no", func(t *testing.T) {
if bind.AcceptsScalar[chan int]() {
t.Fatal()
}
})
t.Run("circular", func(t *testing.T) {
type p *p
if bind.AcceptsScalar[p]() {
t.Fatal()
}
})
2025-09-04 00:33:05 +02:00
t.Run("with reflection type", func(t *testing.T) {
if !bind.TypeAcceptsScalar(reflect.TypeFor[int]()) {
t.Fatal()
}
})
2025-09-03 23:52:33 +02:00
})
t.Run("fields", func(t *testing.T) {
t.Run("yes", func(t *testing.T) {
2025-09-04 00:33:05 +02:00
type s struct{ Foo int }
2025-09-03 23:52:33 +02:00
if !bind.AcceptsFields[s]() {
t.Fatal()
}
})
t.Run("no", func(t *testing.T) {
if bind.AcceptsFields[chan int]() {
t.Fatal()
}
})
t.Run("circular", func(t *testing.T) {
type p *p
if bind.AcceptsFields[p]() {
t.Fatal()
}
})
2025-09-04 00:33:05 +02:00
t.Run("with reflection type", func(t *testing.T) {
if !bind.TypeAcceptsFields(reflect.TypeFor[struct{ Foo int }]()) {
t.Fatal()
}
})
2025-09-03 23:52:33 +02:00
})
t.Run("list", func(t *testing.T) {
t.Run("yes", func(t *testing.T) {
if !bind.AcceptsList[[]int]() {
t.Fatal()
}
})
t.Run("no", func(t *testing.T) {
if bind.AcceptsList[int]() {
t.Fatal()
}
})
t.Run("circular", func(t *testing.T) {
type p *p
if bind.AcceptsList[p]() {
t.Fatal()
}
})
2025-09-04 00:33:05 +02:00
t.Run("with reflection type", func(t *testing.T) {
if !bind.TypeAcceptsList(reflect.TypeFor[[]int]()) {
t.Fatal()
}
})
2025-09-03 23:52:33 +02:00
})
})
2025-08-27 21:03:25 +02:00
}