Consul is a distributed, highly available, and data center aware solution to connect and configure applications across dynamic, distributed infrastructure.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

146 lines
3.0 KiB

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package resource
import (
"fmt"
"math/rand"
"sort"
"strings"
"testing"
"github.com/stretchr/testify/require"
"google.golang.org/protobuf/proto"
"github.com/hashicorp/consul/proto-public/pbresource"
)
func TestLessReference(t *testing.T) {
parseTenancy := func(s string) *pbresource.Tenancy {
// format is: <partition>.<peerName>.<namespace>
parts := strings.Split(s, ".")
if len(parts) != 3 {
panic("bad tenancy")
}
return &pbresource.Tenancy{
Partition: parts[0],
PeerName: parts[1],
Namespace: parts[2],
}
}
makeRef := func(s string) *pbresource.Reference {
// format is:
// - <type>/<tenancy>/<name>@<section>
// - <type>/<tenancy>/<name>
//
// type = (gvk style)
// tenancy = <partition>.<peerName>.<namespace>
parts := strings.Split(s, "/")
require.Len(t, parts, 3)
name, section, _ := strings.Cut(parts[2], "@")
return &pbresource.Reference{
Type: GVKToType(parts[0]),
Tenancy: parseTenancy(parts[1]),
Name: name,
Section: section,
}
}
var inputs []*pbresource.Reference
stringify := func(all []*pbresource.Reference) []string {
var out []string
for _, ref := range all {
out = append(out, ReferenceToString(ref))
}
return out
}
// We generate pre-sorted data.
vals := []string{"a", "aa", "b", "bb"}
sectionVals := append([]string{""}, vals...)
for _, group := range vals {
for _, version := range vals {
for _, kind := range vals {
for _, partition := range vals {
for _, peer := range vals {
for _, namespace := range vals {
for _, name := range vals {
for _, section := range sectionVals {
if section != "" {
section = "@" + section
}
inputs = append(inputs, makeRef(
fmt.Sprintf(
"%s.%s.%s/%s.%s.%s/%s%s",
group, version, kind,
partition, peer, namespace,
name, section,
),
))
}
}
}
}
}
}
}
}
require.True(t, sort.IsSorted(sortedReferences(inputs)))
const randomTrials = 5
mixed := protoSliceClone(inputs)
for i := 0; i < randomTrials; i++ {
rand.Shuffle(len(mixed), func(i, j int) {
mixed[i], mixed[j] = mixed[j], mixed[i]
})
// We actually got a permuted list out of this.
require.NotEqual(t, stringify(inputs), stringify(mixed))
sort.Slice(mixed, func(i, j int) bool {
return LessReference(mixed[i], mixed[j])
})
// And it sorted.
require.Equal(t, stringify(inputs), stringify(mixed))
}
}
type sortedReferences []*pbresource.Reference
func (r sortedReferences) Len() int {
return len(r)
}
func (r sortedReferences) Less(i, j int) bool {
return LessReference(r[i], r[j])
}
func (r sortedReferences) Swap(i, j int) {
r[i], r[j] = r[j], r[i]
}
func protoClone[T proto.Message](v T) T {
return proto.Clone(v).(T)
}
func protoSliceClone[T proto.Message](in []T) []T {
if in == nil {
return nil
}
out := make([]T, 0, len(in))
for _, v := range in {
out = append(out, protoClone[T](v))
}
return out
}