// Copyright IBM Corp. 2014, 2026 // SPDX-License-Identifier: BUSL-1.1 package ephemeral import ( "testing" "github.com/zclconf/go-cty/cty" "github.com/hashicorp/terraform/internal/configs/configschema" ) func TestNonNullWriteOnlyPaths(t *testing.T) { for name, tc := range map[string]struct { val cty.Value schema *configschema.Block expectedPaths []cty.Path }{ "no write-only attributes": { val: cty.ObjectVal(map[string]cty.Value{ "id": cty.StringVal("i-abc123"), }), schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": { Type: cty.String, }, }, }, }, "write-only attribute with null value": { val: cty.ObjectVal(map[string]cty.Value{ "id": cty.NullVal(cty.String), }), schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "id": { Type: cty.String, Optional: true, WriteOnly: true, }, }, }, }, "write-only attribute with non-null value": { val: cty.ObjectVal(map[string]cty.Value{ "valid": cty.NullVal(cty.String), "id": cty.StringVal("i-abc123"), }), schema: &configschema.Block{ Attributes: map[string]*configschema.Attribute{ "valid": { Type: cty.String, Optional: true, WriteOnly: true, }, "id": { Type: cty.String, Optional: true, WriteOnly: true, }, }, }, expectedPaths: []cty.Path{cty.GetAttrPath("id")}, }, "write-only attributes in blocks": { val: cty.ObjectVal(map[string]cty.Value{ "foo": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "valid-write-only": cty.NullVal(cty.String), "valid": cty.StringVal("valid"), "id": cty.StringVal("i-abc123"), "bar": cty.ListVal([]cty.Value{ cty.ObjectVal(map[string]cty.Value{ "valid-write-only": cty.NullVal(cty.String), "valid": cty.StringVal("valid"), "id": cty.StringVal("i-abc123"), }), cty.ObjectVal(map[string]cty.Value{ "valid-write-only": cty.NullVal(cty.String), "valid": cty.StringVal("valid"), "id": cty.StringVal("i-xyz123"), }), }), }), }), }), schema: &configschema.Block{ BlockTypes: map[string]*configschema.NestedBlock{ "foo": { Nesting: configschema.NestingList, Block: configschema.Block{ Attributes: map[string]*configschema.Attribute{ "valid-write-only": { Type: cty.String, Optional: true, WriteOnly: true, }, "valid": { Type: cty.String, Optional: true, }, "id": { Type: cty.String, Optional: true, WriteOnly: true, }, }, BlockTypes: map[string]*configschema.NestedBlock{ "bar": { Nesting: configschema.NestingList, Block: configschema.Block{ Attributes: map[string]*configschema.Attribute{ "valid-write-only": { Type: cty.String, Optional: true, WriteOnly: true, }, "valid": { Type: cty.String, Optional: true, }, "id": { Type: cty.String, Optional: true, WriteOnly: true, }, }, }, }, }, }, }, }, }, expectedPaths: []cty.Path{ cty.GetAttrPath("foo").Index(cty.NumberIntVal(0)).GetAttr("id"), cty.GetAttrPath("foo").Index(cty.NumberIntVal(0)).GetAttr("bar").Index(cty.NumberIntVal(0)).GetAttr("id"), cty.GetAttrPath("foo").Index(cty.NumberIntVal(0)).GetAttr("bar").Index(cty.NumberIntVal(1)).GetAttr("id"), }, }, } { t.Run(name, func(t *testing.T) { paths, err := nonNullWriteOnlyPaths(tc.val, tc.schema) if err != nil { t.Fatal(err) } if len(paths) != len(tc.expectedPaths) { t.Fatalf("expected %d write-only paths, got %d", len(tc.expectedPaths), len(paths)) } for i, path := range paths { if !path.Equals(tc.expectedPaths[i]) { t.Fatalf("expected path %#v, got %#v", tc.expectedPaths[i], path) } } }) } }