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 }