Merge pull request #471 from beckn/feature/redis-plugin
Initial commit for Cache Plugin
This commit is contained in:
5
go.mod
5
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
|
||||
|
||||
12
go.sum
12
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=
|
||||
102
pkg/plugin/implementation/cache/cache.go
vendored
Normal file
102
pkg/plugin/implementation/cache/cache.go
vendored
Normal file
@@ -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()
|
||||
}
|
||||
200
pkg/plugin/implementation/cache/cache_test.go
vendored
Normal file
200
pkg/plugin/implementation/cache/cache_test.go
vendored
Normal file
@@ -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)
|
||||
}
|
||||
36
pkg/plugin/implementation/cache/cmd/plugin.go
vendored
Normal file
36
pkg/plugin/implementation/cache/cmd/plugin.go
vendored
Normal file
@@ -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{}
|
||||
159
pkg/plugin/implementation/cache/cmd/plugin_test.go
vendored
Normal file
159
pkg/plugin/implementation/cache/cmd/plugin_test.go
vendored
Normal file
@@ -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)
|
||||
}
|
||||
103
test.go
Normal file
103
test.go
Normal file
@@ -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.")
|
||||
}
|
||||
Reference in New Issue
Block a user