mirror of
https://github.com/opentffoundation/opentf.git
synced 2025-12-25 01:00:16 -05:00
addrs: UniqueKey and UniqueKeyer
Many times now we've seen situations where we need to use addresses as map keys, but not all of our address types are comparable and thus we tend to end up using string representations as keys instead. That's problematic because conversion to string uses type information and some of the address types have string representations that are ambiguous with one another. UniqueKey therefore represents an opaque key that is unique for each functionally-distinct address across all types that implement UniqueKeyer. For this initial commit I've implemented UniqueKeyer only for the Referenceable family of types. These are an easy case because they were all already comparable (intentionally) anyway. Later commits can implement UniqueKeyer for other types that are not naturally comparable, such as any which include a ModuleInstance. This also includes a new type addrs.Set which wraps a map as a set of addresses, using the unique keys to ensure that there can be only one element for each distinct address.
This commit is contained in:
64
internal/addrs/unique_key_test.go
Normal file
64
internal/addrs/unique_key_test.go
Normal file
@@ -0,0 +1,64 @@
|
||||
package addrs
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// TestUniqueKeyer aims to ensure that all of the types that have unique keys
|
||||
// will continue to meet the UniqueKeyer contract under future changes.
|
||||
//
|
||||
// If you add a new implementation of UniqueKey, consider adding a test case
|
||||
// for it here.
|
||||
func TestUniqueKeyer(t *testing.T) {
|
||||
tests := []UniqueKeyer{
|
||||
CountAttr{Name: "index"},
|
||||
ForEachAttr{Name: "key"},
|
||||
TerraformAttr{Name: "workspace"},
|
||||
PathAttr{Name: "module"},
|
||||
InputVariable{Name: "foo"},
|
||||
ModuleCall{Name: "foo"},
|
||||
ModuleCallInstance{
|
||||
Call: ModuleCall{Name: "foo"},
|
||||
Key: StringKey("a"),
|
||||
},
|
||||
ModuleCallOutput{
|
||||
Call: ModuleCall{Name: "foo"},
|
||||
Name: "bar",
|
||||
},
|
||||
ModuleCallInstanceOutput{
|
||||
Call: ModuleCallInstance{
|
||||
Call: ModuleCall{Name: "foo"},
|
||||
Key: StringKey("a"),
|
||||
},
|
||||
Name: "bar",
|
||||
},
|
||||
Resource{
|
||||
Mode: ManagedResourceMode,
|
||||
Type: "foo",
|
||||
Name: "bar",
|
||||
},
|
||||
ResourceInstance{
|
||||
Resource: Resource{
|
||||
Mode: ManagedResourceMode,
|
||||
Type: "foo",
|
||||
Name: "bar",
|
||||
},
|
||||
Key: IntKey(1),
|
||||
},
|
||||
Self,
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(fmt.Sprintf("%s", test), func(t *testing.T) {
|
||||
a := test.UniqueKey()
|
||||
b := test.UniqueKey()
|
||||
|
||||
// The following comparison will panic if the unique key is not
|
||||
// of a comparable type.
|
||||
if a != b {
|
||||
t.Fatalf("the two unique keys are not equal\na: %#v\b: %#v", a, b)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user