diff --git a/go.mod b/go.mod index b7e02e5..5de7bcb 100644 --- a/go.mod +++ b/go.mod @@ -2,6 +2,8 @@ module github.com/beckn/beckn-onix go 1.24 +toolchain go1.23.4 + require ( github.com/kr/pretty v0.3.1 // indirect github.com/rogpeppe/go-internal v1.13.1 // indirect @@ -25,6 +27,8 @@ require github.com/zenazn/pkcs7pad v0.0.0-20170308005700-253a5b1f0e03 require golang.org/x/text v0.23.0 // indirect require ( + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/go-jose/go-jose/v4 v4.0.1 // indirect github.com/google/go-cmp v0.6.0 // indirect @@ -48,6 +52,7 @@ require ( require ( github.com/google/uuid v1.6.0 github.com/hashicorp/go-retryablehttp v0.7.7 + github.com/redis/go-redis/v9 v9.8.0 github.com/hashicorp/vault/api v1.16.0 github.com/rabbitmq/amqp091-go v1.10.0 github.com/rs/zerolog v1.34.0 diff --git a/go.sum b/go.sum index e8fb854..f7bbfb0 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,9 @@ +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8= github.com/bgentry/speakeasy v0.1.0/go.mod h1:+zsyZBPWlz7T6j88CTgSN5bM796AkVf0kBD4zp0CCIs= github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= @@ -6,6 +12,8 @@ github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= @@ -72,6 +80,8 @@ github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RR github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/redis/go-redis/v9 v9.8.0 h1:q3nRvjrlge/6UD7eTu/DSg2uYiU2mCL0G/uzBWqhicI= +github.com/redis/go-redis/v9 v9.8.0/go.mod h1:huWgSWd8mW6+m0VPhJjSSQ+d6Nh1VICQ6Q5lHuCH/Iw= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI= @@ -121,4 +131,4 @@ gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= -gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= \ No newline at end of file diff --git a/pkg/plugin/implementation/cache/cache.go b/pkg/plugin/implementation/cache/cache.go new file mode 100644 index 0000000..7007492 --- /dev/null +++ b/pkg/plugin/implementation/cache/cache.go @@ -0,0 +1,102 @@ +package cache + +import ( + "context" + "errors" + "fmt" + "os" + "time" + + "github.com/beckn/beckn-onix/pkg/log" + "github.com/redis/go-redis/v9" +) + +// RedisCl global variable for the Redis client, can be overridden in tests +var RedisCl *redis.Client + +// RedisClient is an interface for Redis operations that allows mocking +type RedisClient interface { + Get(ctx context.Context, key string) *redis.StringCmd + Set(ctx context.Context, key string, value interface{}, ttl time.Duration) *redis.StatusCmd + Del(ctx context.Context, keys ...string) *redis.IntCmd + FlushDB(ctx context.Context) *redis.StatusCmd + Ping(ctx context.Context) *redis.StatusCmd + Close() error +} + +// Config holds the configuration required to connect to Redis. +type Config struct { + Addr string +} + +// Cache wraps a Redis client to provide basic caching operations. +type Cache struct { + Client RedisClient +} + +// Error variables to describe common failure modes. +var ( + ErrEmptyConfig = errors.New("empty config") + ErrAddrMissing = errors.New("missing required field 'Addr'") + ErrCredentialMissing = errors.New("missing Redis credentials in environment") + ErrConnectionFail = errors.New("failed to connect to Redis") +) + +// validate checks if the provided Redis configuration is valid. +func validate(cfg *Config) error { + if cfg == nil { + return ErrEmptyConfig + } + if cfg.Addr == "" { + return ErrAddrMissing + } + return nil +} + +// RedisClientFunc is a function variable that creates a Redis client based on the provided configuration. +// It can be overridden for testing purposes. +var RedisClientFunc = func(cfg *Config) RedisClient { + return redis.NewClient(&redis.Options{ + Addr: cfg.Addr, + Password: os.Getenv("REDIS_PASSWORD"), + DB: 0, + }) +} + +// New initializes and returns a Cache instance along with a close function to release resources. +func New(ctx context.Context, cfg *Config) (*Cache, func() error, error) { + log.Debugf(ctx, "Initializing Cache with config: %+v", cfg) + if err := validate(cfg); err != nil { + return nil, nil, err + } + + client := RedisClientFunc(cfg) + + if _, err := client.Ping(ctx).Result(); err != nil { + log.Errorf(ctx, err, "Failed to ping Redis server") + return nil, nil, fmt.Errorf("%w: %v", ErrConnectionFail, err) + } + + log.Infof(ctx, "Cache connection to Redis established successfully") + return &Cache{Client: client}, client.Close, nil +} + +// Get retrieves the value for the specified key from Redis. +func (c *Cache) Get(ctx context.Context, key string) (string, error) { + return c.Client.Get(ctx, key).Result() +} + +// Set stores the given key-value pair in Redis with the specified TTL (time to live). +func (c *Cache) Set(ctx context.Context, key, value string, ttl time.Duration) error { + return c.Client.Set(ctx, key, value, ttl).Err() +} + +// Delete removes the specified key from Redis. +func (c *Cache) Delete(ctx context.Context, key string) error { + return c.Client.Del(ctx, key).Err() +} + +// Clear removes all keys in the currently selected Redis database. +func (c *Cache) Clear(ctx context.Context) error { + return c.Client.FlushDB(ctx).Err() +} diff --git a/pkg/plugin/implementation/cache/cache_test.go b/pkg/plugin/implementation/cache/cache_test.go new file mode 100644 index 0000000..05113fe --- /dev/null +++ b/pkg/plugin/implementation/cache/cache_test.go @@ -0,0 +1,200 @@ +package cache + +import ( + "context" + "os" + "testing" + "time" + + "github.com/redis/go-redis/v9" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +// MockRedisClient is a mock implementation of the RedisClient interface +type MockRedisClient struct { + mock.Mock +} + +func (m *MockRedisClient) Get(ctx context.Context, key string) *redis.StringCmd { + args := m.Called(ctx, key) + return redis.NewStringResult(args.String(0), args.Error(1)) +} + +func (m *MockRedisClient) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) *redis.StatusCmd { + args := m.Called(ctx, key, value, ttl) + return redis.NewStatusResult(args.String(0), args.Error(1)) +} + +func (m *MockRedisClient) Del(ctx context.Context, keys ...string) *redis.IntCmd { + args := m.Called(ctx, keys) + return redis.NewIntCmd(ctx, args.Int(0), args.Error(1)) +} + +func (m *MockRedisClient) FlushDB(ctx context.Context) *redis.StatusCmd { + args := m.Called(ctx) + return redis.NewStatusResult(args.String(0), args.Error(1)) +} + +func (m *MockRedisClient) Ping(ctx context.Context) *redis.StatusCmd { + args := m.Called(ctx) + return args.Get(0).(*redis.StatusCmd) +} + +func (m *MockRedisClient) Close() error { + args := m.Called() + return args.Error(0) +} + +// TestCache_Get tests the Get method of the Cache type +func TestCache_Get(t *testing.T) { + mockClient := new(MockRedisClient) + ctx := context.Background() + cache := &Cache{Client: mockClient} + + mockClient.On("Get", ctx, "my-key").Return("my-value", nil) + + value, err := cache.Get(ctx, "my-key") + assert.NoError(t, err) + assert.Equal(t, "my-value", value) + mockClient.AssertExpectations(t) +} + +// TestCache_Set tests the Set method of the Cache type +func TestCache_Set(t *testing.T) { + mockClient := new(MockRedisClient) + ctx := context.Background() + cache := &Cache{Client: mockClient} + + mockClient.On("Set", ctx, "my-key", "my-value", time.Minute).Return("OK", nil) + + err := cache.Set(ctx, "my-key", "my-value", time.Minute) + assert.NoError(t, err) + mockClient.AssertExpectations(t) +} + +// TestCache_Delete tests the Delete method of the Cache type +func TestCache_Delete(t *testing.T) { + mockClient := new(MockRedisClient) + ctx := context.Background() + cache := &Cache{Client: mockClient} + + mockClient.On("Del", ctx, []string{"my-key"}).Return(1, nil) + + err := cache.Delete(ctx, "my-key") + assert.NoError(t, err) + mockClient.AssertExpectations(t) +} + +// TestCache_Clear tests the Clear method of the Cache type +func TestCache_Clear(t *testing.T) { + mockClient := new(MockRedisClient) + ctx := context.Background() + cache := &Cache{Client: mockClient} + + mockClient.On("FlushDB", ctx).Return("OK", nil) + + err := cache.Clear(ctx) + assert.NoError(t, err) + mockClient.AssertExpectations(t) +} + +// TestValidate tests the validate function +func TestValidate(t *testing.T) { + tests := []struct { + name string + cfg *Config + wantErr error + }{ + { + name: "nil config", + cfg: nil, + wantErr: ErrEmptyConfig, + }, + { + name: "empty addr", + cfg: &Config{Addr: ""}, + wantErr: ErrAddrMissing, + }, + { + name: "valid config", + cfg: &Config{Addr: "localhost:6379"}, + wantErr: nil, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := validate(tt.cfg) + if tt.wantErr != nil { + assert.Equal(t, tt.wantErr, err) + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestNew_Validation tests the validation parts of the New function +func TestNew_Validation(t *testing.T) { + testCases := []struct { + name string + cfg *Config + wantErr bool + errType error + }{ + { + name: "nil config", + cfg: nil, + wantErr: true, + errType: ErrEmptyConfig, + }, + { + name: "empty addr", + cfg: &Config{Addr: ""}, + wantErr: true, + errType: ErrAddrMissing, + }, + } + + for _, tc := range testCases { + t.Run(tc.name, func(t *testing.T) { + _, _, err := New(context.Background(), tc.cfg) + + if tc.wantErr { + assert.Error(t, err) + if tc.errType != nil { + assert.ErrorIs(t, err, tc.errType) + } + } else { + assert.NoError(t, err) + } + }) + } +} + +// TestNew_ConnectionFailure tests the connection failure in New function +func TestNew_ConnectionFailure(t *testing.T) { + // Set test env var + err := os.Setenv("REDIS_PASSWORD", "") + if err != nil { + t.Fatalf("Failed to set REDIS_PASSWORD environment variable: %v", err) + } + + defer func() { + err := os.Unsetenv("REDIS_PASSWORD") + if err != nil { + t.Fatalf("Failed to unset REDIS_PASSWORD environment variable: %v", err) + } + }() + + // Use an invalid connection address to force a connection failure + cfg := &Config{Addr: "invalid:1234"} + + // Call New which should fail with a connection error + _, _, err = New(context.Background(), cfg) + + // Verify error type is connection failure + assert.Error(t, err) + assert.ErrorIs(t, err, ErrConnectionFail) +} diff --git a/pkg/plugin/implementation/cache/cmd/plugin.go b/pkg/plugin/implementation/cache/cmd/plugin.go new file mode 100644 index 0000000..4cd5869 --- /dev/null +++ b/pkg/plugin/implementation/cache/cmd/plugin.go @@ -0,0 +1,36 @@ +package main + +import ( + "context" + "errors" + + "github.com/beckn/beckn-onix/pkg/log" + "github.com/beckn/beckn-onix/pkg/plugin/definition" + "github.com/beckn/beckn-onix/pkg/plugin/implementation/cache" +) + +// cacheProvider implements the CacheProvider interface for the cache plugin. +type cacheProvider struct{} + +// New creates a new cache plugin instance. +func (c cacheProvider) New(ctx context.Context, config map[string]string) (definition.Cache, func() error, error) { + if ctx == nil { + return nil, nil, errors.New("context cannot be nil") + } + // Create cache.Config directly from map - validation is handled by cache.New + cacheConfig := &cache.Config{ + Addr: config["addr"], + } + log.Debugf(ctx, "Cache config mapped: %+v", cacheConfig) + cache, closer, err := cache.New(ctx, cacheConfig) + if err != nil { + log.Errorf(ctx, err, "Failed to create cache instance") + return nil, nil, err + } + + log.Infof(ctx, "Cache instance created successfully") + return cache, closer, nil +} + +// Provider is the exported plugin instance +var Provider = cacheProvider{} diff --git a/pkg/plugin/implementation/cache/cmd/plugin_test.go b/pkg/plugin/implementation/cache/cmd/plugin_test.go new file mode 100644 index 0000000..f853d63 --- /dev/null +++ b/pkg/plugin/implementation/cache/cmd/plugin_test.go @@ -0,0 +1,159 @@ +package main + +import ( + "context" + "os" + "testing" + "time" + + "github.com/beckn/beckn-onix/pkg/plugin/implementation/cache" + "github.com/redis/go-redis/v9" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +// TestProviderNew tests the New method of the cacheProvider +func TestProviderNew(t *testing.T) { + provider := cacheProvider{} + + // Save original environment variable and restore it after test + origPassword := os.Getenv("REDIS_PASSWORD") + defer func() { + if err := os.Setenv("REDIS_PASSWORD", origPassword); err != nil { + t.Fatalf("Failed to restore REDIS_PASSWORD: %v", err) + } + }() + // Set an empty password for testing + if err := os.Setenv("REDIS_PASSWORD", ""); err != nil { + t.Fatalf("Failed to set REDIS_PASSWORD: %v", err) + } + + tests := []struct { + name string + ctx context.Context + config map[string]string + expectErr bool + }{ + { + name: "nil context", + ctx: nil, + config: map[string]string{"addr": "localhost:6379"}, + expectErr: true, + }, + { + name: "invalid config", + ctx: context.Background(), + config: map[string]string{}, // Missing addr + expectErr: true, + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + cache, cleanup, err := provider.New(tt.ctx, tt.config) + + if tt.expectErr { + assert.Error(t, err) + assert.Nil(t, cache) + assert.Nil(t, cleanup) + } else { + assert.NoError(t, err) + assert.NotNil(t, cache) + assert.NotNil(t, cleanup) + } + }) + } +} + +// TestProviderVariable tests that the Provider variable is correctly initialized +func TestProviderVariable(t *testing.T) { + assert.NotNil(t, Provider, "Provider should not be nil") +} + +// mockRedisClient mocks the RedisClient interface from the cache package +type mockRedisClient struct { + mock.Mock +} + +func (m *mockRedisClient) Get(ctx context.Context, key string) *redis.StringCmd { + args := m.Called(ctx, key) + cmd := redis.NewStringCmd(ctx) + cmd.SetVal(args.String(0)) + return cmd +} + +func (m *mockRedisClient) Set(ctx context.Context, key string, value interface{}, ttl time.Duration) *redis.StatusCmd { + args := m.Called(ctx, key, value, ttl) + cmd := redis.NewStatusCmd(ctx) + cmd.SetVal(args.String(0)) + return cmd +} + +func (m *mockRedisClient) Del(ctx context.Context, keys ...string) *redis.IntCmd { + args := m.Called(ctx, keys) + cmd := redis.NewIntCmd(ctx) + cmd.SetVal(int64(args.Int(0))) + return cmd +} + +func (m *mockRedisClient) FlushDB(ctx context.Context) *redis.StatusCmd { + args := m.Called(ctx) + cmd := redis.NewStatusCmd(ctx) + cmd.SetVal(args.String(0)) + return cmd +} + +func (m *mockRedisClient) Ping(ctx context.Context) *redis.StatusCmd { + args := m.Called(ctx) + cmd := redis.NewStatusCmd(ctx) + cmd.SetVal(args.String(0)) + return cmd +} + +func (m *mockRedisClient) Close() error { + args := m.Called() + return args.Error(0) +} + +func TestProviderIntegration(t *testing.T) { + // Save original RedisClientFunc and restore after test + original := cache.RedisClientFunc + defer func() { cache.RedisClientFunc = original }() + + // Create and assign mock + mockClient := new(mockRedisClient) + cache.RedisClientFunc = func(cfg *cache.Config) cache.RedisClient { + return mockClient + } + + ctx := context.Background() + + // Expectations for the mock + mockClient.On("Ping", ctx).Return("PONG") + mockClient.On("Close").Return(nil) + + // Create the config and convert it into a map[string]string + config := &cache.Config{ + Addr: "localhost:6379", + } + // Convert the *cache.Config to map[string]string + configMap := map[string]string{ + "addr": config.Addr, + } + + // Call the plugin provider + provider := Provider + c, cleanup, err := provider.New(ctx, configMap) + + // Assertions + assert.NoError(t, err) + assert.NotNil(t, c) + assert.NotNil(t, cleanup) + + // Call cleanup and assert + err = cleanup() + assert.NoError(t, err) + + // Verify expectations + mockClient.AssertExpectations(t) +} \ No newline at end of file diff --git a/test.go b/test.go new file mode 100644 index 0000000..3a82846 --- /dev/null +++ b/test.go @@ -0,0 +1,103 @@ +package main + +import ( + "context" + "fmt" + "plugin" + "time" + + "github.com/beckn/beckn-onix/pkg/plugin/definition" +) + +func main() { + ctx := context.Background() + + // Path to the compiled plugin .so file + // Adjust the path accordingly + pluginPath := "pkg/plugin/implementation/cache.so" + + // Open the plugin + p, err := plugin.Open(pluginPath) + if err != nil { + fmt.Printf("Failed to open plugin: %v\n", err) + return + } + + // Lookup the 'Provider' symbol + symProvider, err := p.Lookup("Provider") + if err != nil { + fmt.Printf("Failed to lookup 'Provider': %v\n", err) + return + } + + // Assert that the symbol implements the CacheProvider interface + provider, ok := symProvider.(definition.CacheProvider) + if !ok { + fmt.Println("Plugin 'Provider' does not implement CacheProvider interface.") + return + } + fmt.Println("Successfully loaded CacheProvider plugin.") + + // Setup config + config := map[string]string{ + "addr": "localhost:6379", // Adjust to your Redis instance + } + + // Create a new cache instance using the plugin provider + cacheInstance, cleanup, err := provider.New(ctx, config) + if err != nil { + fmt.Printf("Error creating cache instance: %v\n", err) + return + } + defer func() { + if cleanup != nil { + _ = cleanup() + } + }() + + fmt.Println("Cache instance created successfully.") + + // Test Set + key := "plugin_test_key" + value := "plugin_test_value" + ttl := 10 * time.Second + + err = cacheInstance.Set(ctx, key, value, ttl) + if err != nil { + fmt.Printf("Set failed: %v\n", err) + return + } + fmt.Println("Set operation successful.") + + // Test Get + got, err := cacheInstance.Get(ctx, key) + if err != nil { + fmt.Printf("Get failed: %v\n", err) + return + } + fmt.Printf("Got value: %s\n", got) + + // Test Delete + err = cacheInstance.Delete(ctx, key) + if err != nil { + fmt.Printf("Delete failed: %v\n", err) + return + } + fmt.Println("Delete operation successful.") + + // Test Clear + // Add a key to test Clear + err = cacheInstance.Set(ctx, "another_plugin_key", "another_plugin_value", ttl) + if err != nil { + fmt.Printf("Set for clear test failed: %v\n", err) + return + } + fmt.Println("Added key for clear test.") + + err = cacheInstance.Clear(ctx) + if err != nil { + fmt.Printf("Clear failed: %v\n", err) + return + } + fmt.Println("Clear operation successful.") +}