github.com/eliastor/durgaform@v0.0.0-20220816172711-d0ab2d17673e/internal/addrs/parse_target_test.go (about)

     1  package addrs
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/go-test/deep"
     7  	"github.com/hashicorp/hcl/v2"
     8  	"github.com/hashicorp/hcl/v2/hclsyntax"
     9  	"github.com/eliastor/durgaform/internal/tfdiags"
    10  )
    11  
    12  func TestParseTarget(t *testing.T) {
    13  	tests := []struct {
    14  		Input   string
    15  		Want    *Target
    16  		WantErr string
    17  	}{
    18  		{
    19  			`module.foo`,
    20  			&Target{
    21  				Subject: ModuleInstance{
    22  					{
    23  						Name: "foo",
    24  					},
    25  				},
    26  				SourceRange: tfdiags.SourceRange{
    27  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
    28  					End:   tfdiags.SourcePos{Line: 1, Column: 11, Byte: 10},
    29  				},
    30  			},
    31  			``,
    32  		},
    33  		{
    34  			`module.foo[2]`,
    35  			&Target{
    36  				Subject: ModuleInstance{
    37  					{
    38  						Name:        "foo",
    39  						InstanceKey: IntKey(2),
    40  					},
    41  				},
    42  				SourceRange: tfdiags.SourceRange{
    43  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
    44  					End:   tfdiags.SourcePos{Line: 1, Column: 14, Byte: 13},
    45  				},
    46  			},
    47  			``,
    48  		},
    49  		{
    50  			`module.foo[2].module.bar`,
    51  			&Target{
    52  				Subject: ModuleInstance{
    53  					{
    54  						Name:        "foo",
    55  						InstanceKey: IntKey(2),
    56  					},
    57  					{
    58  						Name: "bar",
    59  					},
    60  				},
    61  				SourceRange: tfdiags.SourceRange{
    62  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
    63  					End:   tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
    64  				},
    65  			},
    66  			``,
    67  		},
    68  		{
    69  			`aws_instance.foo`,
    70  			&Target{
    71  				Subject: AbsResource{
    72  					Resource: Resource{
    73  						Mode: ManagedResourceMode,
    74  						Type: "aws_instance",
    75  						Name: "foo",
    76  					},
    77  					Module: RootModuleInstance,
    78  				},
    79  				SourceRange: tfdiags.SourceRange{
    80  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
    81  					End:   tfdiags.SourcePos{Line: 1, Column: 17, Byte: 16},
    82  				},
    83  			},
    84  			``,
    85  		},
    86  		{
    87  			`aws_instance.foo[1]`,
    88  			&Target{
    89  				Subject: AbsResourceInstance{
    90  					Resource: ResourceInstance{
    91  						Resource: Resource{
    92  							Mode: ManagedResourceMode,
    93  							Type: "aws_instance",
    94  							Name: "foo",
    95  						},
    96  						Key: IntKey(1),
    97  					},
    98  					Module: RootModuleInstance,
    99  				},
   100  				SourceRange: tfdiags.SourceRange{
   101  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   102  					End:   tfdiags.SourcePos{Line: 1, Column: 20, Byte: 19},
   103  				},
   104  			},
   105  			``,
   106  		},
   107  		{
   108  			`data.aws_instance.foo`,
   109  			&Target{
   110  				Subject: AbsResource{
   111  					Resource: Resource{
   112  						Mode: DataResourceMode,
   113  						Type: "aws_instance",
   114  						Name: "foo",
   115  					},
   116  					Module: RootModuleInstance,
   117  				},
   118  				SourceRange: tfdiags.SourceRange{
   119  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   120  					End:   tfdiags.SourcePos{Line: 1, Column: 22, Byte: 21},
   121  				},
   122  			},
   123  			``,
   124  		},
   125  		{
   126  			`data.aws_instance.foo[1]`,
   127  			&Target{
   128  				Subject: AbsResourceInstance{
   129  					Resource: ResourceInstance{
   130  						Resource: Resource{
   131  							Mode: DataResourceMode,
   132  							Type: "aws_instance",
   133  							Name: "foo",
   134  						},
   135  						Key: IntKey(1),
   136  					},
   137  					Module: RootModuleInstance,
   138  				},
   139  				SourceRange: tfdiags.SourceRange{
   140  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   141  					End:   tfdiags.SourcePos{Line: 1, Column: 25, Byte: 24},
   142  				},
   143  			},
   144  			``,
   145  		},
   146  		{
   147  			`module.foo.aws_instance.bar`,
   148  			&Target{
   149  				Subject: AbsResource{
   150  					Resource: Resource{
   151  						Mode: ManagedResourceMode,
   152  						Type: "aws_instance",
   153  						Name: "bar",
   154  					},
   155  					Module: ModuleInstance{
   156  						{Name: "foo"},
   157  					},
   158  				},
   159  				SourceRange: tfdiags.SourceRange{
   160  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   161  					End:   tfdiags.SourcePos{Line: 1, Column: 28, Byte: 27},
   162  				},
   163  			},
   164  			``,
   165  		},
   166  		{
   167  			`module.foo.module.bar.aws_instance.baz`,
   168  			&Target{
   169  				Subject: AbsResource{
   170  					Resource: Resource{
   171  						Mode: ManagedResourceMode,
   172  						Type: "aws_instance",
   173  						Name: "baz",
   174  					},
   175  					Module: ModuleInstance{
   176  						{Name: "foo"},
   177  						{Name: "bar"},
   178  					},
   179  				},
   180  				SourceRange: tfdiags.SourceRange{
   181  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   182  					End:   tfdiags.SourcePos{Line: 1, Column: 39, Byte: 38},
   183  				},
   184  			},
   185  			``,
   186  		},
   187  		{
   188  			`module.foo.module.bar.aws_instance.baz["hello"]`,
   189  			&Target{
   190  				Subject: AbsResourceInstance{
   191  					Resource: ResourceInstance{
   192  						Resource: Resource{
   193  							Mode: ManagedResourceMode,
   194  							Type: "aws_instance",
   195  							Name: "baz",
   196  						},
   197  						Key: StringKey("hello"),
   198  					},
   199  					Module: ModuleInstance{
   200  						{Name: "foo"},
   201  						{Name: "bar"},
   202  					},
   203  				},
   204  				SourceRange: tfdiags.SourceRange{
   205  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   206  					End:   tfdiags.SourcePos{Line: 1, Column: 48, Byte: 47},
   207  				},
   208  			},
   209  			``,
   210  		},
   211  		{
   212  			`module.foo.data.aws_instance.bar`,
   213  			&Target{
   214  				Subject: AbsResource{
   215  					Resource: Resource{
   216  						Mode: DataResourceMode,
   217  						Type: "aws_instance",
   218  						Name: "bar",
   219  					},
   220  					Module: ModuleInstance{
   221  						{Name: "foo"},
   222  					},
   223  				},
   224  				SourceRange: tfdiags.SourceRange{
   225  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   226  					End:   tfdiags.SourcePos{Line: 1, Column: 33, Byte: 32},
   227  				},
   228  			},
   229  			``,
   230  		},
   231  		{
   232  			`module.foo.module.bar.data.aws_instance.baz`,
   233  			&Target{
   234  				Subject: AbsResource{
   235  					Resource: Resource{
   236  						Mode: DataResourceMode,
   237  						Type: "aws_instance",
   238  						Name: "baz",
   239  					},
   240  					Module: ModuleInstance{
   241  						{Name: "foo"},
   242  						{Name: "bar"},
   243  					},
   244  				},
   245  				SourceRange: tfdiags.SourceRange{
   246  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   247  					End:   tfdiags.SourcePos{Line: 1, Column: 44, Byte: 43},
   248  				},
   249  			},
   250  			``,
   251  		},
   252  		{
   253  			`module.foo.module.bar[0].data.aws_instance.baz`,
   254  			&Target{
   255  				Subject: AbsResource{
   256  					Resource: Resource{
   257  						Mode: DataResourceMode,
   258  						Type: "aws_instance",
   259  						Name: "baz",
   260  					},
   261  					Module: ModuleInstance{
   262  						{Name: "foo", InstanceKey: NoKey},
   263  						{Name: "bar", InstanceKey: IntKey(0)},
   264  					},
   265  				},
   266  				SourceRange: tfdiags.SourceRange{
   267  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   268  					End:   tfdiags.SourcePos{Line: 1, Column: 47, Byte: 46},
   269  				},
   270  			},
   271  			``,
   272  		},
   273  		{
   274  			`module.foo.module.bar["a"].data.aws_instance.baz["hello"]`,
   275  			&Target{
   276  				Subject: AbsResourceInstance{
   277  					Resource: ResourceInstance{
   278  						Resource: Resource{
   279  							Mode: DataResourceMode,
   280  							Type: "aws_instance",
   281  							Name: "baz",
   282  						},
   283  						Key: StringKey("hello"),
   284  					},
   285  					Module: ModuleInstance{
   286  						{Name: "foo", InstanceKey: NoKey},
   287  						{Name: "bar", InstanceKey: StringKey("a")},
   288  					},
   289  				},
   290  				SourceRange: tfdiags.SourceRange{
   291  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   292  					End:   tfdiags.SourcePos{Line: 1, Column: 58, Byte: 57},
   293  				},
   294  			},
   295  			``,
   296  		},
   297  		{
   298  			`module.foo.module.bar.data.aws_instance.baz["hello"]`,
   299  			&Target{
   300  				Subject: AbsResourceInstance{
   301  					Resource: ResourceInstance{
   302  						Resource: Resource{
   303  							Mode: DataResourceMode,
   304  							Type: "aws_instance",
   305  							Name: "baz",
   306  						},
   307  						Key: StringKey("hello"),
   308  					},
   309  					Module: ModuleInstance{
   310  						{Name: "foo"},
   311  						{Name: "bar"},
   312  					},
   313  				},
   314  				SourceRange: tfdiags.SourceRange{
   315  					Start: tfdiags.SourcePos{Line: 1, Column: 1, Byte: 0},
   316  					End:   tfdiags.SourcePos{Line: 1, Column: 53, Byte: 52},
   317  				},
   318  			},
   319  			``,
   320  		},
   321  
   322  		{
   323  			`aws_instance`,
   324  			nil,
   325  			`Resource specification must include a resource type and name.`,
   326  		},
   327  		{
   328  			`module`,
   329  			nil,
   330  			`Prefix "module." must be followed by a module name.`,
   331  		},
   332  		{
   333  			`module["baz"]`,
   334  			nil,
   335  			`Prefix "module." must be followed by a module name.`,
   336  		},
   337  		{
   338  			`module.baz.bar`,
   339  			nil,
   340  			`Resource specification must include a resource type and name.`,
   341  		},
   342  		{
   343  			`aws_instance.foo.bar`,
   344  			nil,
   345  			`Resource instance key must be given in square brackets.`,
   346  		},
   347  		{
   348  			`aws_instance.foo[1].baz`,
   349  			nil,
   350  			`Unexpected extra operators after address.`,
   351  		},
   352  	}
   353  
   354  	for _, test := range tests {
   355  		t.Run(test.Input, func(t *testing.T) {
   356  			traversal, travDiags := hclsyntax.ParseTraversalAbs([]byte(test.Input), "", hcl.Pos{Line: 1, Column: 1})
   357  			if travDiags.HasErrors() {
   358  				t.Fatal(travDiags.Error())
   359  			}
   360  
   361  			got, diags := ParseTarget(traversal)
   362  
   363  			switch len(diags) {
   364  			case 0:
   365  				if test.WantErr != "" {
   366  					t.Fatalf("succeeded; want error: %s", test.WantErr)
   367  				}
   368  			case 1:
   369  				if test.WantErr == "" {
   370  					t.Fatalf("unexpected diagnostics: %s", diags.Err())
   371  				}
   372  				if got, want := diags[0].Description().Detail, test.WantErr; got != want {
   373  					t.Fatalf("wrong error\ngot:  %s\nwant: %s", got, want)
   374  				}
   375  			default:
   376  				t.Fatalf("too many diagnostics: %s", diags.Err())
   377  			}
   378  
   379  			if diags.HasErrors() {
   380  				return
   381  			}
   382  
   383  			for _, problem := range deep.Equal(got, test.Want) {
   384  				t.Errorf(problem)
   385  			}
   386  		})
   387  	}
   388  }