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