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