• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1package proptools
2
3import (
4	"strings"
5	"testing"
6)
7
8func mustHash(t *testing.T, data interface{}) uint64 {
9	t.Helper()
10	result, err := CalculateHash(data)
11	if err != nil {
12		t.Fatal(err)
13	}
14	return result
15}
16
17func TestHashingMapGetsSameResults(t *testing.T) {
18	data := map[string]string{"foo": "bar", "baz": "qux"}
19	first := mustHash(t, data)
20	second := mustHash(t, data)
21	third := mustHash(t, data)
22	fourth := mustHash(t, data)
23	if first != second || second != third || third != fourth {
24		t.Fatal("Did not get the same result every time for a map")
25	}
26}
27
28func TestHashingNonSerializableTypesFails(t *testing.T) {
29	testCases := []struct {
30		name string
31		data interface{}
32	}{
33		{
34			name: "function pointer",
35			data: []func(){nil},
36		},
37		{
38			name: "channel",
39			data: []chan int{make(chan int)},
40		},
41		{
42			name: "list with non-serializable type",
43			data: []interface{}{"foo", make(chan int)},
44		},
45	}
46	for _, testCase := range testCases {
47		t.Run(testCase.name, func(t *testing.T) {
48			_, err := CalculateHash(testCase)
49			if err == nil {
50				t.Fatal("Expected hashing error but didn't get one")
51			}
52			expected := "data may only contain primitives, strings, arrays, slices, structs, maps, and pointers"
53			if !strings.Contains(err.Error(), expected) {
54				t.Fatalf("Expected %q, got %q", expected, err.Error())
55			}
56		})
57	}
58}
59
60var hashTestCases = []struct {
61	name string
62	data interface{}
63}{
64	{
65		name: "int",
66		data: 5,
67	},
68	{
69		name: "string",
70		data: "foo",
71	},
72	{
73		name: "*string",
74		data: StringPtr("foo"),
75	},
76	{
77		name: "array",
78		data: [3]string{"foo", "bar", "baz"},
79	},
80	{
81		name: "slice",
82		data: []string{"foo", "bar", "baz"},
83	},
84	{
85		name: "struct",
86		data: struct {
87			foo string
88			bar int
89		}{
90			foo: "foo",
91			bar: 3,
92		},
93	},
94	{
95		name: "map",
96		data: map[string]int{
97			"foo": 3,
98			"bar": 4,
99		},
100	},
101	{
102		name: "list of interfaces with different types",
103		data: []interface{}{"foo", 3, []string{"bar", "baz"}},
104	},
105	{
106		name: "nested maps",
107		data: map[string]map[string]map[string]map[string]map[string]int{
108			"foo": {"foo": {"foo": {"foo": {"foo": 5}}}},
109		},
110	},
111	{
112		name: "multiple maps",
113		data: struct {
114			foo  map[string]int
115			bar  map[string]int
116			baz  map[string]int
117			qux  map[string]int
118			quux map[string]int
119		}{
120			foo:  map[string]int{"foo": 1, "bar": 2},
121			bar:  map[string]int{"bar": 2},
122			baz:  map[string]int{"baz": 3, "foo": 1},
123			qux:  map[string]int{"qux": 4},
124			quux: map[string]int{"quux": 5},
125		},
126	},
127	{
128		name: "nested structs",
129		data: nestableStruct{
130			foo: nestableStruct{
131				foo: nestableStruct{
132					foo: nestableStruct{
133						foo: nestableStruct{
134							foo: "foo",
135						},
136					},
137				},
138			},
139		},
140	},
141}
142
143func TestHashSuccessful(t *testing.T) {
144	for _, testCase := range hashTestCases {
145		t.Run(testCase.name, func(t *testing.T) {
146			mustHash(t, testCase.data)
147		})
148	}
149}
150
151func TestHashingDereferencePointers(t *testing.T) {
152	str1 := "this is a hash test for pointers"
153	str2 := "this is a hash test for pointers"
154	data := []struct {
155		content *string
156	}{
157		{content: &str1},
158		{content: &str2},
159	}
160	first := mustHash(t, data[0])
161	second := mustHash(t, data[1])
162	if first != second {
163		t.Fatal("Got different results for the same string")
164	}
165}
166
167type nestableStruct struct {
168	foo interface{}
169}
170
171func TestContainsConfigurable(t *testing.T) {
172	testCases := []struct {
173		name   string
174		value  any
175		result bool
176	}{
177		{
178			name: "struct without configurable",
179			value: struct {
180				S string
181			}{},
182			result: false,
183		},
184		{
185			name: "struct with configurable",
186			value: struct {
187				S Configurable[string]
188			}{},
189			result: true,
190		},
191		{
192			name: "struct with allowed configurable",
193			value: struct {
194				S Configurable[string] `blueprint:"allow_configurable_in_provider"`
195			}{},
196			result: false,
197		},
198	}
199
200	for _, testCase := range testCases {
201		t.Run(testCase.name, func(t *testing.T) {
202			got := ContainsConfigurable(testCase.value)
203			if got != testCase.result {
204				t.Errorf("expected %v, got %v", testCase.value, got)
205			}
206		})
207	}
208}
209
210func BenchmarkCalculateHash(b *testing.B) {
211	for _, testCase := range hashTestCases {
212		b.Run(testCase.name, func(b *testing.B) {
213			b.ReportAllocs()
214			for i := 0; i < b.N; i++ {
215				_, err := CalculateHash(testCase.data)
216				if err != nil {
217					panic(err)
218				}
219			}
220		})
221	}
222}
223