diff --git a/decoder/internal/walker/walker_test.go b/decoder/internal/walker/walker_test.go new file mode 100644 index 00000000..ace51d20 --- /dev/null +++ b/decoder/internal/walker/walker_test.go @@ -0,0 +1,160 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package walker + +import ( + "context" + "fmt" + "testing" + + "github.com/hashicorp/hcl-lang/schema" + "github.com/hashicorp/hcl-lang/schemacontext" + "github.com/hashicorp/hcl/v2" + "github.com/hashicorp/hcl/v2/hclsyntax" +) + +func TestWalk_basic(t *testing.T) { + ctx := context.Background() + src := []byte(` +first { + nested1 {} + nested2 {} +} +second { + foo = "bar" +} +`) + file, diags := hclsyntax.ParseConfig(src, "test.hcl", hcl.InitialPos) + if len(diags) > 0 { + t.Fatalf("unexpected parser diagnostics: %s", diags) + } + + rootSchema := schema.NewBodySchema() + nodeCount := 0 + tw := NewTestWalker(func(ctx context.Context, node hclsyntax.Node, nodeSchema schema.Schema) (context.Context, hcl.Diagnostics) { + nodeCount++ + return ctx, nil + }) + diags = Walk(ctx, file.Body.(*hclsyntax.Body), rootSchema, tw) + if len(diags) > 0 { + t.Fatalf("unexpected diagnostics: %s", diags) + } + // root body + first(block+body) + nested1(block+body) + nested2(block+body) + second(block+body) + foo + expectedNodeCount := 10 + if nodeCount != expectedNodeCount { + t.Fatalf("unexpected node count: %d, expected: %d", nodeCount, expectedNodeCount) + } +} + +func TestWalk_nestingLevel(t *testing.T) { + ctx := context.Background() + src := []byte(` +first { + nested1 {} + nested2 {} +} +rootattr = "foo" +second { + foo = "bar" +} +`) + file, pDiags := hclsyntax.ParseConfig(src, "test.hcl", hcl.InitialPos) + if len(pDiags) > 0 { + t.Fatalf("unexpected parser diagnostics: %s", pDiags) + } + + rootSchema := schema.NewBodySchema() + + firstBlockFound := false + nestedBlockFound := false + rootAttrFound := false + nestedAttrFound := false + + tw := NewTestWalker(func(ctx context.Context, node hclsyntax.Node, nodeSchema schema.Schema) (context.Context, hcl.Diagnostics) { + var diags hcl.Diagnostics + + nestingLvl, ok := schemacontext.BlockNestingLevel(ctx) + if !ok { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: "nesting level not available", + }) + } + + if block, ok := node.(*hclsyntax.Block); ok && block.Type == "first" { + firstBlockFound = true + if nestingLvl != 0 { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("%q: unexpected nesting level %d, expected 0", "first", nestingLvl), + }) + } + } + if block, ok := node.(*hclsyntax.Block); ok && block.Type == "nested2" { + nestedBlockFound = true + if nestingLvl != 1 { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("%q: unexpected nesting level %d, expected 1", "nested2", nestingLvl), + }) + } + } + + if attr, ok := node.(*hclsyntax.Attribute); ok && attr.Name == "rootattr" { + rootAttrFound = true + if nestingLvl != 0 { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("%q: unexpected nesting level %d, expected 0", "rootattr", nestingLvl), + }) + } + } + + if attr, ok := node.(*hclsyntax.Attribute); ok && attr.Name == "foo" { + nestedAttrFound = true + if nestingLvl != 1 { + diags = diags.Append(&hcl.Diagnostic{ + Severity: hcl.DiagError, + Summary: fmt.Sprintf("%q: unexpected nesting level %d, expected 1", "foo", nestingLvl), + }) + } + } + + return ctx, diags + }) + + diags := Walk(ctx, file.Body.(*hclsyntax.Body), rootSchema, tw) + if len(diags) > 0 { + t.Fatalf("unexpected diagnostics: %s", diags) + } + + if !firstBlockFound { + t.Error("expected first block to be found") + } + if !nestedBlockFound { + t.Error("expected nested block to be found") + } + if !rootAttrFound { + t.Error("expected root attribute to be found") + } + if !nestedAttrFound { + t.Error("expected nested attribute to be found") + } +} + +func NewTestWalker(visitFunc visitFunc) Walker { + return testWalker{ + visitFunc: visitFunc, + } +} + +type visitFunc func(ctx context.Context, node hclsyntax.Node, nodeSchema schema.Schema) (context.Context, hcl.Diagnostics) + +type testWalker struct { + visitFunc visitFunc +} + +func (tw testWalker) Visit(ctx context.Context, node hclsyntax.Node, nodeSchema schema.Schema) (context.Context, hcl.Diagnostics) { + return tw.visitFunc(ctx, node, nodeSchema) +}