test(16.2-06): full unit coverage of the plugin field type framework

- pact: FormFieldError message joining, nil pointer, funcs never serialized
- cabana: registry and option boot rules, compile (option kinds, general
  and core keys, CompileForm vs CompileFormWithTypes, settings and relation
  refusal), decode 422/500 split, localized and capped notices, Load,
  ListValue with the request ctx, field writes, field-types and upload
  routes
- TestFieldTypeSavePostgres (Docker host): create, show, untouched and
  changed update, refused update, upload in and out of the record scope
This commit is contained in:
2026-10-10 15:49:35 +02:00
parent 08a20021e3
commit 3404a4b3ad
3 changed files with 1354 additions and 0 deletions

View File

@@ -0,0 +1,88 @@
package pact
import (
"context"
"encoding/json"
"errors"
"fmt"
"testing"
)
func TestFormFieldErrorMessage(t *testing.T) {
for _, tc := range []struct {
messages []string
want string
}{
{nil, ""},
{[]string{}, ""},
{[]string{"acme.demo::lang.bad"}, "acme.demo::lang.bad"},
{[]string{"first", "second", "third"}, "first; second; third"},
} {
err := &FormFieldError{Messages: tc.messages}
if got := err.Error(); got != tc.want {
t.Fatalf("Error(%q) = %q, want %q", tc.messages, got, tc.want)
}
}
t.Run("a nil pointer does not panic", func(t *testing.T) {
var err *FormFieldError
if got := err.Error(); got != "" {
t.Fatalf("nil Error() = %q", got)
}
})
t.Run("it is found through a wrapped error", func(t *testing.T) {
wrapped := fmt.Errorf("decode: %w", &FormFieldError{Messages: []string{"a", "b"}})
var refused *FormFieldError
if !errors.As(wrapped, &refused) || refused.Error() != "a; b" {
t.Fatalf("errors.As = %v, %v", refused, wrapped)
}
})
}
// TestFormFieldTypeContract pins the plugin-facing shape: the funcs are
// never serialized, and a plugin implements HasFormFieldTypes.
func TestFormFieldTypeContract(t *testing.T) {
typ := FormFieldType{
Name: "notes",
Element: "acme-demo-notes",
Translatable: true,
Scripts: []string{"assets/js/notes.js"},
Styles: []string{"assets/css/notes.css"},
Options: []string{"rows"},
CheckOptions: func(map[string]any) error { return nil },
Decode: func(context.Context, FormFieldValue) (FormFieldDecoded, error) {
return FormFieldDecoded{}, nil
},
Load: func(context.Context, FormFieldValue) (any, error) { return nil, nil },
ListValue: func(context.Context, any) string { return "" },
Upload: func(context.Context, FormFieldUpload) (FormFieldUploadResult, error) {
return FormFieldUploadResult{}, nil
},
}
raw, err := json.Marshal(typ)
if err != nil {
t.Fatal(err)
}
var keys map[string]any
if err := json.Unmarshal(raw, &keys); err != nil {
t.Fatal(err)
}
for _, fn := range []string{"CheckOptions", "Decode", "Load", "ListValue", "Upload"} {
if _, ok := keys[fn]; ok {
t.Fatalf("%s is serialized: %s", fn, raw)
}
}
if keys["Name"] != "notes" || keys["Element"] != "acme-demo-notes" || keys["Translatable"] != true {
t.Fatalf("json = %s", raw)
}
var src HasFormFieldTypes = fieldTypePlugin{types: []FormFieldType{typ}}
if got := src.FormFieldTypes(); len(got) != 1 || got[0].Name != "notes" {
t.Fatalf("FormFieldTypes() = %+v", got)
}
}
type fieldTypePlugin struct{ types []FormFieldType }
func (p fieldTypePlugin) FormFieldTypes() []FormFieldType { return p.types }