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  }