github.com/hashicorp/hcl/v2@v2.20.0/json/public_test.go (about)

     1  // Copyright (c) HashiCorp, Inc.
     2  // SPDX-License-Identifier: MPL-2.0
     3  
     4  package json
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  	"testing"
    10  
    11  	"github.com/hashicorp/hcl/v2"
    12  	"github.com/zclconf/go-cty/cty"
    13  )
    14  
    15  func TestParse_nonObject(t *testing.T) {
    16  	src := `true`
    17  	file, diags := Parse([]byte(src), "")
    18  	if len(diags) != 1 {
    19  		t.Errorf("got %d diagnostics; want 1", len(diags))
    20  	}
    21  	if file == nil {
    22  		t.Errorf("got nil File; want actual file")
    23  	}
    24  	if file.Body == nil {
    25  		t.Fatalf("got nil Body; want actual body")
    26  	}
    27  	if file.Body.(*body).val == nil {
    28  		t.Errorf("got nil Body object; want placeholder object")
    29  	}
    30  }
    31  
    32  func TestParseTemplate(t *testing.T) {
    33  	src := `{"greeting": "hello ${\"world\"}"}`
    34  	file, diags := Parse([]byte(src), "")
    35  	if len(diags) != 0 {
    36  		t.Errorf("got %d diagnostics on parse; want 0", len(diags))
    37  		for _, diag := range diags {
    38  			t.Logf("- %s", diag.Error())
    39  		}
    40  	}
    41  	if file == nil {
    42  		t.Errorf("got nil File; want actual file")
    43  	}
    44  	if file.Body == nil {
    45  		t.Fatalf("got nil Body; want actual body")
    46  	}
    47  	attrs, diags := file.Body.JustAttributes()
    48  	if len(diags) != 0 {
    49  		t.Errorf("got %d diagnostics on decode; want 0", len(diags))
    50  		for _, diag := range diags {
    51  			t.Logf("- %s", diag.Error())
    52  		}
    53  	}
    54  
    55  	val, diags := attrs["greeting"].Expr.Value(&hcl.EvalContext{})
    56  	if len(diags) != 0 {
    57  		t.Errorf("got %d diagnostics on eval; want 0", len(diags))
    58  		for _, diag := range diags {
    59  			t.Logf("- %s", diag.Error())
    60  		}
    61  	}
    62  
    63  	if !val.RawEquals(cty.StringVal("hello world")) {
    64  		t.Errorf("wrong result %#v; want %#v", val, cty.StringVal("hello world"))
    65  	}
    66  }
    67  
    68  func TestParseTemplateUnwrap(t *testing.T) {
    69  	src := `{"greeting": "${true}"}`
    70  	file, diags := Parse([]byte(src), "")
    71  	if len(diags) != 0 {
    72  		t.Errorf("got %d diagnostics on parse; want 0", len(diags))
    73  		for _, diag := range diags {
    74  			t.Logf("- %s", diag.Error())
    75  		}
    76  	}
    77  	if file == nil {
    78  		t.Errorf("got nil File; want actual file")
    79  	}
    80  	if file.Body == nil {
    81  		t.Fatalf("got nil Body; want actual body")
    82  	}
    83  	attrs, diags := file.Body.JustAttributes()
    84  	if len(diags) != 0 {
    85  		t.Errorf("got %d diagnostics on decode; want 0", len(diags))
    86  		for _, diag := range diags {
    87  			t.Logf("- %s", diag.Error())
    88  		}
    89  	}
    90  
    91  	val, diags := attrs["greeting"].Expr.Value(&hcl.EvalContext{})
    92  	if len(diags) != 0 {
    93  		t.Errorf("got %d diagnostics on eval; want 0", len(diags))
    94  		for _, diag := range diags {
    95  			t.Logf("- %s", diag.Error())
    96  		}
    97  	}
    98  
    99  	if !val.RawEquals(cty.True) {
   100  		t.Errorf("wrong result %#v; want %#v", val, cty.True)
   101  	}
   102  }
   103  
   104  func TestParse_malformed(t *testing.T) {
   105  	src := `{
   106    "http_proxy_url: "http://xxxxxx",
   107  }`
   108  	file, diags := Parse([]byte(src), "")
   109  	if got, want := len(diags), 2; got != want {
   110  		t.Errorf("got %d diagnostics; want %d", got, want)
   111  	}
   112  	if err, want := diags.Error(), `Missing property value colon`; !strings.Contains(err, want) {
   113  		t.Errorf("diags are %q, but should contain %q", err, want)
   114  	}
   115  	if file == nil {
   116  		t.Errorf("got nil File; want actual file")
   117  	}
   118  }
   119  
   120  func TestParseWithStartPos(t *testing.T) {
   121  	src := `{
   122    "foo": {
   123      "bar": "baz"
   124    }
   125  }`
   126  	part := `{
   127      "bar": "baz"
   128    }`
   129  
   130  	file, diags := Parse([]byte(src), "")
   131  	partFile, partDiags := ParseWithStartPos([]byte(part), "", hcl.Pos{Byte: 0, Line: 2, Column: 10})
   132  	if len(diags) != 0 {
   133  		t.Errorf("got %d diagnostics on parse src; want 0", len(diags))
   134  		for _, diag := range diags {
   135  			t.Logf("- %s", diag.Error())
   136  		}
   137  	}
   138  	if len(partDiags) != 0 {
   139  		t.Errorf("got %d diagnostics on parse part src; want 0", len(partDiags))
   140  		for _, diag := range partDiags {
   141  			t.Logf("- %s", diag.Error())
   142  		}
   143  	}
   144  
   145  	if file == nil {
   146  		t.Errorf("got nil File; want actual file")
   147  	}
   148  	if file.Body == nil {
   149  		t.Fatalf("got nil Body; want actual body")
   150  	}
   151  	if partFile == nil {
   152  		t.Errorf("got nil part File; want actual file")
   153  	}
   154  	if partFile.Body == nil {
   155  		t.Fatalf("got nil part Body; want actual body")
   156  	}
   157  
   158  	content, diags := file.Body.Content(&hcl.BodySchema{
   159  		Blocks: []hcl.BlockHeaderSchema{{Type: "foo"}},
   160  	})
   161  	if len(diags) != 0 {
   162  		t.Errorf("got %d diagnostics on decode; want 0", len(diags))
   163  		for _, diag := range diags {
   164  			t.Logf("- %s", diag.Error())
   165  		}
   166  	}
   167  	attrs, diags := content.Blocks[0].Body.JustAttributes()
   168  	if len(diags) != 0 {
   169  		t.Errorf("got %d diagnostics on decode; want 0", len(diags))
   170  		for _, diag := range diags {
   171  			t.Logf("- %s", diag.Error())
   172  		}
   173  	}
   174  	srcRange := attrs["bar"].Expr.Range()
   175  
   176  	partAttrs, diags := partFile.Body.JustAttributes()
   177  	if len(diags) != 0 {
   178  		t.Errorf("got %d diagnostics on decode; want 0", len(diags))
   179  		for _, diag := range diags {
   180  			t.Logf("- %s", diag.Error())
   181  		}
   182  	}
   183  	partRange := partAttrs["bar"].Expr.Range()
   184  
   185  	if srcRange.String() != partRange.String() {
   186  		t.Errorf("The two ranges did not match: src=%s, part=%s", srcRange, partRange)
   187  	}
   188  }
   189  
   190  func TestParseExpression(t *testing.T) {
   191  	tests := []struct {
   192  		Input string
   193  		Want  string
   194  	}{
   195  		{
   196  			`"hello"`,
   197  			`cty.StringVal("hello")`,
   198  		},
   199  		{
   200  			`"hello ${noun}"`,
   201  			`cty.StringVal("hello world")`,
   202  		},
   203  		{
   204  			"true",
   205  			"cty.True",
   206  		},
   207  		{
   208  			"false",
   209  			"cty.False",
   210  		},
   211  		{
   212  			"1",
   213  			"cty.NumberIntVal(1)",
   214  		},
   215  		{
   216  			"{}",
   217  			"cty.EmptyObjectVal",
   218  		},
   219  		{
   220  			`{"foo":"bar","baz":1}`,
   221  			`cty.ObjectVal(map[string]cty.Value{"baz":cty.NumberIntVal(1), "foo":cty.StringVal("bar")})`,
   222  		},
   223  		{
   224  			"[]",
   225  			"cty.EmptyTupleVal",
   226  		},
   227  		{
   228  			`["1",2,3]`,
   229  			`cty.TupleVal([]cty.Value{cty.StringVal("1"), cty.NumberIntVal(2), cty.NumberIntVal(3)})`,
   230  		},
   231  	}
   232  
   233  	for _, test := range tests {
   234  		t.Run(test.Input, func(t *testing.T) {
   235  			expr, diags := ParseExpression([]byte(test.Input), "")
   236  			if diags.HasErrors() {
   237  				t.Errorf("got %d diagnostics; want 0", len(diags))
   238  				for _, d := range diags {
   239  					t.Logf("  - %s", d.Error())
   240  				}
   241  			}
   242  
   243  			value, diags := expr.Value(&hcl.EvalContext{
   244  				Variables: map[string]cty.Value{
   245  					"noun": cty.StringVal("world"),
   246  				},
   247  			})
   248  			if diags.HasErrors() {
   249  				t.Errorf("got %d diagnostics on decode value; want 0", len(diags))
   250  				for _, d := range diags {
   251  					t.Logf("  - %s", d.Error())
   252  				}
   253  			}
   254  			got := fmt.Sprintf("%#v", value)
   255  
   256  			if got != test.Want {
   257  				t.Errorf("got %s, but want %s", got, test.Want)
   258  			}
   259  		})
   260  	}
   261  }
   262  
   263  func TestParseExpression_malformed(t *testing.T) {
   264  	src := `invalid`
   265  	expr, diags := ParseExpression([]byte(src), "")
   266  	if got, want := len(diags), 1; got != want {
   267  		t.Errorf("got %d diagnostics; want %d", got, want)
   268  	}
   269  	if err, want := diags.Error(), `Invalid JSON keyword`; !strings.Contains(err, want) {
   270  		t.Errorf("diags are %q, but should contain %q", err, want)
   271  	}
   272  	if expr == nil {
   273  		t.Errorf("got nil Expression; want actual expression")
   274  	}
   275  }
   276  
   277  func TestParseExpressionWithStartPos(t *testing.T) {
   278  	src := `{
   279    "foo": "bar"
   280  }`
   281  	part := `"bar"`
   282  
   283  	file, diags := Parse([]byte(src), "")
   284  	partExpr, partDiags := ParseExpressionWithStartPos([]byte(part), "", hcl.Pos{Byte: 0, Line: 2, Column: 10})
   285  	if len(diags) != 0 {
   286  		t.Errorf("got %d diagnostics on parse src; want 0", len(diags))
   287  		for _, diag := range diags {
   288  			t.Logf("- %s", diag.Error())
   289  		}
   290  	}
   291  	if len(partDiags) != 0 {
   292  		t.Errorf("got %d diagnostics on parse part src; want 0", len(partDiags))
   293  		for _, diag := range partDiags {
   294  			t.Logf("- %s", diag.Error())
   295  		}
   296  	}
   297  
   298  	if file == nil {
   299  		t.Errorf("got nil File; want actual file")
   300  	}
   301  	if file.Body == nil {
   302  		t.Errorf("got nil Body: want actual body")
   303  	}
   304  	if partExpr == nil {
   305  		t.Errorf("got nil Expression; want actual expression")
   306  	}
   307  
   308  	content, diags := file.Body.Content(&hcl.BodySchema{
   309  		Attributes: []hcl.AttributeSchema{{Name: "foo"}},
   310  	})
   311  	if len(diags) != 0 {
   312  		t.Errorf("got %d diagnostics on decode; want 0", len(diags))
   313  		for _, diag := range diags {
   314  			t.Logf("- %s", diag.Error())
   315  		}
   316  	}
   317  	expr := content.Attributes["foo"].Expr
   318  
   319  	if expr.Range().String() != partExpr.Range().String() {
   320  		t.Errorf("The two ranges did not match: src=%s, part=%s", expr.Range(), partExpr.Range())
   321  	}
   322  }