internal/mcp: treat zero struct fields generously

Validating a struct is ambiguous, because a zero field could be
considered missing or present.

Interpret a zero optional struct field in whichever way results in
success.

Change-Id: I6a9474e6f6558d2a8522bc5ba0451967367e467d
Reviewed-on: https://go-review.googlesource.com/c/tools/+/675956
Reviewed-by: Alan Donovan <adonovan@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
Jonathan Amsterdam 2025-05-23 15:41:07 -04:00
commit a08732fd0a

View file

@ -81,30 +81,92 @@ func TestStructInstance(t *testing.T) {
instance := struct {
I int
B bool `json:"b"`
u int
}{1, true, 0}
P *int // either missing or nil
u int // unexported: not a property
}{1, true, nil, 0}
// The instance fails for all of these schemas, demonstrating that it
// was processed correctly.
for _, schema := range []*Schema{
{MinProperties: Ptr(3)},
{MaxProperties: Ptr(1)},
{Required: []string{"i"}}, // the name is "I"
{Required: []string{"B"}}, // the name is "b"
{PropertyNames: &Schema{MinLength: Ptr(2)}},
{Properties: map[string]*Schema{"b": {Type: "number"}}},
{Required: []string{"I"}, AdditionalProperties: falseSchema()},
{DependentRequired: map[string][]string{"b": {"u"}}},
{DependentSchemas: map[string]*Schema{"b": falseSchema()}},
{UnevaluatedProperties: falseSchema()},
for _, tt := range []struct {
s Schema
want bool
}{
{
Schema{MinProperties: Ptr(4)},
false,
},
{
Schema{MinProperties: Ptr(3)},
true, // P interpreted as present
},
{
Schema{MaxProperties: Ptr(1)},
false,
},
{
Schema{MaxProperties: Ptr(2)},
true, // P interpreted as absent
},
{
Schema{Required: []string{"i"}}, // the name is "I"
false,
},
{
Schema{Required: []string{"B"}}, // the name is "b"
false,
},
{
Schema{PropertyNames: &Schema{MinLength: Ptr(2)}},
false,
},
{
Schema{Properties: map[string]*Schema{"b": {Type: "boolean"}}},
true,
},
{
Schema{Properties: map[string]*Schema{"b": {Type: "number"}}},
false,
},
{
Schema{Required: []string{"I"}},
true,
},
{
Schema{Required: []string{"I", "P"}},
true, // P interpreted as present
},
{
Schema{Required: []string{"I", "P"}, Properties: map[string]*Schema{"P": {Type: "number"}}},
false, // P interpreted as present, but not a number
},
{
Schema{Required: []string{"I"}, Properties: map[string]*Schema{"P": {Type: "number"}}},
true, // P not required, so interpreted as absent
},
{
Schema{Required: []string{"I"}, AdditionalProperties: falseSchema()},
false,
},
{
Schema{DependentRequired: map[string][]string{"b": {"u"}}},
false,
},
{
Schema{DependentSchemas: map[string]*Schema{"b": falseSchema()}},
false,
},
{
Schema{UnevaluatedProperties: falseSchema()},
false,
},
} {
res, err := schema.Resolve("", nil)
res, err := tt.s.Resolve("", nil)
if err != nil {
t.Fatal(err)
}
err = res.Validate(instance)
if err == nil {
t.Errorf("succeeded but wanted failure; schema = %s", schema.json())
if err == nil && !tt.want {
t.Errorf("succeeded unexpectedly\nschema = %s", tt.s.json())
} else if err != nil && tt.want {
t.Errorf("Validate: %v\nschema = %s", err, tt.s.json())
}
}
}