github.com/hashicorp/terraform-plugin-sdk@v1.17.2/internal/plugin/convert/diagnostics_test.go (about)

     1  package convert
     2  
     3  import (
     4  	"errors"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"github.com/hashicorp/terraform-plugin-sdk/internal/tfdiags"
     9  	proto "github.com/hashicorp/terraform-plugin-sdk/internal/tfplugin5"
    10  	"github.com/zclconf/go-cty/cty"
    11  )
    12  
    13  func TestProtoDiagnostics(t *testing.T) {
    14  	diags := WarnsAndErrsToProto(
    15  		[]string{
    16  			"warning 1",
    17  			"warning 2",
    18  		},
    19  		[]error{
    20  			errors.New("error 1"),
    21  			errors.New("error 2"),
    22  		},
    23  	)
    24  
    25  	expected := []*proto.Diagnostic{
    26  		{
    27  			Severity: proto.Diagnostic_WARNING,
    28  			Summary:  "warning 1",
    29  		},
    30  		{
    31  			Severity: proto.Diagnostic_WARNING,
    32  			Summary:  "warning 2",
    33  		},
    34  		{
    35  			Severity: proto.Diagnostic_ERROR,
    36  			Summary:  "error 1",
    37  		},
    38  		{
    39  			Severity: proto.Diagnostic_ERROR,
    40  			Summary:  "error 2",
    41  		},
    42  	}
    43  
    44  	if !cmp.Equal(expected, diags) {
    45  		t.Fatal(cmp.Diff(expected, diags))
    46  	}
    47  }
    48  
    49  func TestDiagnostics(t *testing.T) {
    50  	type diagFlat struct {
    51  		Severity tfdiags.Severity
    52  		Attr     []interface{}
    53  		Summary  string
    54  		Detail   string
    55  	}
    56  
    57  	tests := map[string]struct {
    58  		Cons func([]*proto.Diagnostic) []*proto.Diagnostic
    59  		Want []diagFlat
    60  	}{
    61  		"nil": {
    62  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
    63  				return diags
    64  			},
    65  			nil,
    66  		},
    67  		"error": {
    68  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
    69  				return append(diags, &proto.Diagnostic{
    70  					Severity: proto.Diagnostic_ERROR,
    71  					Summary:  "simple error",
    72  				})
    73  			},
    74  			[]diagFlat{
    75  				{
    76  					Severity: tfdiags.Error,
    77  					Summary:  "simple error",
    78  				},
    79  			},
    80  		},
    81  		"detailed error": {
    82  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
    83  				return append(diags, &proto.Diagnostic{
    84  					Severity: proto.Diagnostic_ERROR,
    85  					Summary:  "simple error",
    86  					Detail:   "detailed error",
    87  				})
    88  			},
    89  			[]diagFlat{
    90  				{
    91  					Severity: tfdiags.Error,
    92  					Summary:  "simple error",
    93  					Detail:   "detailed error",
    94  				},
    95  			},
    96  		},
    97  		"warning": {
    98  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
    99  				return append(diags, &proto.Diagnostic{
   100  					Severity: proto.Diagnostic_WARNING,
   101  					Summary:  "simple warning",
   102  				})
   103  			},
   104  			[]diagFlat{
   105  				{
   106  					Severity: tfdiags.Warning,
   107  					Summary:  "simple warning",
   108  				},
   109  			},
   110  		},
   111  		"detailed warning": {
   112  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
   113  				return append(diags, &proto.Diagnostic{
   114  					Severity: proto.Diagnostic_WARNING,
   115  					Summary:  "simple warning",
   116  					Detail:   "detailed warning",
   117  				})
   118  			},
   119  			[]diagFlat{
   120  				{
   121  					Severity: tfdiags.Warning,
   122  					Summary:  "simple warning",
   123  					Detail:   "detailed warning",
   124  				},
   125  			},
   126  		},
   127  		"multi error": {
   128  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
   129  				diags = append(diags, &proto.Diagnostic{
   130  					Severity: proto.Diagnostic_ERROR,
   131  					Summary:  "first error",
   132  				}, &proto.Diagnostic{
   133  					Severity: proto.Diagnostic_ERROR,
   134  					Summary:  "second error",
   135  				})
   136  				return diags
   137  			},
   138  			[]diagFlat{
   139  				{
   140  					Severity: tfdiags.Error,
   141  					Summary:  "first error",
   142  				},
   143  				{
   144  					Severity: tfdiags.Error,
   145  					Summary:  "second error",
   146  				},
   147  			},
   148  		},
   149  		"warning and error": {
   150  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
   151  				diags = append(diags, &proto.Diagnostic{
   152  					Severity: proto.Diagnostic_WARNING,
   153  					Summary:  "warning",
   154  				}, &proto.Diagnostic{
   155  					Severity: proto.Diagnostic_ERROR,
   156  					Summary:  "error",
   157  				})
   158  				return diags
   159  			},
   160  			[]diagFlat{
   161  				{
   162  					Severity: tfdiags.Warning,
   163  					Summary:  "warning",
   164  				},
   165  				{
   166  					Severity: tfdiags.Error,
   167  					Summary:  "error",
   168  				},
   169  			},
   170  		},
   171  		"attr error": {
   172  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
   173  				diags = append(diags, &proto.Diagnostic{
   174  					Severity: proto.Diagnostic_ERROR,
   175  					Summary:  "error",
   176  					Detail:   "error detail",
   177  					Attribute: &proto.AttributePath{
   178  						Steps: []*proto.AttributePath_Step{
   179  							{
   180  								Selector: &proto.AttributePath_Step_AttributeName{
   181  									AttributeName: "attribute_name",
   182  								},
   183  							},
   184  						},
   185  					},
   186  				})
   187  				return diags
   188  			},
   189  			[]diagFlat{
   190  				{
   191  					Severity: tfdiags.Error,
   192  					Summary:  "error",
   193  					Detail:   "error detail",
   194  					Attr:     []interface{}{"attribute_name"},
   195  				},
   196  			},
   197  		},
   198  		"multi attr": {
   199  			func(diags []*proto.Diagnostic) []*proto.Diagnostic {
   200  				diags = append(diags,
   201  					&proto.Diagnostic{
   202  						Severity: proto.Diagnostic_ERROR,
   203  						Summary:  "error 1",
   204  						Detail:   "error 1 detail",
   205  						Attribute: &proto.AttributePath{
   206  							Steps: []*proto.AttributePath_Step{
   207  								{
   208  									Selector: &proto.AttributePath_Step_AttributeName{
   209  										AttributeName: "attr",
   210  									},
   211  								},
   212  							},
   213  						},
   214  					},
   215  					&proto.Diagnostic{
   216  						Severity: proto.Diagnostic_ERROR,
   217  						Summary:  "error 2",
   218  						Detail:   "error 2 detail",
   219  						Attribute: &proto.AttributePath{
   220  							Steps: []*proto.AttributePath_Step{
   221  								{
   222  									Selector: &proto.AttributePath_Step_AttributeName{
   223  										AttributeName: "attr",
   224  									},
   225  								},
   226  								{
   227  									Selector: &proto.AttributePath_Step_AttributeName{
   228  										AttributeName: "sub",
   229  									},
   230  								},
   231  							},
   232  						},
   233  					},
   234  					&proto.Diagnostic{
   235  						Severity: proto.Diagnostic_WARNING,
   236  						Summary:  "warning",
   237  						Detail:   "warning detail",
   238  						Attribute: &proto.AttributePath{
   239  							Steps: []*proto.AttributePath_Step{
   240  								{
   241  									Selector: &proto.AttributePath_Step_AttributeName{
   242  										AttributeName: "attr",
   243  									},
   244  								},
   245  								{
   246  									Selector: &proto.AttributePath_Step_ElementKeyInt{
   247  										ElementKeyInt: 1,
   248  									},
   249  								},
   250  								{
   251  									Selector: &proto.AttributePath_Step_AttributeName{
   252  										AttributeName: "sub",
   253  									},
   254  								},
   255  							},
   256  						},
   257  					},
   258  					&proto.Diagnostic{
   259  						Severity: proto.Diagnostic_ERROR,
   260  						Summary:  "error 3",
   261  						Detail:   "error 3 detail",
   262  						Attribute: &proto.AttributePath{
   263  							Steps: []*proto.AttributePath_Step{
   264  								{
   265  									Selector: &proto.AttributePath_Step_AttributeName{
   266  										AttributeName: "attr",
   267  									},
   268  								},
   269  								{
   270  									Selector: &proto.AttributePath_Step_ElementKeyString{
   271  										ElementKeyString: "idx",
   272  									},
   273  								},
   274  								{
   275  									Selector: &proto.AttributePath_Step_AttributeName{
   276  										AttributeName: "sub",
   277  									},
   278  								},
   279  							},
   280  						},
   281  					},
   282  				)
   283  
   284  				return diags
   285  			},
   286  			[]diagFlat{
   287  				{
   288  					Severity: tfdiags.Error,
   289  					Summary:  "error 1",
   290  					Detail:   "error 1 detail",
   291  					Attr:     []interface{}{"attr"},
   292  				},
   293  				{
   294  					Severity: tfdiags.Error,
   295  					Summary:  "error 2",
   296  					Detail:   "error 2 detail",
   297  					Attr:     []interface{}{"attr", "sub"},
   298  				},
   299  				{
   300  					Severity: tfdiags.Warning,
   301  					Summary:  "warning",
   302  					Detail:   "warning detail",
   303  					Attr:     []interface{}{"attr", 1, "sub"},
   304  				},
   305  				{
   306  					Severity: tfdiags.Error,
   307  					Summary:  "error 3",
   308  					Detail:   "error 3 detail",
   309  					Attr:     []interface{}{"attr", "idx", "sub"},
   310  				},
   311  			},
   312  		},
   313  	}
   314  
   315  	flattenTFDiags := func(ds tfdiags.Diagnostics) []diagFlat {
   316  		var flat []diagFlat
   317  		for _, item := range ds {
   318  			desc := item.Description()
   319  
   320  			var attr []interface{}
   321  
   322  			for _, a := range tfdiags.GetAttribute(item) {
   323  				switch step := a.(type) {
   324  				case cty.GetAttrStep:
   325  					attr = append(attr, step.Name)
   326  				case cty.IndexStep:
   327  					switch step.Key.Type() {
   328  					case cty.Number:
   329  						i, _ := step.Key.AsBigFloat().Int64()
   330  						attr = append(attr, int(i))
   331  					case cty.String:
   332  						attr = append(attr, step.Key.AsString())
   333  					}
   334  				}
   335  			}
   336  
   337  			flat = append(flat, diagFlat{
   338  				Severity: item.Severity(),
   339  				Attr:     attr,
   340  				Summary:  desc.Summary,
   341  				Detail:   desc.Detail,
   342  			})
   343  		}
   344  		return flat
   345  	}
   346  
   347  	for name, tc := range tests {
   348  		t.Run(name, func(t *testing.T) {
   349  			// we take the
   350  			tfDiags := ProtoToDiagnostics(tc.Cons(nil))
   351  
   352  			flat := flattenTFDiags(tfDiags)
   353  
   354  			if !cmp.Equal(flat, tc.Want, typeComparer, valueComparer, equateEmpty) {
   355  				t.Fatal(cmp.Diff(flat, tc.Want, typeComparer, valueComparer, equateEmpty))
   356  			}
   357  		})
   358  	}
   359  }