github.com/rajeev159/opa@v0.45.0/topdown/cache/cache_test.go (about)

     1  // Copyright 2020 The OPA Authors.  All rights reserved.
     2  // Use of this source code is governed by an Apache2
     3  // license that can be found in the LICENSE file.
     4  
     5  package cache
     6  
     7  import (
     8  	"reflect"
     9  	"testing"
    10  
    11  	"github.com/open-policy-agent/opa/ast"
    12  )
    13  
    14  func TestParseCachingConfig(t *testing.T) {
    15  	maxSize := new(int64)
    16  	*maxSize = defaultMaxSizeBytes
    17  	expected := &Config{InterQueryBuiltinCache: InterQueryBuiltinCacheConfig{MaxSizeBytes: maxSize}}
    18  
    19  	tests := map[string]struct {
    20  		input   []byte
    21  		wantErr bool
    22  	}{
    23  		"empty_config": {
    24  			input:   nil,
    25  			wantErr: false,
    26  		},
    27  		"default_limit": {
    28  			input:   []byte(`{"inter_query_builtin_cache": {},}`),
    29  			wantErr: false,
    30  		},
    31  		"bad_limit": {
    32  			input:   []byte(`{"inter_query_builtin_cache": {"max_size_bytes": "100"},}`),
    33  			wantErr: true,
    34  		},
    35  	}
    36  
    37  	for name, tc := range tests {
    38  		t.Run(name, func(t *testing.T) {
    39  
    40  			config, err := ParseCachingConfig(tc.input)
    41  			if tc.wantErr {
    42  				if err == nil {
    43  					t.Fatal("Expected error but got nil")
    44  				}
    45  			} else {
    46  				if err != nil {
    47  					t.Fatalf("Unexpected error %v", err)
    48  				}
    49  			}
    50  
    51  			if !tc.wantErr && !reflect.DeepEqual(config, expected) {
    52  				t.Fatalf("want %v got %v", expected, config)
    53  			}
    54  		})
    55  	}
    56  
    57  	// cache limit specified
    58  	in := `{"inter_query_builtin_cache": {"max_size_bytes": 100},}`
    59  
    60  	config, err := ParseCachingConfig([]byte(in))
    61  	if err != nil {
    62  		t.Fatalf("Unexpected error %v", err)
    63  	}
    64  
    65  	limit := int64(100)
    66  	expected.InterQueryBuiltinCache.MaxSizeBytes = &limit
    67  
    68  	if !reflect.DeepEqual(config, expected) {
    69  		t.Fatalf("want %v got %v", expected, config)
    70  	}
    71  }
    72  
    73  func TestInsert(t *testing.T) {
    74  
    75  	in := `{"inter_query_builtin_cache": {"max_size_bytes": 20},}` // 20 byte limit for test purposes
    76  
    77  	config, err := ParseCachingConfig([]byte(in))
    78  	if err != nil {
    79  		t.Fatalf("Unexpected error %v", err)
    80  	}
    81  
    82  	cache := NewInterQueryCache(config)
    83  
    84  	// large cache value that exceeds limit
    85  	cacheValueLarge := newInterQueryCacheValue(ast.StringTerm("bar").Value, 40)
    86  	dropped := cache.Insert(ast.StringTerm("foo").Value, cacheValueLarge)
    87  
    88  	if dropped != 1 {
    89  		t.Fatal("Expected dropped to be one")
    90  	}
    91  
    92  	_, found := cache.Get(ast.StringTerm("foo").Value)
    93  	if found {
    94  		t.Fatal("Unexpected key \"foo\" in cache")
    95  	}
    96  
    97  	cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20)
    98  	dropped = cache.Insert(ast.StringTerm("foo").Value, cacheValue)
    99  
   100  	if dropped != 0 {
   101  		t.Fatal("Expected dropped to be zero")
   102  	}
   103  
   104  	// exceed cache limit
   105  	cacheValue2 := newInterQueryCacheValue(ast.StringTerm("bar2").Value, 20)
   106  	dropped = cache.Insert(ast.StringTerm("foo2").Value, cacheValue2)
   107  
   108  	if dropped != 1 {
   109  		t.Fatal("Expected dropped to be one")
   110  	}
   111  
   112  	_, found = cache.Get(ast.StringTerm("foo2").Value)
   113  	if !found {
   114  		t.Fatal("Expected key \"foo2\" in cache")
   115  	}
   116  
   117  	_, found = cache.Get(ast.StringTerm("foo").Value)
   118  	if found {
   119  		t.Fatal("Unexpected key \"foo\" in cache")
   120  	}
   121  }
   122  
   123  func TestDelete(t *testing.T) {
   124  	config, err := ParseCachingConfig(nil)
   125  	if err != nil {
   126  		t.Fatalf("Unexpected error %v", err)
   127  	}
   128  
   129  	cache := NewInterQueryCache(config)
   130  	cacheValue := newInterQueryCacheValue(ast.StringTerm("bar").Value, 20)
   131  
   132  	dropped := cache.Insert(ast.StringTerm("foo").Value, cacheValue)
   133  
   134  	if dropped != 0 {
   135  		t.Fatal("Expected dropped to be zero")
   136  	}
   137  
   138  	cache.Delete(ast.StringTerm("foo").Value)
   139  
   140  	_, found := cache.Get(ast.StringTerm("foo").Value)
   141  	if found {
   142  		t.Fatal("Unexpected key \"foo\" in cache")
   143  	}
   144  }
   145  
   146  func TestUpdateConfig(t *testing.T) {
   147  	config, err := ParseCachingConfig(nil)
   148  	if err != nil {
   149  		t.Fatalf("Unexpected error %v", err)
   150  	}
   151  	c := NewInterQueryCache(config)
   152  	actualC, ok := c.(*cache)
   153  	if !ok {
   154  		t.Fatal("Unexpected error converting InterQueryCache to cache struct")
   155  	}
   156  	if actualC.config != config {
   157  		t.Fatal("Cache config is different than expected")
   158  	}
   159  	actualC.UpdateConfig(nil)
   160  	if actualC.config != config {
   161  		t.Fatal("Cache config is different than expected after a nil update")
   162  	}
   163  	config2, err := ParseCachingConfig(nil)
   164  	if err != nil {
   165  		t.Fatalf("Unexpected error: %v", err)
   166  	}
   167  	actualC.UpdateConfig(config2)
   168  	if actualC.config != config2 {
   169  		t.Fatal("Cache config is different than expected after update")
   170  	}
   171  }
   172  
   173  func TestDefaultMaxSizeBytes(t *testing.T) {
   174  	c := NewInterQueryCache(nil)
   175  	actualC, ok := c.(*cache)
   176  	if !ok {
   177  		t.Fatal("Unexpected error converting InterQueryCache to cache struct")
   178  	}
   179  	if actualC.maxSizeBytes() != defaultMaxSizeBytes {
   180  		t.Fatal("Expected maxSizeBytes() to return default when config is nil")
   181  	}
   182  }
   183  
   184  type testInterQueryCacheValue struct {
   185  	value ast.Value
   186  	size  int
   187  }
   188  
   189  func newInterQueryCacheValue(value ast.Value, size int) *testInterQueryCacheValue {
   190  	return &testInterQueryCacheValue{value: value, size: size}
   191  }
   192  
   193  func (p testInterQueryCacheValue) SizeInBytes() int64 {
   194  	return int64(p.size)
   195  }