github.com/terraform-modules-krish/terratest@v0.29.0/modules/terraform/format_test.go (about)

     1  package terraform
     2  
     3  import (
     4  	"fmt"
     5  	"testing"
     6  
     7  	"github.com/stretchr/testify/assert"
     8  )
     9  
    10  func TestFormatTerraformVarsAsArgs(t *testing.T) {
    11  	t.Parallel()
    12  
    13  	testCases := []struct {
    14  		vars     map[string]interface{}
    15  		expected []string
    16  	}{
    17  		{map[string]interface{}{}, nil},
    18  		{map[string]interface{}{"foo": "bar"}, []string{"-var", "foo=bar"}},
    19  		{map[string]interface{}{"foo": 123}, []string{"-var", "foo=123"}},
    20  		{map[string]interface{}{"foo": true}, []string{"-var", "foo=true"}},
    21  		{map[string]interface{}{"foo": nil}, []string{"-var", "foo=null"}},
    22  		{map[string]interface{}{"foo": []int{1, 2, 3}}, []string{"-var", "foo=[1, 2, 3]"}},
    23  		{map[string]interface{}{"foo": map[string]string{"baz": "blah"}}, []string{"-var", "foo={\"baz\" = \"blah\"}"}},
    24  		{
    25  			map[string]interface{}{"str": "bar", "int": -1, "bool": false, "list": []string{"foo", "bar", "baz"}, "map": map[string]int{"foo": 0}},
    26  			[]string{"-var", "str=bar", "-var", "int=-1", "-var", "bool=false", "-var", "list=[\"foo\", \"bar\", \"baz\"]", "-var", "map={\"foo\" = 0}"},
    27  		},
    28  	}
    29  
    30  	for _, testCase := range testCases {
    31  		checkResultWithRetry(t, 100, testCase.expected, fmt.Sprintf("FormatTerraformVarsAsArgs(%v)", testCase.vars), func() interface{} {
    32  			return FormatTerraformVarsAsArgs(testCase.vars)
    33  		})
    34  	}
    35  }
    36  
    37  func TestPrimitiveToHclString(t *testing.T) {
    38  	t.Parallel()
    39  
    40  	testCases := []struct {
    41  		value    interface{}
    42  		expected string
    43  	}{
    44  		{"", ""},
    45  		{"foo", "foo"},
    46  		{"true", "true"},
    47  		{true, "true"},
    48  		{3, "3"},
    49  		{nil, "null"},
    50  		{[]int{1, 2, 3}, "[1 2 3]"}, // Anything that isn't a primitive is forced into a string
    51  	}
    52  
    53  	for _, testCase := range testCases {
    54  		actual := primitiveToHclString(testCase.value, false)
    55  		assert.Equal(t, testCase.expected, actual, "Value: %v", testCase.value)
    56  	}
    57  }
    58  
    59  func TestMapToHclString(t *testing.T) {
    60  	t.Parallel()
    61  
    62  	testCases := []struct {
    63  		value    map[string]interface{}
    64  		expected string
    65  	}{
    66  		{map[string]interface{}{}, "{}"},
    67  		{map[string]interface{}{"key1": "value1"}, "{\"key1\" = \"value1\"}"},
    68  		{map[string]interface{}{"key1": 123}, "{\"key1\" = 123}"},
    69  		{map[string]interface{}{"key1": true}, "{\"key1\" = true}"},
    70  		{map[string]interface{}{"key1": []int{1, 2, 3}}, "{\"key1\" = [1, 2, 3]}"}, // Any value that isn't a primitive is forced into a string
    71  		{map[string]interface{}{"key1": "value1", "key2": 0, "key3": false}, "{\"key1\" = \"value1\", \"key2\" = 0, \"key3\" = false}"},
    72  		{map[string]interface{}{"key1.a.b.c": "value1"}, "{\"key1.a.b.c\" = \"value1\"}"},
    73  	}
    74  
    75  	for _, testCase := range testCases {
    76  		checkResultWithRetry(t, 100, testCase.expected, fmt.Sprintf("mapToHclString(%v)", testCase.value), func() interface{} {
    77  			return mapToHclString(testCase.value)
    78  		})
    79  	}
    80  }
    81  
    82  // Some of our tests execute code that loops over a map to produce output. The problem is that the order of map
    83  // iteration is generally unpredictable and, to make it even more unpredictable, Go intentionally randomizes the
    84  // iteration order (https://blog.golang.org/go-maps-in-action#TOC_7). Therefore, the order of items in the output
    85  // is unpredictable, and doing a simple assert.Equals call will intermittently fail.
    86  //
    87  // We have a few unsatisfactory ways to solve this problem:
    88  //
    89  // 1. Enforce iteration order. This is easy to do in other languages, where you have built-in sorted maps. In Go, no
    90  //    such map exists, and if you create a custom one, you can't use the range keyword on it
    91  //    (http://stackoverflow.com/a/35810932/483528). As a result, we'd have to modify our implementation code to take
    92  //    iteration order into account which is a totally unnecessary feature that increases complexity.
    93  // 2. We could parse the output string and do an order-independent comparison. However, that adds a bunch of parsing
    94  //    logic into the test code which is a totally unnecessary feature that increases complexity.
    95  // 3. We accept that Go is a shitty language and, if the test fails, we re-run it a bunch of times in the hope that, if
    96  //    the bug is caused by key ordering, we will randomly get the proper order in a future run. The code being tested
    97  //    here is tiny & fast, so doing a hundred retries is still sub millisecond, so while ugly, this provides a very
    98  //    simple solution.
    99  //
   100  // Isn't it great that Go's designers built features into the language to prevent bugs that now force every Go
   101  // developer to write thousands of lines of extra code like this, which is of course likely to contain bugs itself?
   102  func checkResultWithRetry(t *testing.T, maxRetries int, expectedValue interface{}, description string, generateValue func() interface{}) {
   103  	for i := 0; i < maxRetries; i++ {
   104  		actualValue := generateValue()
   105  		if assert.ObjectsAreEqual(expectedValue, actualValue) {
   106  			return
   107  		}
   108  		t.Logf("Retry %d of %s failed: expected %v, got %v", i, description, expectedValue, actualValue)
   109  	}
   110  
   111  	assert.Fail(t, "checkResultWithRetry failed", "After %d retries, %s still not succeeding (see retries above)", description)
   112  }
   113  
   114  func TestSliceToHclString(t *testing.T) {
   115  	t.Parallel()
   116  
   117  	testCases := []struct {
   118  		value    []interface{}
   119  		expected string
   120  	}{
   121  		{[]interface{}{}, "[]"},
   122  		{[]interface{}{"foo"}, "[\"foo\"]"},
   123  		{[]interface{}{123}, "[123]"},
   124  		{[]interface{}{true}, "[true]"},
   125  		{[]interface{}{[]int{1, 2, 3}}, "[[1, 2, 3]]"}, // Any value that isn't a primitive is forced into a string
   126  		{[]interface{}{"foo", 0, false}, "[\"foo\", 0, false]"},
   127  		{[]interface{}{map[string]interface{}{"foo": "bar"}}, "[{\"foo\" = \"bar\"}]"},
   128  		{[]interface{}{map[string]interface{}{"foo": "bar"}, map[string]interface{}{"foo": "bar"}}, "[{\"foo\" = \"bar\"}, {\"foo\" = \"bar\"}]"},
   129  	}
   130  
   131  	for _, testCase := range testCases {
   132  		actual := sliceToHclString(testCase.value)
   133  		assert.Equal(t, testCase.expected, actual, "Value: %v", testCase.value)
   134  	}
   135  }
   136  
   137  func TestToHclString(t *testing.T) {
   138  	t.Parallel()
   139  
   140  	testCases := []struct {
   141  		value    interface{}
   142  		expected string
   143  	}{
   144  		{"", ""},
   145  		{"foo", "foo"},
   146  		{123, "123"},
   147  		{true, "true"},
   148  		{[]int{1, 2, 3}, "[1, 2, 3]"},
   149  		{[]string{"foo", "bar", "baz"}, "[\"foo\", \"bar\", \"baz\"]"},
   150  		{map[string]string{"key1": "value1"}, "{\"key1\" = \"value1\"}"},
   151  		{map[string]int{"key1": 123}, "{\"key1\" = 123}"},
   152  	}
   153  
   154  	for _, testCase := range testCases {
   155  		actual := toHclString(testCase.value, false)
   156  		assert.Equal(t, testCase.expected, actual, "Value: %v", testCase.value)
   157  	}
   158  }
   159  
   160  func TestTryToConvertToGenericSlice(t *testing.T) {
   161  	t.Parallel()
   162  
   163  	testCases := []struct {
   164  		value           interface{}
   165  		expectedSlice   []interface{}
   166  		expectedIsSlice bool
   167  	}{
   168  		{"", []interface{}{}, false},
   169  		{"foo", []interface{}{}, false},
   170  		{true, []interface{}{}, false},
   171  		{531, []interface{}{}, false},
   172  		{map[string]string{"foo": "bar"}, []interface{}{}, false},
   173  		{[]string{}, []interface{}{}, true},
   174  		{[]int{}, []interface{}{}, true},
   175  		{[]bool{}, []interface{}{}, true},
   176  		{[]interface{}{}, []interface{}{}, true},
   177  		{[]string{"foo", "bar", "baz"}, []interface{}{"foo", "bar", "baz"}, true},
   178  		{[]int{1, 2, 3}, []interface{}{1, 2, 3}, true},
   179  		{[]bool{true, true, false}, []interface{}{true, true, false}, true},
   180  		{[]interface{}{"foo", "bar", "baz"}, []interface{}{"foo", "bar", "baz"}, true},
   181  	}
   182  
   183  	for _, testCase := range testCases {
   184  		actualSlice, actualIsSlice := tryToConvertToGenericSlice(testCase.value)
   185  		assert.Equal(t, testCase.expectedSlice, actualSlice, "Value: %v", testCase.value)
   186  		assert.Equal(t, testCase.expectedIsSlice, actualIsSlice, "Value: %v", testCase.value)
   187  	}
   188  }
   189  
   190  func TestTryToConvertToGenericMap(t *testing.T) {
   191  	t.Parallel()
   192  
   193  	testCases := []struct {
   194  		value         interface{}
   195  		expectedMap   map[string]interface{}
   196  		expectedIsMap bool
   197  	}{
   198  		{"", map[string]interface{}{}, false},
   199  		{"foo", map[string]interface{}{}, false},
   200  		{true, map[string]interface{}{}, false},
   201  		{531, map[string]interface{}{}, false},
   202  		{[]string{"foo", "bar"}, map[string]interface{}{}, false},
   203  		{map[int]int{}, map[string]interface{}{}, false},
   204  		{map[bool]string{}, map[string]interface{}{}, false},
   205  		{map[string]string{}, map[string]interface{}{}, true},
   206  		{map[string]int{}, map[string]interface{}{}, true},
   207  		{map[string]bool{}, map[string]interface{}{}, true},
   208  		{map[string]interface{}{}, map[string]interface{}{}, true},
   209  		{map[string]string{"key1": "value1", "key2": "value2"}, map[string]interface{}{"key1": "value1", "key2": "value2"}, true},
   210  		{map[string]int{"key1": 1, "key2": 2, "key3": 3}, map[string]interface{}{"key1": 1, "key2": 2, "key3": 3}, true},
   211  		{map[string]bool{"key1": true}, map[string]interface{}{"key1": true}, true},
   212  		{map[string]interface{}{"key1": "value1"}, map[string]interface{}{"key1": "value1"}, true},
   213  	}
   214  
   215  	for _, testCase := range testCases {
   216  		actualMap, actualIsMap := tryToConvertToGenericMap(testCase.value)
   217  		assert.Equal(t, testCase.expectedMap, actualMap, "Value: %v", testCase.value)
   218  		assert.Equal(t, testCase.expectedIsMap, actualIsMap, "Value: %v", testCase.value)
   219  	}
   220  }