package hcltest import ( "strings" "testing" "reflect" "github.com/hashicorp/hcl/v2" "github.com/zclconf/go-cty/cty" ) var mockBodyIsBody hcl.Body = mockBody{} var mockExprLiteralIsExpr hcl.Expression = mockExprLiteral{} var mockExprVariableIsExpr hcl.Expression = mockExprVariable("") func TestMockBodyPartialContent(t *testing.T) { tests := map[string]struct { In *hcl.BodyContent Schema *hcl.BodySchema Want *hcl.BodyContent Remain *hcl.BodyContent DiagCount int }{ "empty": { &hcl.BodyContent{}, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, 0, }, "attribute requested": { &hcl.BodyContent{ Attributes: MockAttrs(map[string]hcl.Expression{ "name": MockExprLiteral(cty.StringVal("Ermintrude")), }), }, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "name", }, }, }, &hcl.BodyContent{ Attributes: MockAttrs(map[string]hcl.Expression{ "name": MockExprLiteral(cty.StringVal("Ermintrude")), }), Blocks: hcl.Blocks{}, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, 0, }, "attribute remains": { &hcl.BodyContent{ Attributes: MockAttrs(map[string]hcl.Expression{ "name": MockExprLiteral(cty.StringVal("Ermintrude")), }), }, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, &hcl.BodyContent{ Attributes: MockAttrs(map[string]hcl.Expression{ "name": MockExprLiteral(cty.StringVal("Ermintrude")), }), Blocks: hcl.Blocks{}, }, 0, }, "attribute missing": { &hcl.BodyContent{ Attributes: hcl.Attributes{}, }, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "name", Required: true, }, }, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, 1, // missing attribute "name" }, "block requested, no labels": { &hcl.BodyContent{ Blocks: hcl.Blocks{ { Type: "baz", }, }, }, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "baz", }, }, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{ { Type: "baz", }, }, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, 0, }, "block requested, wrong labels": { &hcl.BodyContent{ Blocks: hcl.Blocks{ { Type: "baz", }, }, }, &hcl.BodySchema{ Blocks: []hcl.BlockHeaderSchema{ { Type: "baz", LabelNames: []string{"foo"}, }, }, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{ { Type: "baz", }, }, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, 1, // "baz" requires 1 label }, "block remains": { &hcl.BodyContent{ Blocks: hcl.Blocks{ { Type: "baz", }, }, }, &hcl.BodySchema{}, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{}, }, &hcl.BodyContent{ Attributes: hcl.Attributes{}, Blocks: hcl.Blocks{ { Type: "baz", }, }, }, 0, }, "various": { &hcl.BodyContent{ Attributes: MockAttrs(map[string]hcl.Expression{ "name": MockExprLiteral(cty.StringVal("Ermintrude")), "age": MockExprLiteral(cty.NumberIntVal(32)), }), Blocks: hcl.Blocks{ { Type: "baz", }, { Type: "bar", Labels: []string{"foo1"}, }, { Type: "bar", Labels: []string{"foo2"}, }, }, }, &hcl.BodySchema{ Attributes: []hcl.AttributeSchema{ { Name: "name", }, }, Blocks: []hcl.BlockHeaderSchema{ { Type: "bar", LabelNames: []string{"name"}, }, }, }, &hcl.BodyContent{ Attributes: MockAttrs(map[string]hcl.Expression{ "name": MockExprLiteral(cty.StringVal("Ermintrude")), }), Blocks: hcl.Blocks{ { Type: "bar", Labels: []string{"foo1"}, }, { Type: "bar", Labels: []string{"foo2"}, }, }, }, &hcl.BodyContent{ Attributes: MockAttrs(map[string]hcl.Expression{ "age": MockExprLiteral(cty.NumberIntVal(32)), }), Blocks: hcl.Blocks{ { Type: "baz", }, }, }, 0, }, } for name, test := range tests { t.Run(name, func(t *testing.T) { inBody := MockBody(test.In) got, remainBody, diags := inBody.PartialContent(test.Schema) if len(diags) != test.DiagCount { t.Errorf("wrong number of diagnostics %d; want %d", len(diags), test.DiagCount) for _, diag := range diags { t.Logf("- %s", diag) } } if !reflect.DeepEqual(got, test.Want) { t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.Want) } gotRemain := remainBody.(mockBody).C if !reflect.DeepEqual(gotRemain, test.Remain) { t.Errorf("wrong remain\ngot: %#v\nwant: %#v", gotRemain, test.Remain) } }) } } func TestExprList(t *testing.T) { tests := map[string]struct { In hcl.Expression Want []hcl.Expression Diags string }{ "as list": { In: MockExprLiteral(cty.ListVal([]cty.Value{ cty.StringVal("foo"), cty.StringVal("bar"), })), Want: []hcl.Expression{ MockExprLiteral(cty.StringVal("foo")), MockExprLiteral(cty.StringVal("bar")), }, }, "as tuple": { In: MockExprLiteral(cty.TupleVal([]cty.Value{ cty.StringVal("foo"), cty.StringVal("bar"), })), Want: []hcl.Expression{ MockExprLiteral(cty.StringVal("foo")), MockExprLiteral(cty.StringVal("bar")), }, }, "not list": { In: MockExprLiteral(cty.ObjectVal(map[string]cty.Value{ "a": cty.StringVal("foo"), "b": cty.StringVal("bar"), })), Want: nil, Diags: "list expression is required", }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { got, diags := hcl.ExprList(tc.In) if tc.Diags != "" { if diags.HasErrors() && !strings.Contains(diags.Error(), tc.Diags) { t.Errorf("expected error %q, got %q", tc.Diags, diags) } if !diags.HasErrors() { t.Errorf("expected diagnostic message %q", tc.Diags) } } else if diags.HasErrors() { t.Error(diags) } if !reflect.DeepEqual(got, tc.Want) { t.Errorf("incorrect expression,\ngot: %#v\nwant: %#v", got, tc.Want) } }) } } func TestExprMap(t *testing.T) { tests := map[string]struct { In hcl.Expression Want []hcl.KeyValuePair Diags string }{ "as object": { In: MockExprLiteral(cty.ObjectVal(map[string]cty.Value{ "name": cty.StringVal("test"), "count": cty.NumberIntVal(2), })), Want: []hcl.KeyValuePair{ { Key: MockExprLiteral(cty.StringVal("count")), Value: MockExprLiteral(cty.NumberIntVal(2)), }, { Key: MockExprLiteral(cty.StringVal("name")), Value: MockExprLiteral(cty.StringVal("test")), }, }, }, "as map": { In: MockExprLiteral(cty.MapVal(map[string]cty.Value{ "name": cty.StringVal("test"), "version": cty.StringVal("2.0.0"), })), Want: []hcl.KeyValuePair{ { Key: MockExprLiteral(cty.StringVal("name")), Value: MockExprLiteral(cty.StringVal("test")), }, { Key: MockExprLiteral(cty.StringVal("version")), Value: MockExprLiteral(cty.StringVal("2.0.0")), }, }, }, "not map": { In: MockExprLiteral(cty.ListVal([]cty.Value{ cty.StringVal("foo"), cty.StringVal("bar"), })), Want: nil, Diags: "map expression is required", }, } for name, tc := range tests { t.Run(name, func(t *testing.T) { got, diags := hcl.ExprMap(tc.In) if tc.Diags != "" { if diags.HasErrors() && !strings.Contains(diags.Error(), tc.Diags) { t.Errorf("expected error %q, got %q", tc.Diags, diags) } if !diags.HasErrors() { t.Errorf("expected diagnostic message %q", tc.Diags) } } else if diags.HasErrors() { t.Error(diags) } if !reflect.DeepEqual(got, tc.Want) { t.Errorf("incorrect expression,\ngot: %#v\nwant: %#v", got, tc.Want) } }) } }