github.com/hashicorp/hcl/v2@v2.20.0/hclwrite/ast_block_test.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package hclwrite 5 6 import ( 7 "fmt" 8 "reflect" 9 "strings" 10 "testing" 11 12 "github.com/davecgh/go-spew/spew" 13 "github.com/google/go-cmp/cmp" 14 "github.com/hashicorp/hcl/v2" 15 "github.com/hashicorp/hcl/v2/hclsyntax" 16 ) 17 18 func TestBlockType(t *testing.T) { 19 tests := []struct { 20 src string 21 want string 22 }{ 23 { 24 ` 25 service { 26 attr0 = "val0" 27 } 28 `, 29 "service", 30 }, 31 } 32 33 for _, test := range tests { 34 t.Run(fmt.Sprintf("%s", test.want), func(t *testing.T) { 35 f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) 36 if len(diags) != 0 { 37 for _, diag := range diags { 38 t.Logf("- %s", diag.Error()) 39 } 40 t.Fatalf("unexpected diagnostics") 41 } 42 43 block := f.Body().Blocks()[0] 44 got := string(block.Type()) 45 if got != test.want { 46 t.Errorf("wrong result\ngot: %s\nwant: %s", got, test.want) 47 } 48 }) 49 } 50 } 51 52 func TestBlockLabels(t *testing.T) { 53 tests := []struct { 54 src string 55 want []string 56 }{ 57 { 58 ` 59 nolabel { 60 } 61 `, 62 []string{}, 63 }, 64 { 65 ` 66 quoted "label1" { 67 } 68 `, 69 []string{"label1"}, 70 }, 71 { 72 ` 73 quoted "label1" "label2" { 74 } 75 `, 76 []string{"label1", "label2"}, 77 }, 78 { 79 ` 80 quoted "label1" /* foo */ "label2" { 81 } 82 `, 83 []string{"label1", "label2"}, 84 }, 85 { 86 ` 87 unquoted label1 { 88 } 89 `, 90 []string{"label1"}, 91 }, 92 { 93 ` 94 unquoted label1 /* foo */ label2 { 95 } 96 `, 97 []string{"label1", "label2"}, 98 }, 99 { 100 ` 101 mixed label1 "label2" { 102 } 103 `, 104 []string{"label1", "label2"}, 105 }, 106 { 107 ` 108 escape "\u0041" { 109 } 110 `, 111 []string{"\u0041"}, 112 }, 113 { 114 ` 115 blank "" { 116 } 117 `, 118 []string{""}, 119 }, 120 } 121 122 for _, test := range tests { 123 t.Run(fmt.Sprintf("%s", strings.Join(test.want, " ")), func(t *testing.T) { 124 f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) 125 if len(diags) != 0 { 126 for _, diag := range diags { 127 t.Logf("- %s", diag.Error()) 128 } 129 t.Fatalf("unexpected diagnostics") 130 } 131 132 block := f.Body().Blocks()[0] 133 got := block.Labels() 134 if !reflect.DeepEqual(got, test.want) { 135 t.Errorf("wrong result\ngot: %#v\nwant: %#v", got, test.want) 136 } 137 }) 138 } 139 } 140 141 func TestBlockSetType(t *testing.T) { 142 tests := []struct { 143 src string 144 oldTypeName string 145 newTypeName string 146 labels []string 147 want Tokens 148 }{ 149 { 150 "foo {}", 151 "foo", 152 "bar", 153 nil, 154 Tokens{ 155 { 156 Type: hclsyntax.TokenIdent, 157 Bytes: []byte(`bar`), 158 SpacesBefore: 0, 159 }, 160 { 161 Type: hclsyntax.TokenOBrace, 162 Bytes: []byte{'{'}, 163 SpacesBefore: 1, 164 }, 165 { 166 Type: hclsyntax.TokenCBrace, 167 Bytes: []byte{'}'}, 168 SpacesBefore: 0, 169 }, 170 { 171 Type: hclsyntax.TokenEOF, 172 Bytes: []byte{}, 173 SpacesBefore: 0, 174 }, 175 }, 176 }, 177 } 178 179 for _, test := range tests { 180 t.Run(fmt.Sprintf("%s %s %s in %s", test.oldTypeName, test.newTypeName, test.labels, test.src), func(t *testing.T) { 181 f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) 182 if len(diags) != 0 { 183 for _, diag := range diags { 184 t.Logf("- %s", diag.Error()) 185 } 186 t.Fatalf("unexpected diagnostics") 187 } 188 189 b := f.Body().FirstMatchingBlock(test.oldTypeName, test.labels) 190 b.SetType(test.newTypeName) 191 got := f.BuildTokens(nil) 192 format(got) 193 if !reflect.DeepEqual(got, test.want) { 194 diff := cmp.Diff(test.want, got) 195 t.Errorf("wrong result\ngot: %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(test.want), diff) 196 } 197 }) 198 } 199 } 200 201 func TestBlockSetLabels(t *testing.T) { 202 tests := []struct { 203 src string 204 typeName string 205 oldLabels []string 206 newLabels []string 207 want Tokens 208 }{ 209 { 210 `foo "hoge" {}`, 211 "foo", 212 []string{"hoge"}, 213 []string{"fuga"}, // update first label 214 Tokens{ 215 { 216 Type: hclsyntax.TokenIdent, 217 Bytes: []byte(`foo`), 218 SpacesBefore: 0, 219 }, 220 { 221 Type: hclsyntax.TokenOQuote, 222 Bytes: []byte(`"`), 223 SpacesBefore: 1, 224 }, 225 { 226 Type: hclsyntax.TokenQuotedLit, 227 Bytes: []byte(`fuga`), 228 SpacesBefore: 0, 229 }, 230 { 231 Type: hclsyntax.TokenCQuote, 232 Bytes: []byte(`"`), 233 SpacesBefore: 0, 234 }, 235 { 236 Type: hclsyntax.TokenOBrace, 237 Bytes: []byte{'{'}, 238 SpacesBefore: 1, 239 }, 240 { 241 Type: hclsyntax.TokenCBrace, 242 Bytes: []byte{'}'}, 243 SpacesBefore: 0, 244 }, 245 { 246 Type: hclsyntax.TokenEOF, 247 Bytes: []byte{}, 248 SpacesBefore: 0, 249 }, 250 }, 251 }, 252 { 253 `foo "hoge" "fuga" {}`, 254 "foo", 255 []string{"hoge", "fuga"}, 256 []string{"hoge", "piyo"}, // update second label 257 Tokens{ 258 { 259 Type: hclsyntax.TokenIdent, 260 Bytes: []byte(`foo`), 261 SpacesBefore: 0, 262 }, 263 { 264 Type: hclsyntax.TokenOQuote, 265 Bytes: []byte(`"`), 266 SpacesBefore: 1, 267 }, 268 { 269 Type: hclsyntax.TokenQuotedLit, 270 Bytes: []byte(`hoge`), 271 SpacesBefore: 0, 272 }, 273 { 274 Type: hclsyntax.TokenCQuote, 275 Bytes: []byte(`"`), 276 SpacesBefore: 0, 277 }, 278 { 279 Type: hclsyntax.TokenOQuote, 280 Bytes: []byte(`"`), 281 SpacesBefore: 1, 282 }, 283 { 284 Type: hclsyntax.TokenQuotedLit, 285 Bytes: []byte(`piyo`), 286 SpacesBefore: 0, 287 }, 288 { 289 Type: hclsyntax.TokenCQuote, 290 Bytes: []byte(`"`), 291 SpacesBefore: 0, 292 }, 293 { 294 Type: hclsyntax.TokenOBrace, 295 Bytes: []byte{'{'}, 296 SpacesBefore: 1, 297 }, 298 { 299 Type: hclsyntax.TokenCBrace, 300 Bytes: []byte{'}'}, 301 SpacesBefore: 0, 302 }, 303 { 304 Type: hclsyntax.TokenEOF, 305 Bytes: []byte{}, 306 SpacesBefore: 0, 307 }, 308 }, 309 }, 310 { 311 `foo {}`, 312 "foo", 313 []string{}, 314 []string{"fuga"}, // insert a new label to empty list 315 Tokens{ 316 { 317 Type: hclsyntax.TokenIdent, 318 Bytes: []byte(`foo`), 319 SpacesBefore: 0, 320 }, 321 { 322 Type: hclsyntax.TokenOQuote, 323 Bytes: []byte(`"`), 324 SpacesBefore: 1, 325 }, 326 { 327 Type: hclsyntax.TokenQuotedLit, 328 Bytes: []byte(`fuga`), 329 SpacesBefore: 0, 330 }, 331 { 332 Type: hclsyntax.TokenCQuote, 333 Bytes: []byte(`"`), 334 SpacesBefore: 0, 335 }, 336 { 337 Type: hclsyntax.TokenOBrace, 338 Bytes: []byte{'{'}, 339 SpacesBefore: 1, 340 }, 341 { 342 Type: hclsyntax.TokenCBrace, 343 Bytes: []byte{'}'}, 344 SpacesBefore: 0, 345 }, 346 { 347 Type: hclsyntax.TokenEOF, 348 Bytes: []byte{}, 349 SpacesBefore: 0, 350 }, 351 }, 352 }, 353 { 354 `foo "hoge" {}`, 355 "foo", 356 []string{"hoge"}, 357 []string{}, // remove all labels 358 Tokens{ 359 { 360 Type: hclsyntax.TokenIdent, 361 Bytes: []byte(`foo`), 362 SpacesBefore: 0, 363 }, 364 { 365 Type: hclsyntax.TokenOBrace, 366 Bytes: []byte{'{'}, 367 SpacesBefore: 1, 368 }, 369 { 370 Type: hclsyntax.TokenCBrace, 371 Bytes: []byte{'}'}, 372 SpacesBefore: 0, 373 }, 374 { 375 Type: hclsyntax.TokenEOF, 376 Bytes: []byte{}, 377 SpacesBefore: 0, 378 }, 379 }, 380 }, 381 { 382 `foo "hoge" /* fuga */ "piyo" {}`, 383 "foo", 384 []string{"hoge", "piyo"}, 385 []string{"fuga"}, // force quoted form even if the old one is unquoted. 386 Tokens{ 387 { 388 Type: hclsyntax.TokenIdent, 389 Bytes: []byte(`foo`), 390 SpacesBefore: 0, 391 }, 392 { 393 Type: hclsyntax.TokenOQuote, 394 Bytes: []byte(`"`), 395 SpacesBefore: 1, 396 }, 397 { 398 Type: hclsyntax.TokenQuotedLit, 399 Bytes: []byte(`fuga`), 400 SpacesBefore: 0, 401 }, 402 { 403 Type: hclsyntax.TokenCQuote, 404 Bytes: []byte(`"`), 405 SpacesBefore: 0, 406 }, 407 { 408 Type: hclsyntax.TokenOBrace, 409 Bytes: []byte{'{'}, 410 SpacesBefore: 1, 411 }, 412 { 413 Type: hclsyntax.TokenCBrace, 414 Bytes: []byte{'}'}, 415 SpacesBefore: 0, 416 }, 417 { 418 Type: hclsyntax.TokenEOF, 419 Bytes: []byte{}, 420 SpacesBefore: 0, 421 }, 422 }, 423 }, 424 { 425 `foo "hoge" /* foo */ "" {}`, 426 "foo", 427 []string{"hoge", ""}, 428 []string{"fuga"}, // force quoted form even if the old one is unquoted. 429 Tokens{ 430 { 431 Type: hclsyntax.TokenIdent, 432 Bytes: []byte(`foo`), 433 SpacesBefore: 0, 434 }, 435 { 436 Type: hclsyntax.TokenOQuote, 437 Bytes: []byte(`"`), 438 SpacesBefore: 1, 439 }, 440 { 441 Type: hclsyntax.TokenQuotedLit, 442 Bytes: []byte(`fuga`), 443 SpacesBefore: 0, 444 }, 445 { 446 Type: hclsyntax.TokenCQuote, 447 Bytes: []byte(`"`), 448 SpacesBefore: 0, 449 }, 450 { 451 Type: hclsyntax.TokenOBrace, 452 Bytes: []byte{'{'}, 453 SpacesBefore: 1, 454 }, 455 { 456 Type: hclsyntax.TokenCBrace, 457 Bytes: []byte{'}'}, 458 SpacesBefore: 0, 459 }, 460 { 461 Type: hclsyntax.TokenEOF, 462 Bytes: []byte{}, 463 SpacesBefore: 0, 464 }, 465 }, 466 }, 467 } 468 469 for _, test := range tests { 470 t.Run(fmt.Sprintf("%s %s %s in %s", test.typeName, test.oldLabels, test.newLabels, test.src), func(t *testing.T) { 471 f, diags := ParseConfig([]byte(test.src), "", hcl.Pos{Line: 1, Column: 1}) 472 if len(diags) != 0 { 473 for _, diag := range diags { 474 t.Logf("- %s", diag.Error()) 475 } 476 t.Fatalf("unexpected diagnostics") 477 } 478 479 b := f.Body().FirstMatchingBlock(test.typeName, test.oldLabels) 480 b.SetLabels(test.newLabels) 481 got := f.BuildTokens(nil) 482 format(got) 483 if !reflect.DeepEqual(got, test.want) { 484 diff := cmp.Diff(test.want, got) 485 t.Errorf("wrong result\ngot: %s\nwant: %s\ndiff:\n%s", spew.Sdump(got), spew.Sdump(test.want), diff) 486 } 487 }) 488 } 489 }