diff options
Diffstat (limited to 'tests')
-rw-r--r-- | tests/docker-compose.yaml | 7 | ||||
-rw-r--r-- | tests/plugins/http/plugin_middleware.go | 8 | ||||
-rw-r--r-- | tests/plugins/http/response_test.go | 4 | ||||
-rw-r--r-- | tests/plugins/kv/boltdb/configs/.rr-init.yaml | 46 | ||||
-rw-r--r-- | tests/plugins/kv/boltdb/plugin_test.go | 195 | ||||
-rw-r--r-- | tests/plugins/kv/memcached/configs/.rr-init.yaml | 43 | ||||
-rw-r--r-- | tests/plugins/kv/memcached/plugin_test.go | 195 | ||||
-rw-r--r-- | tests/plugins/kv/memory/configs/.rr-init.yaml | 45 | ||||
-rw-r--r-- | tests/plugins/kv/memory/plugin_test.go | 195 |
9 files changed, 736 insertions, 2 deletions
diff --git a/tests/docker-compose.yaml b/tests/docker-compose.yaml new file mode 100644 index 00000000..fd1a48bf --- /dev/null +++ b/tests/docker-compose.yaml @@ -0,0 +1,7 @@ +version: '3' + +services: + memcached: + image: memcached:latest + ports: + - "0.0.0.0:11211:11211"
\ No newline at end of file diff --git a/tests/plugins/http/plugin_middleware.go b/tests/plugins/http/plugin_middleware.go index 8d02524d..00640b69 100644 --- a/tests/plugins/http/plugin_middleware.go +++ b/tests/plugins/http/plugin_middleware.go @@ -6,15 +6,18 @@ import ( "github.com/spiral/roadrunner/v2/plugins/config" ) +// PluginMiddleware test type PluginMiddleware struct { config config.Configurer } +// Init test func (p *PluginMiddleware) Init(cfg config.Configurer) error { p.config = cfg return nil } +// Middleware test func (p *PluginMiddleware) Middleware(next http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/halt" { @@ -29,19 +32,23 @@ func (p *PluginMiddleware) Middleware(next http.Handler) http.HandlerFunc { } } +// Name test func (p *PluginMiddleware) Name() string { return "pluginMiddleware" } +// PluginMiddleware2 test type PluginMiddleware2 struct { config config.Configurer } +// Init test func (p *PluginMiddleware2) Init(cfg config.Configurer) error { p.config = cfg return nil } +// Middleware test func (p *PluginMiddleware2) Middleware(next http.Handler) http.HandlerFunc { return func(w http.ResponseWriter, r *http.Request) { if r.URL.Path == "/boom" { @@ -56,6 +63,7 @@ func (p *PluginMiddleware2) Middleware(next http.Handler) http.HandlerFunc { } } +// Name test func (p *PluginMiddleware2) Name() string { return "pluginMiddleware2" } diff --git a/tests/plugins/http/response_test.go b/tests/plugins/http/response_test.go index a9cbf91a..9bd2626d 100644 --- a/tests/plugins/http/response_test.go +++ b/tests/plugins/http/response_test.go @@ -138,8 +138,8 @@ func TestWrite_HandlesTrailers(t *testing.T) { assert.NoError(t, r.Write(w)) assert.Nil(t, w.h[httpPlugin.TrailerHeaderKey]) - assert.Nil(t, w.h["foo"]) //nolint:golint,staticcheck - assert.Nil(t, w.h["baz"]) //nolint:golint,staticcheck + assert.Nil(t, w.h["foo"]) //nolint:staticcheck + assert.Nil(t, w.h["baz"]) //nolint:staticcheck assert.Equal(t, "test", w.h.Get("Trailer:foo")) assert.Equal(t, "demo", w.h.Get("Trailer:bar")) diff --git a/tests/plugins/kv/boltdb/configs/.rr-init.yaml b/tests/plugins/kv/boltdb/configs/.rr-init.yaml new file mode 100644 index 00000000..4629a24b --- /dev/null +++ b/tests/plugins/kv/boltdb/configs/.rr-init.yaml @@ -0,0 +1,46 @@ +rpc: + listen: tcp://127.0.0.1:6001 + disabled: false + +server: + command: "php ../../../psr-worker-bench.php" + user: "" + group: "" + env: + "RR_HTTP": "true" + relay: "pipes" + relayTimeout: "20s" + +logs: + mode: development + level: debug + +http: + address: 127.0.0.1:44933 + maxRequestSize: 1024 + middleware: ["gzip", "headers"] + uploads: + forbid: [".php", ".exe", ".bat"] + trustedSubnets: + [ + "10.0.0.0/8", + "127.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + "::1/128", + "fc00::/7", + "fe80::/10", + ] + pool: + numWorkers: 6 + maxJobs: 0 + allocateTimeout: 60s + destroyTimeout: 60s + +# boltdb simple driver +boltdb: + dir: "." + file: "rr" + bucket: "test" + permissions: 777 + interval: 1 # seconds diff --git a/tests/plugins/kv/boltdb/plugin_test.go b/tests/plugins/kv/boltdb/plugin_test.go new file mode 100644 index 00000000..ba9b695a --- /dev/null +++ b/tests/plugins/kv/boltdb/plugin_test.go @@ -0,0 +1,195 @@ +package boltdb_tests //nolint:golint,stylecheck + +import ( + "net" + "net/rpc" + "os" + "os/signal" + "sync" + "syscall" + "testing" + "time" + + "github.com/spiral/endure" + goridgeRpc "github.com/spiral/goridge/v3/pkg/rpc" + "github.com/spiral/roadrunner/v2/plugins/config" + httpPlugin "github.com/spiral/roadrunner/v2/plugins/http" + "github.com/spiral/roadrunner/v2/plugins/kv" + "github.com/spiral/roadrunner/v2/plugins/kv/boltdb" + "github.com/spiral/roadrunner/v2/plugins/logger" + rpcPlugin "github.com/spiral/roadrunner/v2/plugins/rpc" + "github.com/spiral/roadrunner/v2/plugins/server" + "github.com/stretchr/testify/assert" +) + +func TestBoltDb(t *testing.T) { + cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel)) + assert.NoError(t, err) + + cfg := &config.Viper{ + Path: "configs/.rr-init.yaml", + Prefix: "rr", + } + + err = cont.RegisterAll( + cfg, + &boltdb.Plugin{}, + &rpcPlugin.Plugin{}, + &logger.ZapLogger{}, + &server.Plugin{}, + &httpPlugin.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 1) + t.Run("testBoltDbRPCMethods", testRPCMethods) + stopCh <- struct{}{} + wg.Wait() + + _ = os.Remove("rr") +} + +func testRPCMethods(t *testing.T) { + conn, err := net.Dial("tcp", "127.0.0.1:6001") + assert.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + var setRes bool + items := make([]kv.Item, 0, 5) + items = append(items, kv.Item{ + Key: "a", + Value: "aa", + }) + items = append(items, kv.Item{ + Key: "b", + Value: "bb", + }) + // add 5 second ttl + tt := time.Now().Add(time.Second * 5).Format(time.RFC3339) + items = append(items, kv.Item{ + Key: "c", + Value: "cc", + TTL: tt, + }) + + items = append(items, kv.Item{ + Key: "d", + Value: "dd", + }) + + items = append(items, kv.Item{ + Key: "e", + Value: "ee", + }) + + // Register 3 keys with values + err = client.Call("boltdb.Set", items, &setRes) + assert.NoError(t, err) + assert.True(t, setRes) + + ret := make(map[string]bool) + keys := []string{"a", "b", "c"} + err = client.Call("boltdb.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 3) // should be 3 + + // key "c" should be deleted + time.Sleep(time.Second * 7) + + ret = make(map[string]bool) + err = client.Call("boltdb.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 2) // should be 2 + + mGet := make(map[string]interface{}) + keys = []string{"a", "b", "c"} + err = client.Call("boltdb.MGet", keys, &mGet) + assert.NoError(t, err) + assert.Len(t, mGet, 2) // c is expired + assert.Equal(t, string("aa"), mGet["a"].(string)) + assert.Equal(t, string("bb"), mGet["b"].(string)) + + mExpKeys := make([]kv.Item, 0, 2) + tt2 := time.Now().Add(time.Second * 10).Format(time.RFC3339) + mExpKeys = append(mExpKeys, kv.Item{Key: "a", TTL: tt2}) + mExpKeys = append(mExpKeys, kv.Item{Key: "b", TTL: tt2}) + mExpKeys = append(mExpKeys, kv.Item{Key: "d", TTL: tt2}) + + // MEXPIRE + var mExpRes bool + err = client.Call("boltdb.MExpire", mExpKeys, &mExpRes) + assert.NoError(t, err) + assert.True(t, mExpRes) + + // TTL + keys = []string{"a", "b", "d"} + ttlRes := make(map[string]interface{}) + err = client.Call("boltdb.TTL", keys, &ttlRes) + assert.NoError(t, err) + assert.Len(t, ttlRes, 3) + + // HAS AFTER TTL + time.Sleep(time.Second * 11) + ret = make(map[string]bool) + keys = []string{"a", "b", "d"} + err = client.Call("boltdb.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 0) + + // DELETE + keys = []string{"e"} + var delRet bool + err = client.Call("boltdb.Delete", keys, &delRet) + assert.NoError(t, err) + assert.True(t, delRet) + + // HAS AFTER DELETE + ret = make(map[string]bool) + keys = []string{"e"} + err = client.Call("boltdb.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 0) +} diff --git a/tests/plugins/kv/memcached/configs/.rr-init.yaml b/tests/plugins/kv/memcached/configs/.rr-init.yaml new file mode 100644 index 00000000..759fc3ba --- /dev/null +++ b/tests/plugins/kv/memcached/configs/.rr-init.yaml @@ -0,0 +1,43 @@ +rpc: + listen: tcp://127.0.0.1:6001 + disabled: false + +server: + command: "php ../../../psr-worker-bench.php" + user: "" + group: "" + env: + "RR_HTTP": "true" + relay: "pipes" + relayTimeout: "20s" + +logs: + mode: development + level: debug + +http: + address: 127.0.0.1:44933 + maxRequestSize: 1024 + middleware: ["gzip", "headers"] + uploads: + forbid: [".php", ".exe", ".bat"] + trustedSubnets: + [ + "10.0.0.0/8", + "127.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + "::1/128", + "fc00::/7", + "fe80::/10", + ] + pool: + numWorkers: 6 + maxJobs: 0 + allocateTimeout: 60s + destroyTimeout: 60s + +# boltdb simple driver +memcached: + addr: + - "localhost:11211"
\ No newline at end of file diff --git a/tests/plugins/kv/memcached/plugin_test.go b/tests/plugins/kv/memcached/plugin_test.go new file mode 100644 index 00000000..6eff8715 --- /dev/null +++ b/tests/plugins/kv/memcached/plugin_test.go @@ -0,0 +1,195 @@ +package memcached_test + +import ( + "net" + "net/rpc" + "os" + "os/signal" + "sync" + "syscall" + "testing" + "time" + + "github.com/spiral/endure" + goridgeRpc "github.com/spiral/goridge/v3/pkg/rpc" + "github.com/spiral/roadrunner/v2/plugins/config" + httpPlugin "github.com/spiral/roadrunner/v2/plugins/http" + "github.com/spiral/roadrunner/v2/plugins/kv" + "github.com/spiral/roadrunner/v2/plugins/kv/memcached" + "github.com/spiral/roadrunner/v2/plugins/logger" + rpcPlugin "github.com/spiral/roadrunner/v2/plugins/rpc" + "github.com/spiral/roadrunner/v2/plugins/server" + "github.com/stretchr/testify/assert" +) + +func TestMemcache(t *testing.T) { + cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel)) + assert.NoError(t, err) + + cfg := &config.Viper{ + Path: "configs/.rr-init.yaml", + Prefix: "rr", + } + + err = cont.RegisterAll( + cfg, + &memcached.Plugin{}, + &rpcPlugin.Plugin{}, + &logger.ZapLogger{}, + &server.Plugin{}, + &httpPlugin.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 1) + t.Run("testMemcachedRPCMethods", testRPCMethods) + stopCh <- struct{}{} + wg.Wait() + + _ = os.Remove("rr") +} + +func testRPCMethods(t *testing.T) { + conn, err := net.Dial("tcp", "127.0.0.1:6001") + assert.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + var setRes bool + items := make([]kv.Item, 0, 5) + items = append(items, kv.Item{ + Key: "a", + Value: "aa", + }) + items = append(items, kv.Item{ + Key: "b", + Value: "bb", + }) + // add 5 second ttl + tt := time.Now().Add(time.Second * 5).Format(time.RFC3339) + items = append(items, kv.Item{ + Key: "c", + Value: "cc", + TTL: tt, + }) + + items = append(items, kv.Item{ + Key: "d", + Value: "dd", + }) + + items = append(items, kv.Item{ + Key: "e", + Value: "ee", + }) + + // Register 3 keys with values + err = client.Call("memcached.Set", items, &setRes) + assert.NoError(t, err) + assert.True(t, setRes) + + ret := make(map[string]bool) + keys := []string{"a", "b", "c"} + err = client.Call("memcached.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 3) // should be 3 + + // key "c" should be deleted + time.Sleep(time.Second * 7) + + ret = make(map[string]bool) + err = client.Call("memcached.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 2) // should be 2 + + mGet := make(map[string]interface{}) + keys = []string{"a", "b", "c"} + err = client.Call("memcached.MGet", keys, &mGet) + assert.NoError(t, err) + assert.Len(t, mGet, 2) // c is expired + assert.Equal(t, string("aa"), string(mGet["a"].([]byte))) + assert.Equal(t, string("bb"), string(mGet["b"].([]byte))) + + mExpKeys := make([]kv.Item, 0, 2) + tt2 := time.Now().Add(time.Second * 10).Format(time.RFC3339) + mExpKeys = append(mExpKeys, kv.Item{Key: "a", TTL: tt2}) + mExpKeys = append(mExpKeys, kv.Item{Key: "b", TTL: tt2}) + mExpKeys = append(mExpKeys, kv.Item{Key: "d", TTL: tt2}) + + // MEXPIRE + var mExpRes bool + err = client.Call("memcached.MExpire", mExpKeys, &mExpRes) + assert.NoError(t, err) + assert.True(t, mExpRes) + + // TTL call is not supported for the memcached driver + keys = []string{"a", "b", "d"} + ttlRes := make(map[string]interface{}) + err = client.Call("memcached.TTL", keys, &ttlRes) + assert.Error(t, err) + assert.Len(t, ttlRes, 0) + + // HAS AFTER TTL + time.Sleep(time.Second * 11) + ret = make(map[string]bool) + keys = []string{"a", "b", "d"} + err = client.Call("memcached.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 0) + + // DELETE + keys = []string{"e"} + var delRet bool + err = client.Call("memcached.Delete", keys, &delRet) + assert.NoError(t, err) + assert.True(t, delRet) + + // HAS AFTER DELETE + ret = make(map[string]bool) + keys = []string{"e"} + err = client.Call("memcached.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 0) +} diff --git a/tests/plugins/kv/memory/configs/.rr-init.yaml b/tests/plugins/kv/memory/configs/.rr-init.yaml new file mode 100644 index 00000000..dedc6cd2 --- /dev/null +++ b/tests/plugins/kv/memory/configs/.rr-init.yaml @@ -0,0 +1,45 @@ +rpc: + listen: tcp://127.0.0.1:6001 + disabled: false + +server: + command: "php ../../../psr-worker-bench.php" + user: "" + group: "" + env: + "RR_HTTP": "true" + relay: "pipes" + relayTimeout: "20s" + +logs: + mode: development + level: debug + +http: + address: 127.0.0.1:44933 + maxRequestSize: 1024 + middleware: ["gzip", "headers"] + uploads: + forbid: [".php", ".exe", ".bat"] + trustedSubnets: + [ + "10.0.0.0/8", + "127.0.0.0/8", + "172.16.0.0/12", + "192.168.0.0/16", + "::1/128", + "fc00::/7", + "fe80::/10", + ] + pool: + numWorkers: 6 + maxJobs: 0 + allocateTimeout: 60s + destroyTimeout: 60s + +# in memory KV driver +memory: + enabled: + true + # keys ttl check interval + interval: 1 diff --git a/tests/plugins/kv/memory/plugin_test.go b/tests/plugins/kv/memory/plugin_test.go new file mode 100644 index 00000000..c6f94602 --- /dev/null +++ b/tests/plugins/kv/memory/plugin_test.go @@ -0,0 +1,195 @@ +package memory_test + +import ( + "net" + "net/rpc" + "os" + "os/signal" + "sync" + "syscall" + "testing" + "time" + + "github.com/spiral/endure" + goridgeRpc "github.com/spiral/goridge/v3/pkg/rpc" + "github.com/spiral/roadrunner/v2/plugins/config" + httpPlugin "github.com/spiral/roadrunner/v2/plugins/http" + "github.com/spiral/roadrunner/v2/plugins/kv" + "github.com/spiral/roadrunner/v2/plugins/kv/memory" + "github.com/spiral/roadrunner/v2/plugins/logger" + rpcPlugin "github.com/spiral/roadrunner/v2/plugins/rpc" + "github.com/spiral/roadrunner/v2/plugins/server" + "github.com/stretchr/testify/assert" +) + +func TestInMemory(t *testing.T) { + cont, err := endure.NewContainer(nil, endure.SetLogLevel(endure.DebugLevel)) + assert.NoError(t, err) + + cfg := &config.Viper{ + Path: "configs/.rr-init.yaml", + Prefix: "rr", + } + + err = cont.RegisterAll( + cfg, + &memory.Plugin{}, + &rpcPlugin.Plugin{}, + &logger.ZapLogger{}, + &server.Plugin{}, + &httpPlugin.Plugin{}, + ) + assert.NoError(t, err) + + err = cont.Init() + if err != nil { + t.Fatal(err) + } + + ch, err := cont.Serve() + assert.NoError(t, err) + + sig := make(chan os.Signal, 1) + signal.Notify(sig, os.Interrupt, syscall.SIGINT, syscall.SIGTERM) + + wg := &sync.WaitGroup{} + wg.Add(1) + + stopCh := make(chan struct{}, 1) + + go func() { + defer wg.Done() + for { + select { + case e := <-ch: + assert.Fail(t, "error", e.Error.Error()) + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + case <-sig: + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + case <-stopCh: + // timeout + err = cont.Stop() + if err != nil { + assert.FailNow(t, "error", err.Error()) + } + return + } + } + }() + + time.Sleep(time.Second * 1) + t.Run("testInMemoryRPCMethods", testRPCMethods) + stopCh <- struct{}{} + wg.Wait() + + _ = os.Remove("rr") +} + +func testRPCMethods(t *testing.T) { + conn, err := net.Dial("tcp", "127.0.0.1:6001") + assert.NoError(t, err) + client := rpc.NewClientWithCodec(goridgeRpc.NewClientCodec(conn)) + + var setRes bool + items := make([]kv.Item, 0, 5) + items = append(items, kv.Item{ + Key: "a", + Value: "aa", + }) + items = append(items, kv.Item{ + Key: "b", + Value: "bb", + }) + // add 5 second ttl + tt := time.Now().Add(time.Second * 5).Format(time.RFC3339) + items = append(items, kv.Item{ + Key: "c", + Value: "cc", + TTL: tt, + }) + + items = append(items, kv.Item{ + Key: "d", + Value: "dd", + }) + + items = append(items, kv.Item{ + Key: "e", + Value: "ee", + }) + + // Register 3 keys with values + err = client.Call("memory.Set", items, &setRes) + assert.NoError(t, err) + assert.True(t, setRes) + + ret := make(map[string]bool) + keys := []string{"a", "b", "c"} + err = client.Call("memory.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 3) // should be 3 + + // key "c" should be deleted + time.Sleep(time.Second * 7) + + ret = make(map[string]bool) + err = client.Call("memory.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 2) // should be 2 + + mGet := make(map[string]interface{}) + keys = []string{"a", "b", "c"} + err = client.Call("memory.MGet", keys, &mGet) + assert.NoError(t, err) + assert.Len(t, mGet, 2) // c is expired + assert.Equal(t, string("aa"), mGet["a"].(string)) + assert.Equal(t, string("bb"), mGet["b"].(string)) + + mExpKeys := make([]kv.Item, 0, 2) + tt2 := time.Now().Add(time.Second * 10).Format(time.RFC3339) + mExpKeys = append(mExpKeys, kv.Item{Key: "a", TTL: tt2}) + mExpKeys = append(mExpKeys, kv.Item{Key: "b", TTL: tt2}) + mExpKeys = append(mExpKeys, kv.Item{Key: "d", TTL: tt2}) + + // MEXPIRE + var mExpRes bool + err = client.Call("memory.MExpire", mExpKeys, &mExpRes) + assert.NoError(t, err) + assert.True(t, mExpRes) + + // TTL + keys = []string{"a", "b", "d"} + ttlRes := make(map[string]interface{}) + err = client.Call("memory.TTL", keys, &ttlRes) + assert.NoError(t, err) + assert.Len(t, ttlRes, 3) + + // HAS AFTER TTL + time.Sleep(time.Second * 11) + ret = make(map[string]bool) + keys = []string{"a", "b", "d"} + err = client.Call("memory.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 0) + + // DELETE + keys = []string{"e"} + var delRet bool + err = client.Call("memory.Delete", keys, &delRet) + assert.NoError(t, err) + assert.True(t, delRet) + + // HAS AFTER DELETE + ret = make(map[string]bool) + keys = []string{"e"} + err = client.Call("memory.Has", keys, &ret) + assert.NoError(t, err) + assert.Len(t, ret, 0) +} |