| // Copyright (c) HashiCorp, Inc. |
| // SPDX-License-Identifier: BUSL-1.1 |
| |
| package configschema |
| |
| import ( |
| "testing" |
| |
| "github.com/zclconf/go-cty/cty" |
| ) |
| |
| func TestBlock_WriteOnlyPaths(t *testing.T) { |
| schema := &Block{ |
| Attributes: map[string]*Attribute{ |
| "not_wo": { |
| Type: cty.String, |
| Optional: true, |
| }, |
| "wo": { |
| Type: cty.String, |
| Optional: true, |
| WriteOnly: true, |
| }, |
| "nested": { |
| Optional: true, |
| NestedType: &Object{ |
| Attributes: map[string]*Attribute{ |
| "boop": { |
| Type: cty.String, |
| Optional: true, |
| }, |
| "honk": { |
| Type: cty.String, |
| Optional: true, |
| WriteOnly: true, |
| }, |
| }, |
| Nesting: NestingList, |
| }, |
| }, |
| "single": { |
| Optional: true, |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "not_wo": { |
| Optional: true, |
| Type: cty.String, |
| }, |
| "wo": { |
| Type: cty.String, |
| Optional: true, |
| WriteOnly: true, |
| }, |
| "nested_single": { |
| Optional: true, |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "not_wo": { |
| Optional: true, |
| Type: cty.String, |
| }, |
| "wo": { |
| Type: cty.String, |
| Optional: true, |
| WriteOnly: true, |
| }, |
| }, |
| }, |
| }, |
| "single_wo": { |
| Optional: true, |
| WriteOnly: true, |
| NestedType: &Object{ |
| Nesting: NestingSingle, |
| Attributes: map[string]*Attribute{ |
| "not_wo": { |
| Optional: true, |
| Type: cty.String, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| }, |
| |
| BlockTypes: map[string]*NestedBlock{ |
| "list": { |
| Nesting: NestingList, |
| Block: Block{ |
| Attributes: map[string]*Attribute{ |
| "not_wo": { |
| Type: cty.String, |
| Optional: true, |
| }, |
| "wo": { |
| Type: cty.String, |
| Optional: true, |
| WriteOnly: true, |
| }, |
| }, |
| }, |
| }, |
| "single_block": { |
| Nesting: NestingSingle, |
| Block: Block{ |
| Attributes: map[string]*Attribute{ |
| "not_wo": { |
| Type: cty.String, |
| Optional: true, |
| }, |
| "wo": { |
| Type: cty.String, |
| Optional: true, |
| WriteOnly: true, |
| }, |
| }, |
| }, |
| }}, |
| } |
| |
| testCases := map[string]struct { |
| value cty.Value |
| expected []cty.Path |
| }{ |
| "unknown value": { |
| cty.UnknownVal(schema.ImpliedType()), |
| []cty.Path{}, |
| }, |
| "null object": { |
| cty.NullVal(schema.ImpliedType()), |
| []cty.Path{}, |
| }, |
| "object with unknown attributes and blocks": { |
| cty.ObjectVal(map[string]cty.Value{ |
| "wo": cty.UnknownVal(cty.String), |
| "not_wo": cty.UnknownVal(cty.String), |
| "nested": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ |
| "boop": cty.String, |
| "honk": cty.String, |
| }))), |
| "list": cty.UnknownVal(schema.BlockTypes["list"].ImpliedType()), |
| }), |
| []cty.Path{ |
| {cty.GetAttrStep{Name: "wo"}}, |
| }, |
| }, |
| "object with block value": { |
| cty.ObjectVal(map[string]cty.Value{ |
| "wo": cty.NullVal(cty.String), |
| "not_wo": cty.UnknownVal(cty.String), |
| "list": cty.ListVal([]cty.Value{ |
| cty.ObjectVal(map[string]cty.Value{ |
| "wo": cty.UnknownVal(cty.String), |
| "not_wo": cty.UnknownVal(cty.String), |
| }), |
| cty.ObjectVal(map[string]cty.Value{ |
| "wo": cty.NullVal(cty.String), |
| "not_wo": cty.NullVal(cty.String), |
| }), |
| }), |
| }), |
| []cty.Path{ |
| {cty.GetAttrStep{Name: "wo"}}, |
| {cty.GetAttrStep{Name: "list"}, cty.IndexStep{Key: cty.NumberIntVal(0)}, cty.GetAttrStep{Name: "wo"}}, |
| {cty.GetAttrStep{Name: "list"}, cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.GetAttrStep{Name: "wo"}}, |
| }, |
| }, |
| "object with known values and nested attribute": { |
| cty.ObjectVal(map[string]cty.Value{ |
| "wo": cty.StringVal("foo"), |
| "not_wo": cty.StringVal("bar"), |
| "nested": cty.ListVal([]cty.Value{ |
| cty.ObjectVal(map[string]cty.Value{ |
| "boop": cty.StringVal("foo"), |
| "honk": cty.StringVal("bar"), |
| }), |
| cty.ObjectVal(map[string]cty.Value{ |
| "boop": cty.NullVal(cty.String), |
| "honk": cty.NullVal(cty.String), |
| }), |
| cty.ObjectVal(map[string]cty.Value{ |
| "boop": cty.UnknownVal(cty.String), |
| "honk": cty.UnknownVal(cty.String), |
| }), |
| }), |
| "list": cty.NullVal(cty.List(cty.Object(map[string]cty.Type{ |
| "not_wo": cty.String, |
| "wo": cty.String, |
| }))), |
| }), |
| []cty.Path{ |
| {cty.GetAttrStep{Name: "wo"}}, |
| {cty.GetAttrStep{Name: "nested"}, cty.IndexStep{Key: cty.NumberIntVal(0)}, cty.GetAttrStep{Name: "honk"}}, |
| {cty.GetAttrStep{Name: "nested"}, cty.IndexStep{Key: cty.NumberIntVal(1)}, cty.GetAttrStep{Name: "honk"}}, |
| {cty.GetAttrStep{Name: "nested"}, cty.IndexStep{Key: cty.NumberIntVal(2)}, cty.GetAttrStep{Name: "honk"}}, |
| }, |
| }, |
| "object with single nested block and attribute": { |
| cty.ObjectVal(map[string]cty.Value{ |
| "wo": cty.StringVal("foo"), |
| "not_wo": cty.StringVal("bar"), |
| "single": cty.ObjectVal(map[string]cty.Value{ |
| "not_wo": cty.StringVal("foo"), |
| "wo": cty.StringVal("bar"), |
| }), |
| "single_block": cty.ObjectVal(map[string]cty.Value{ |
| "not_wo": cty.StringVal("test"), |
| "wo": cty.StringVal("secret"), |
| }), |
| }), |
| []cty.Path{ |
| {cty.GetAttrStep{Name: "wo"}}, |
| {cty.GetAttrStep{Name: "single"}, cty.GetAttrStep{Name: "wo"}}, |
| cty.GetAttrPath("single").GetAttr(("single_wo")), |
| {cty.GetAttrStep{Name: "single_block"}, cty.GetAttrStep{Name: "wo"}}, |
| }, |
| }, |
| "object with doubly single-nested attribute": { |
| cty.ObjectVal(map[string]cty.Value{ |
| "single": cty.ObjectVal(map[string]cty.Value{ |
| "not_wo": cty.StringVal("foo"), |
| "wo": cty.NullVal(cty.String), |
| "nested_single": cty.ObjectVal(map[string]cty.Value{ |
| "not_wo": cty.StringVal("foo"), |
| "wo": cty.NullVal(cty.String), |
| }), |
| }), |
| }), |
| []cty.Path{ |
| {cty.GetAttrStep{Name: "wo"}}, |
| {cty.GetAttrStep{Name: "single"}, cty.GetAttrStep{Name: "wo"}}, |
| cty.GetAttrPath("single").GetAttr(("single_wo")), |
| {cty.GetAttrStep{Name: "single"}, cty.GetAttrStep{Name: "nested_single"}, cty.GetAttrStep{Name: "wo"}}, |
| }, |
| }, |
| "single nested write-only attr": { |
| cty.ObjectVal(map[string]cty.Value{ |
| "single": cty.ObjectVal(map[string]cty.Value{ |
| "single_wo": cty.ObjectVal(map[string]cty.Value{ |
| "not_wo": cty.StringVal("foo").Mark("test"), |
| }), |
| }), |
| }), |
| []cty.Path{ |
| cty.GetAttrPath("wo"), |
| cty.GetAttrPath("single").GetAttr(("wo")), |
| cty.GetAttrPath("single").GetAttr(("single_wo")), |
| }, |
| }, |
| "single nested null write-only attr": { |
| cty.ObjectVal(map[string]cty.Value{ |
| "single": cty.NullVal(cty.Object(map[string]cty.Type{ |
| "not_wo": cty.String, |
| "wo": cty.String, |
| "nested_single": cty.Object(map[string]cty.Type{ |
| "not_wo": cty.String, |
| "wo": cty.String, |
| }), |
| "single_wo": cty.Object(map[string]cty.Type{ |
| "not_wo": cty.String, |
| }), |
| })), |
| }), |
| []cty.Path{ |
| {cty.GetAttrStep{Name: "wo"}}, |
| }, |
| }, |
| } |
| |
| for name, tc := range testCases { |
| t.Run(name, func(t *testing.T) { |
| if err := schema.InternalValidate(); err != nil { |
| t.Fatal(err) |
| } |
| val, err := schema.CoerceValue(tc.value) |
| if err != nil { |
| t.Fatal(err) |
| } |
| woPaths := schema.WriteOnlyPaths(val, nil) |
| if !cty.NewPathSet(tc.expected...).Equal(cty.NewPathSet(woPaths...)) { |
| t.Fatalf("\nexpected: %#v\ngot: %#v\n", tc.expected, woPaths) |
| } |
| }) |
| } |
| } |