github.com/terramate-io/tf@v0.0.0-20230830114523-fce866b4dfcd/command/jsonformat/computed/renderers/testing.go (about) 1 // Copyright (c) HashiCorp, Inc. 2 // SPDX-License-Identifier: MPL-2.0 3 4 package renderers 5 6 import ( 7 "sort" 8 "testing" 9 10 "github.com/google/go-cmp/cmp" 11 12 "github.com/terramate-io/tf/command/jsonformat/computed" 13 "github.com/terramate-io/tf/plans" 14 ) 15 16 type ValidateDiffFunction func(t *testing.T, diff computed.Diff) 17 18 func validateDiff(t *testing.T, diff computed.Diff, expectedAction plans.Action, expectedReplace bool) { 19 if diff.Replace != expectedReplace || diff.Action != expectedAction { 20 t.Errorf("\nreplace:\n\texpected:%t\n\tactual:%t\naction:\n\texpected:%s\n\tactual:%s", expectedReplace, diff.Replace, expectedAction, diff.Action) 21 } 22 } 23 24 func ValidatePrimitive(before, after interface{}, action plans.Action, replace bool) ValidateDiffFunction { 25 return func(t *testing.T, diff computed.Diff) { 26 validateDiff(t, diff, action, replace) 27 28 primitive, ok := diff.Renderer.(*primitiveRenderer) 29 if !ok { 30 t.Errorf("invalid renderer type: %T", diff.Renderer) 31 return 32 } 33 34 beforeDiff := cmp.Diff(primitive.before, before) 35 afterDiff := cmp.Diff(primitive.after, after) 36 37 if len(beforeDiff) > 0 || len(afterDiff) > 0 { 38 t.Errorf("before diff: (%s), after diff: (%s)", beforeDiff, afterDiff) 39 } 40 } 41 } 42 43 func ValidateObject(attributes map[string]ValidateDiffFunction, action plans.Action, replace bool) ValidateDiffFunction { 44 return func(t *testing.T, diff computed.Diff) { 45 validateDiff(t, diff, action, replace) 46 47 object, ok := diff.Renderer.(*objectRenderer) 48 if !ok { 49 t.Errorf("invalid renderer type: %T", diff.Renderer) 50 return 51 } 52 53 if !object.overrideNullSuffix { 54 t.Errorf("created the wrong type of object renderer") 55 } 56 57 validateMapType(t, object.attributes, attributes) 58 } 59 } 60 61 func ValidateNestedObject(attributes map[string]ValidateDiffFunction, action plans.Action, replace bool) ValidateDiffFunction { 62 return func(t *testing.T, diff computed.Diff) { 63 validateDiff(t, diff, action, replace) 64 65 object, ok := diff.Renderer.(*objectRenderer) 66 if !ok { 67 t.Errorf("invalid renderer type: %T", diff.Renderer) 68 return 69 } 70 71 if object.overrideNullSuffix { 72 t.Errorf("created the wrong type of object renderer") 73 } 74 75 validateMapType(t, object.attributes, attributes) 76 } 77 } 78 79 func ValidateMap(elements map[string]ValidateDiffFunction, action plans.Action, replace bool) ValidateDiffFunction { 80 return func(t *testing.T, diff computed.Diff) { 81 validateDiff(t, diff, action, replace) 82 83 m, ok := diff.Renderer.(*mapRenderer) 84 if !ok { 85 t.Errorf("invalid renderer type: %T", diff.Renderer) 86 return 87 } 88 89 validateMapType(t, m.elements, elements) 90 } 91 } 92 93 func validateMapType(t *testing.T, actual map[string]computed.Diff, expected map[string]ValidateDiffFunction) { 94 validateKeys(t, actual, expected) 95 96 for key, expected := range expected { 97 if actual, ok := actual[key]; ok { 98 expected(t, actual) 99 } 100 } 101 } 102 103 func validateKeys[C, V any](t *testing.T, actual map[string]C, expected map[string]V) { 104 if len(actual) != len(expected) { 105 106 var actualAttributes []string 107 var expectedAttributes []string 108 109 for key := range actual { 110 actualAttributes = append(actualAttributes, key) 111 } 112 for key := range expected { 113 expectedAttributes = append(expectedAttributes, key) 114 } 115 116 sort.Strings(actualAttributes) 117 sort.Strings(expectedAttributes) 118 119 if diff := cmp.Diff(actualAttributes, expectedAttributes); len(diff) > 0 { 120 t.Errorf("actual and expected attributes did not match: %s", diff) 121 } 122 } 123 } 124 125 func ValidateList(elements []ValidateDiffFunction, action plans.Action, replace bool) ValidateDiffFunction { 126 return func(t *testing.T, diff computed.Diff) { 127 validateDiff(t, diff, action, replace) 128 129 list, ok := diff.Renderer.(*listRenderer) 130 if !ok { 131 t.Errorf("invalid renderer type: %T", diff.Renderer) 132 return 133 } 134 135 if !list.displayContext { 136 t.Errorf("created the wrong type of list renderer") 137 } 138 139 validateSliceType(t, list.elements, elements) 140 } 141 } 142 143 func ValidateNestedList(elements []ValidateDiffFunction, action plans.Action, replace bool) ValidateDiffFunction { 144 return func(t *testing.T, diff computed.Diff) { 145 validateDiff(t, diff, action, replace) 146 147 list, ok := diff.Renderer.(*listRenderer) 148 if !ok { 149 t.Errorf("invalid renderer type: %T", diff.Renderer) 150 return 151 } 152 153 if list.displayContext { 154 t.Errorf("created the wrong type of list renderer") 155 } 156 157 validateSliceType(t, list.elements, elements) 158 } 159 } 160 161 func ValidateSet(elements []ValidateDiffFunction, action plans.Action, replace bool) ValidateDiffFunction { 162 return func(t *testing.T, diff computed.Diff) { 163 validateDiff(t, diff, action, replace) 164 165 set, ok := diff.Renderer.(*setRenderer) 166 if !ok { 167 t.Errorf("invalid renderer type: %T", diff.Renderer) 168 return 169 } 170 171 validateSliceType(t, set.elements, elements) 172 } 173 } 174 175 func validateSliceType(t *testing.T, actual []computed.Diff, expected []ValidateDiffFunction) { 176 if len(actual) != len(expected) { 177 t.Errorf("expected %d elements but found %d elements", len(expected), len(actual)) 178 return 179 } 180 181 for ix := 0; ix < len(expected); ix++ { 182 expected[ix](t, actual[ix]) 183 } 184 } 185 186 func ValidateBlock( 187 attributes map[string]ValidateDiffFunction, 188 singleBlocks map[string]ValidateDiffFunction, 189 listBlocks map[string][]ValidateDiffFunction, 190 mapBlocks map[string]map[string]ValidateDiffFunction, 191 setBlocks map[string][]ValidateDiffFunction, 192 action plans.Action, 193 replace bool) ValidateDiffFunction { 194 return func(t *testing.T, diff computed.Diff) { 195 validateDiff(t, diff, action, replace) 196 197 block, ok := diff.Renderer.(*blockRenderer) 198 if !ok { 199 t.Errorf("invalid renderer type: %T", diff.Renderer) 200 return 201 } 202 203 validateKeys(t, block.attributes, attributes) 204 validateKeys(t, block.blocks.SingleBlocks, singleBlocks) 205 validateKeys(t, block.blocks.ListBlocks, listBlocks) 206 validateKeys(t, block.blocks.MapBlocks, mapBlocks) 207 validateKeys(t, block.blocks.SetBlocks, setBlocks) 208 209 for key, expected := range attributes { 210 if actual, ok := block.attributes[key]; ok { 211 expected(t, actual) 212 } 213 } 214 215 for key, expected := range singleBlocks { 216 expected(t, block.blocks.SingleBlocks[key]) 217 } 218 219 for key, expected := range listBlocks { 220 if actual, ok := block.blocks.ListBlocks[key]; ok { 221 if len(actual) != len(expected) { 222 t.Errorf("expected %d blocks within %s but found %d elements", len(expected), key, len(actual)) 223 } 224 for ix := range expected { 225 expected[ix](t, actual[ix]) 226 } 227 } 228 } 229 230 for key, expected := range setBlocks { 231 if actual, ok := block.blocks.SetBlocks[key]; ok { 232 if len(actual) != len(expected) { 233 t.Errorf("expected %d blocks within %s but found %d elements", len(expected), key, len(actual)) 234 } 235 for ix := range expected { 236 expected[ix](t, actual[ix]) 237 } 238 } 239 } 240 241 for key, expected := range setBlocks { 242 if actual, ok := block.blocks.SetBlocks[key]; ok { 243 if len(actual) != len(expected) { 244 t.Errorf("expected %d blocks within %s but found %d elements", len(expected), key, len(actual)) 245 } 246 for ix := range expected { 247 expected[ix](t, actual[ix]) 248 } 249 } 250 } 251 252 for key, expected := range mapBlocks { 253 if actual, ok := block.blocks.MapBlocks[key]; ok { 254 if len(actual) != len(expected) { 255 t.Errorf("expected %d blocks within %s but found %d elements", len(expected), key, len(actual)) 256 } 257 for dKey := range expected { 258 expected[dKey](t, actual[dKey]) 259 } 260 } 261 } 262 } 263 } 264 265 func ValidateTypeChange(before, after ValidateDiffFunction, action plans.Action, replace bool) ValidateDiffFunction { 266 return func(t *testing.T, diff computed.Diff) { 267 validateDiff(t, diff, action, replace) 268 269 typeChange, ok := diff.Renderer.(*typeChangeRenderer) 270 if !ok { 271 t.Errorf("invalid renderer type: %T", diff.Renderer) 272 return 273 } 274 275 before(t, typeChange.before) 276 after(t, typeChange.after) 277 } 278 } 279 280 func ValidateSensitive(inner ValidateDiffFunction, beforeSensitive, afterSensitive bool, action plans.Action, replace bool) ValidateDiffFunction { 281 return func(t *testing.T, diff computed.Diff) { 282 validateDiff(t, diff, action, replace) 283 284 sensitive, ok := diff.Renderer.(*sensitiveRenderer) 285 if !ok { 286 t.Errorf("invalid renderer type: %T", diff.Renderer) 287 return 288 } 289 290 if beforeSensitive != sensitive.beforeSensitive || afterSensitive != sensitive.afterSensitive { 291 t.Errorf("before or after sensitive values don't match:\n\texpected; before: %t after: %t\n\tactual; before: %t, after: %t", beforeSensitive, afterSensitive, sensitive.beforeSensitive, sensitive.afterSensitive) 292 } 293 294 inner(t, sensitive.inner) 295 } 296 } 297 298 func ValidateUnknown(before ValidateDiffFunction, action plans.Action, replace bool) ValidateDiffFunction { 299 return func(t *testing.T, diff computed.Diff) { 300 validateDiff(t, diff, action, replace) 301 302 unknown, ok := diff.Renderer.(*unknownRenderer) 303 if !ok { 304 t.Errorf("invalid renderer type: %T", diff.Renderer) 305 return 306 } 307 308 if before == nil { 309 if unknown.before.Renderer != nil { 310 t.Errorf("did not expect a before renderer, but found one") 311 } 312 return 313 } 314 315 if unknown.before.Renderer == nil { 316 t.Errorf("expected a before renderer, but found none") 317 } 318 319 before(t, unknown.before) 320 } 321 }