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  }