blob: 84c4e07b3bf8abc7433759ed2c8305bfbe27b4cf [file]
package rediswatcher_test
import (
"encoding/json"
"fmt"
"github.com/google/uuid"
"reflect"
"testing"
"time"
"github.com/casbin/casbin/v3"
"github.com/casbin/casbin/v3/persist"
rediswatcher "github.com/casbin/redis-watcher/v2"
"github.com/redis/go-redis/v9"
)
func initWatcherWithOptions(t *testing.T, wo rediswatcher.WatcherOptions, cluster ...bool) (*casbin.Enforcer, *rediswatcher.Watcher) {
var (
w persist.Watcher
err error
)
if len(cluster) > 0 && cluster[0] {
w, err = rediswatcher.NewWatcherWithCluster("127.0.0.1:6379,127.0.0.1:6379,127.0.0.1:6379", wo)
} else {
w, err = rediswatcher.NewWatcher("127.0.0.1:6379", wo)
}
if err != nil {
t.Fatalf("Failed to connect to Redis: %v", err)
}
e, err := casbin.NewEnforcer("examples/rbac_model.conf", "examples/rbac_policy.csv")
if err != nil {
t.Fatalf("Failed to create enforcer: %v", err)
}
_ = e.SetWatcher(w)
_ = w.SetUpdateCallback(func(s string) {
t.Log(s)
rediswatcher.DefaultUpdateCallback(e)(s)
})
return e, w.(*rediswatcher.Watcher)
}
func initWatcher(t *testing.T, cluster ...bool) (*casbin.Enforcer, *rediswatcher.Watcher) {
return initWatcherWithOptions(t, rediswatcher.WatcherOptions{}, cluster...)
}
func TestWatcher(t *testing.T) {
_, w := initWatcher(t)
_ = w.SetUpdateCallback(func(s string) {
fmt.Println(s)
})
_ = w.Update()
w.Close()
time.Sleep(time.Millisecond * 500)
}
func TestWatcherWithIngnoreSelfFalse(t *testing.T) {
wo := rediswatcher.WatcherOptions{
LocalID: uuid.New().String(),
}
_, w := initWatcherWithOptions(t, wo)
_ = w.SetUpdateCallback(func(s string) {
msg := &rediswatcher.MSG{}
_ = json.Unmarshal([]byte(s), msg)
if msg.ID != wo.LocalID {
t.Fatalf("instance ID should be %s instead of %s", wo.LocalID, msg.ID)
}
})
_ = w.Update()
time.Sleep(time.Millisecond * 500)
w.Close()
time.Sleep(time.Millisecond * 500)
}
func TestWatcherWithIgnoreSelfTrue(t *testing.T) {
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
_, w := initWatcherWithOptions(t, wo)
_ = w.SetUpdateCallback(func(s string) {
t.Fatalf("This callback should not be called when IgnoreSelf is set true.")
})
_ = w.Update()
time.Sleep(time.Millisecond * 500)
w.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdate(t *testing.T) {
_, w := initWatcher(t)
_ = w.SetUpdateCallback(func(s string) {
msgStruct := &rediswatcher.MSG{}
err := msgStruct.UnmarshalBinary([]byte(s))
if err != nil {
t.Error(err)
return
}
if msgStruct.Method != "Update" {
t.Errorf("Method should be Update instead of %s", msgStruct.Method)
}
})
_ = w.Update()
w.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdateForAddPolicy(t *testing.T) {
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
e, w := initWatcherWithOptions(t, wo)
e2, w2 := initWatcherWithOptions(t, wo)
time.Sleep(time.Millisecond * 500)
_, _ = e.AddPolicy("alice", "book1", "write")
time.Sleep(time.Millisecond * 500)
ePolicy, _ := e.GetPolicy()
e2Policy, _ := e2.GetPolicy()
if !reflect.DeepEqual(e2Policy, ePolicy) {
t.Log("Method", "AddPolicy")
t.Log("e.policy", ePolicy)
t.Log("e2.policy", e2Policy)
t.Error("These two enforcers' policies should be equal")
}
w.Close()
w2.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdateForRemovePolicy(t *testing.T) {
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
e, w := initWatcherWithOptions(t, wo)
e2, w2 := initWatcherWithOptions(t, wo)
time.Sleep(time.Millisecond * 500)
_, _ = e.RemovePolicy("alice", "data1", "read")
time.Sleep(time.Millisecond * 500)
ePolicy, _ := e.GetPolicy()
e2Policy, _ := e2.GetPolicy()
if !reflect.DeepEqual(e2Policy, ePolicy) {
t.Log("Method", "RemovePolicy")
t.Log("e.policy", ePolicy)
t.Log("e2.policy", e2Policy)
t.Error("These two enforcers' policies should be equal")
}
w.Close()
w2.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdateForRemoveFilteredPolicy(t *testing.T) {
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
e, w := initWatcherWithOptions(t, wo)
e2, w2 := initWatcherWithOptions(t, wo)
time.Sleep(time.Millisecond * 500)
_, _ = e.RemoveFilteredPolicy(1, "data1", "read")
time.Sleep(time.Millisecond * 500)
ePolicy, _ := e.GetPolicy()
e2Policy, _ := e2.GetPolicy()
if !reflect.DeepEqual(e2Policy, ePolicy) {
t.Log("Method", "RemoveFilteredPolicy")
t.Log("e.policy", ePolicy)
t.Log("e2.policy", e2Policy)
t.Error("These two enforcers' policies should be equal")
}
w.Close()
w2.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdateSavePolicy(t *testing.T) {
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
e, w := initWatcherWithOptions(t, wo)
e2, w2 := initWatcherWithOptions(t, wo)
time.Sleep(time.Millisecond * 500)
_ = e.SavePolicy()
time.Sleep(time.Millisecond * 500)
ePolicy, _ := e.GetPolicy()
e2Policy, _ := e2.GetPolicy()
if !reflect.DeepEqual(e2Policy, ePolicy) {
t.Log("Method", "SavePolicy")
t.Log("e.policy", ePolicy)
t.Log("e2.policy", e2Policy)
t.Error("These two enforcers' policies should be equal")
}
w.Close()
w2.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdateForAddPolicies(t *testing.T) {
rules := [][]string{
{"jack", "data4", "read"},
{"katy", "data4", "write"},
{"leyo", "data4", "read"},
{"ham", "data4", "write"},
}
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
e, w := initWatcherWithOptions(t, wo)
e2, w2 := initWatcherWithOptions(t, wo)
time.Sleep(time.Millisecond * 500)
_, _ = e.AddPolicies(rules)
time.Sleep(time.Millisecond * 500)
ePolicy, _ := e.GetPolicy()
e2Policy, _ := e2.GetPolicy()
if !reflect.DeepEqual(e2Policy, ePolicy) {
t.Log("Method", "AddPolicies")
t.Log("e.policy", ePolicy)
t.Log("e2.policy", e2Policy)
t.Error("These two enforcers' policies should be equal")
}
w.Close()
w2.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdateForRemovePolicies(t *testing.T) {
rules := [][]string{
{"jack", "data4", "read"},
{"katy", "data4", "write"},
{"leyo", "data4", "read"},
{"ham", "data4", "write"},
}
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
e, w := initWatcherWithOptions(t, wo)
e2, w2 := initWatcherWithOptions(t, wo)
time.Sleep(time.Millisecond * 500)
_, _ = e.RemovePolicies(rules)
time.Sleep(time.Millisecond * 500)
ePolicy, _ := e.GetPolicy()
e2Policy, _ := e2.GetPolicy()
if !reflect.DeepEqual(e2Policy, ePolicy) {
t.Log("Method", "RemovePolicies")
t.Log("e.policy", ePolicy)
t.Log("e2.policy", e2Policy)
t.Error("These two enforcers' policies should be equal")
}
w.Close()
w2.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdateForUpdatePolicy(t *testing.T) {
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
e, w := initWatcherWithOptions(t, wo)
e2, w2 := initWatcherWithOptions(t, wo)
time.Sleep(time.Millisecond * 500)
_, _ = e.UpdatePolicy([]string{"alice", "data1", "read"}, []string{"alice", "data1", "write"})
time.Sleep(time.Millisecond * 500)
ePolicy, _ := e.GetPolicy()
e2Policy, _ := e2.GetPolicy()
if !reflect.DeepEqual(e2Policy, ePolicy) {
t.Log("Method", "UpdatePolicy")
t.Log("e.policy", ePolicy)
t.Log("e2.policy", e2Policy)
t.Error("These two enforcers' policies should be equal")
}
w.Close()
w2.Close()
time.Sleep(time.Millisecond * 500)
}
func TestUpdateForUpdatePolicies(t *testing.T) {
wo := rediswatcher.WatcherOptions{
IgnoreSelf: true,
}
e, w := initWatcherWithOptions(t, wo)
e2, w2 := initWatcherWithOptions(t, wo)
time.Sleep(time.Millisecond * 500)
_, _ = e.UpdatePolicies([][]string{{"alice", "data1", "read"}}, [][]string{{"alice", "data1", "write"}})
time.Sleep(time.Millisecond * 500)
ePolicy, _ := e.GetPolicy()
e2Policy, _ := e2.GetPolicy()
if !reflect.DeepEqual(e2Policy, ePolicy) {
t.Log("Method", "UpdatePolicies")
t.Log("e.policy", ePolicy)
t.Log("e2.policy", e2Policy)
t.Error("These two enforcers' policies should be equal")
}
w.Close()
w2.Close()
time.Sleep(time.Millisecond * 500)
}
func TestNewWatcherWithClusterUsingExistingClient(t *testing.T) {
// Create a regular Redis client that implements UniversalClient interface
// In production, this would be a cluster client
redisClient := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
})
// Use the existing client for both pub and sub
// This demonstrates that UniversalClient type works with any Redis client type
wo := rediswatcher.WatcherOptions{
SubClient: redisClient,
PubClient: redisClient,
}
// Initialize watcher with existing client
// Pass empty string for addrs since we're using existing clients
// When SubClient and PubClient are provided, the addrs parameter is ignored
w, err := rediswatcher.NewWatcherWithCluster("", wo)
if err != nil {
t.Fatalf("Failed to create watcher with existing client: %v", err)
}
watcher := w.(*rediswatcher.Watcher)
// Verify the watcher works
_ = watcher.SetUpdateCallback(func(s string) {
t.Log("Received update:", s)
})
_ = watcher.Update()
time.Sleep(time.Millisecond * 500)
watcher.Close()
time.Sleep(time.Millisecond * 500)
}
func TestNewWatcherWithExistingClient(t *testing.T) {
// Create a regular Redis client externally
redisClient := redis.NewClient(&redis.Options{
Addr: "127.0.0.1:6379",
})
// Use the existing client for both pub and sub
wo := rediswatcher.WatcherOptions{
SubClient: redisClient,
PubClient: redisClient,
}
// Initialize watcher with existing client
// Pass empty string for addr since we're using existing clients
// When SubClient and PubClient are provided, the addr parameter is ignored
w, err := rediswatcher.NewWatcher("", wo)
if err != nil {
t.Fatalf("Failed to create watcher with existing client: %v", err)
}
watcher := w.(*rediswatcher.Watcher)
// Verify the watcher works
_ = watcher.SetUpdateCallback(func(s string) {
t.Log("Received update:", s)
})
_ = watcher.Update()
time.Sleep(time.Millisecond * 500)
watcher.Close()
time.Sleep(time.Millisecond * 500)
}