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 }