github.com/openfga/openfga@v1.5.4-rc1/internal/keys/hasher_test.go (about)

     1  package keys
     2  
     3  import (
     4  	"testing"
     5  
     6  	"github.com/cespare/xxhash/v2"
     7  	openfgav1 "github.com/openfga/api/proto/openfga/v1"
     8  	"github.com/stretchr/testify/require"
     9  
    10  	"github.com/openfga/openfga/pkg/testutils"
    11  	"github.com/openfga/openfga/pkg/tuple"
    12  )
    13  
    14  func TestTupleKeysHasherSortsFirst(t *testing.T) {
    15  	var testCases = map[string]struct {
    16  		tuplesReversed []*openfgav1.TupleKey
    17  		tuplesOriginal []*openfgav1.TupleKey
    18  	}{
    19  		`unordered_users`: {
    20  			tuplesReversed: []*openfgav1.TupleKey{
    21  				tuple.NewTupleKey("document:A", "relationA", "user:A"),
    22  				tuple.NewTupleKey("document:A", "relationA", "user:B"),
    23  				tuple.NewTupleKey("document:A", "relationA", "user:C"),
    24  			},
    25  			tuplesOriginal: []*openfgav1.TupleKey{
    26  				tuple.NewTupleKey("document:A", "relationA", "user:C"),
    27  				tuple.NewTupleKey("document:A", "relationA", "user:B"),
    28  				tuple.NewTupleKey("document:A", "relationA", "user:A"),
    29  			},
    30  		},
    31  		`unordered_relations`: {
    32  			tuplesReversed: []*openfgav1.TupleKey{
    33  				tuple.NewTupleKey("document:A", "relationA", "user:A"),
    34  				tuple.NewTupleKey("document:A", "relationB", "user:A"),
    35  				tuple.NewTupleKey("document:A", "relationC", "user:A"),
    36  			},
    37  			tuplesOriginal: []*openfgav1.TupleKey{
    38  				tuple.NewTupleKey("document:A", "relationC", "user:A"),
    39  				tuple.NewTupleKey("document:A", "relationB", "user:A"),
    40  				tuple.NewTupleKey("document:A", "relationA", "user:A"),
    41  			},
    42  		},
    43  		`unordered_objects`: {
    44  			tuplesReversed: []*openfgav1.TupleKey{
    45  				tuple.NewTupleKey("document:A", "relationA", "user:A"),
    46  				tuple.NewTupleKey("document:B", "relationA", "user:A"),
    47  				tuple.NewTupleKey("document:C", "relationA", "user:A"),
    48  			},
    49  			tuplesOriginal: []*openfgav1.TupleKey{
    50  				tuple.NewTupleKey("document:C", "relationA", "user:A"),
    51  				tuple.NewTupleKey("document:B", "relationA", "user:A"),
    52  				tuple.NewTupleKey("document:A", "relationA", "user:A"),
    53  			},
    54  		},
    55  		`unordered_relations_users_and_objects`: {
    56  			tuplesReversed: []*openfgav1.TupleKey{
    57  				tuple.NewTupleKey("document:A", "relationA", "user:A"),
    58  				tuple.NewTupleKey("document:A", "relationA", "user:B"),
    59  				tuple.NewTupleKey("document:A", "relationB", "user:A"),
    60  				tuple.NewTupleKey("document:A", "relationB", "user:B"),
    61  				tuple.NewTupleKey("document:B", "relationA", "user:A"),
    62  				tuple.NewTupleKey("document:B", "relationA", "user:B"),
    63  				tuple.NewTupleKey("document:B", "relationB", "user:A"),
    64  				tuple.NewTupleKey("document:B", "relationB", "user:B"),
    65  			},
    66  			tuplesOriginal: []*openfgav1.TupleKey{
    67  				tuple.NewTupleKey("document:B", "relationB", "user:B"),
    68  				tuple.NewTupleKey("document:B", "relationB", "user:A"),
    69  				tuple.NewTupleKey("document:B", "relationA", "user:B"),
    70  				tuple.NewTupleKey("document:B", "relationA", "user:A"),
    71  				tuple.NewTupleKey("document:A", "relationB", "user:B"),
    72  				tuple.NewTupleKey("document:A", "relationB", "user:A"),
    73  				tuple.NewTupleKey("document:A", "relationA", "user:B"),
    74  				tuple.NewTupleKey("document:A", "relationA", "user:A"),
    75  			},
    76  		},
    77  	}
    78  	for name, test := range testCases {
    79  		t.Run(name, func(t *testing.T) {
    80  			hasher1 := NewCacheKeyHasher(xxhash.New())
    81  			tuplesHasher := NewTupleKeysHasher(test.tuplesOriginal...)
    82  			require.NoError(t, tuplesHasher.Append(hasher1))
    83  
    84  			hasher2 := NewCacheKeyHasher(xxhash.New())
    85  			tuplesInvertedHasher := NewTupleKeysHasher(test.tuplesReversed...)
    86  			require.NoError(t, tuplesInvertedHasher.Append(hasher2))
    87  
    88  			require.Equal(t, hasher1.Key().ToUInt64(), hasher2.Key().ToUInt64())
    89  		})
    90  	}
    91  }
    92  
    93  func TestContextHasher(t *testing.T) {
    94  	var testCases = []struct {
    95  		name     string
    96  		context1 map[string]any
    97  		context2 map[string]any
    98  		equal    bool
    99  	}{
   100  		{
   101  			context1: map[string]any{
   102  				"x": []any{"1", "2"},
   103  			},
   104  			context2: map[string]any{
   105  				"x": []any{"2", "1"},
   106  			},
   107  			equal: false,
   108  		},
   109  		{
   110  			context1: map[string]any{
   111  				"x": []any{1},
   112  			},
   113  			context2: map[string]any{
   114  				"x": []any{"1"},
   115  			},
   116  			equal: true,
   117  		},
   118  		{
   119  			context1: map[string]any{
   120  				"x": []any{1},
   121  			},
   122  			context2: map[string]any{
   123  				"x": []any{"1.1"},
   124  			},
   125  			equal: false,
   126  		},
   127  		{
   128  			context1: map[string]any{
   129  				"x": []any{1.0},
   130  			},
   131  			context2: map[string]any{
   132  				"x": []any{1},
   133  			},
   134  			equal: true,
   135  		},
   136  		{
   137  			context1: map[string]any{
   138  				"x": []any{float64(3) / 2},
   139  			},
   140  			context2: map[string]any{
   141  				"x": []any{1},
   142  			},
   143  			equal: false,
   144  		},
   145  		{
   146  			context1: map[string]any{
   147  				"x": []any{3 / 2},
   148  			},
   149  			context2: map[string]any{
   150  				"x": []any{1},
   151  			},
   152  			equal: true,
   153  		},
   154  		{
   155  			context1: map[string]any{
   156  				"x": []any{float64(1) / 1},
   157  			},
   158  			context2: map[string]any{
   159  				"x": []any{1},
   160  			},
   161  			equal: true,
   162  		},
   163  		{
   164  			context1: map[string]any{
   165  				"x": []any{0.000011},
   166  			},
   167  			context2: map[string]any{
   168  				"x": []any{0.0000112},
   169  			},
   170  			equal: false,
   171  		},
   172  	}
   173  
   174  	for _, test := range testCases {
   175  		t.Run(test.name, func(t *testing.T) {
   176  			hasher1 := NewCacheKeyHasher(xxhash.New())
   177  
   178  			struct1 := testutils.MustNewStruct(t, test.context1)
   179  			err := NewContextHasher(struct1).Append(hasher1)
   180  			require.NoError(t, err)
   181  			key1 := hasher1.Key().ToUInt64()
   182  
   183  			hasher2 := NewCacheKeyHasher(xxhash.New())
   184  			struct2 := testutils.MustNewStruct(t, test.context2)
   185  			err = NewContextHasher(struct2).Append(hasher2)
   186  			require.NoError(t, err)
   187  			key2 := hasher2.Key().ToUInt64()
   188  
   189  			if test.equal {
   190  				require.Equal(t, key1, key2)
   191  			} else {
   192  				require.NotEqual(t, key1, key2)
   193  			}
   194  		})
   195  	}
   196  }