Files
opencloud/services/search/pkg/mapping/fillstruct_test.go
Dominik Schmidt 431c97e712 test(search): convert mapping package tests to ginkgo
New package, so use the repo's standard test framework.
2026-07-05 16:56:10 +02:00

107 lines
3.2 KiB
Go

package mapping
import (
"errors"
"reflect"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)
// The walker is shared by both deserializers, so its structural behavior
// (flattening embedded structs, recursing into nested pointers, joining the
// prefix, keeping a pointer only when a field was set, fail-soft skipping) is
// tested here once against a trivial string setter instead of twice through
// Deserialize and DeserializeStringsAt.
// FsEmbVal/FsEmbPtr are exported so their embedded field is exported; an
// unexported embedded type would be skipped by resolveField.
type FsEmbVal struct {
EV string `json:"ev"`
}
type FsEmbPtr struct {
EP string `json:"ep"`
}
type fsNested struct {
N string `json:"n"`
}
type fsRoot struct {
FsEmbVal // embedded value struct: fields promoted
*FsEmbPtr // embedded pointer struct: allocated on demand
Leaf string `json:"leaf"`
Nested *fsNested `json:"nested"` // nested pointer: recursed under "nested."
}
var errBadLeaf = errors.New("bad leaf")
// fsSet writes raw into a string field; the "BAD" sentinel simulates a parse
// failure so the fail-soft path can be exercised.
func fsSet(v reflect.Value, raw string) error {
if raw == "BAD" {
return errBadLeaf
}
v.SetString(raw)
return nil
}
var _ = Describe("fillStruct", func() {
fill := func(fields map[string]string, prefix string) (fsRoot, bool) {
var root fsRoot
touched := fillStruct(reflect.ValueOf(&root).Elem(), fields, prefix, fsSet)
return root, touched
}
It("flattens embedded and recurses nested", func() {
root, touched := fill(map[string]string{
"leaf": "L",
"ev": "EV",
"ep": "EP",
"nested.n": "N",
}, "")
Expect(touched).To(BeTrue())
Expect(root.Leaf).To(Equal("L"))
Expect(root.EV).To(Equal("EV"), "embedded value not promoted")
Expect(root.FsEmbPtr).ToNot(BeNil(), "embedded pointer not allocated")
Expect(root.EP).To(Equal("EP"))
Expect(root.Nested).ToNot(BeNil(), "nested pointer not populated")
Expect(root.Nested.N).To(Equal("N"))
})
It("leaves touched false and pointers nil when nothing matches", func() {
root, touched := fill(map[string]string{"other": "x"}, "")
Expect(touched).To(BeFalse())
Expect(root.Nested).To(BeNil(), "Nested should stay nil")
Expect(root.FsEmbPtr).To(BeNil(), "embedded pointer should stay nil")
})
It("joins the prefix arg with the field name", func() {
root, touched := fill(map[string]string{
"pre.leaf": "L",
"pre.nested.n": "N",
}, "pre")
Expect(touched).To(BeTrue())
Expect(root.Leaf).To(Equal("L"))
Expect(root.Nested).ToNot(BeNil())
Expect(root.Nested.N).To(Equal("N"))
})
It("is fail-soft: errored leaf stays zero, walk continues", func() {
root, touched := fill(map[string]string{
"leaf": "BAD",
"ev": "EV",
}, "")
Expect(touched).To(BeTrue(), "expected touched because ev was set")
Expect(root.Leaf).To(BeEmpty(), "errored leaf should stay zero")
Expect(root.EV).To(Equal("EV"), "walk should continue past the error")
})
It("drops the embedded pointer when its only field errors", func() {
root, touched := fill(map[string]string{"ep": "BAD"}, "")
Expect(touched).To(BeFalse())
Expect(root.FsEmbPtr).To(BeNil(), "embedded pointer should stay nil on error")
})
})