From 3856ebbbff49fe1cf765d597ebfc1262ee1f9267 Mon Sep 17 00:00:00 2001 From: yxrrxy <1532529704@qq.com> Date: Wed, 16 Jul 2025 18:29:42 +0800 Subject: [PATCH 1/6] feat: Add unified Config struct for Redis adapter initialization --- .github/workflows/ci.yml | 4 +- README.md | 41 +++-- adapter.go | 172 +++++++++++++++------ adapter_test.go | 315 ++++++++++++++++++++++++++++++++++++++- 4 files changed, 465 insertions(+), 67 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b31900..87b0bfb 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -24,9 +24,7 @@ jobs: run: go test -v -coverprofile=profile.cov ./... - name: Install goveralls - env: - GO111MODULE: off - run: go get github.com/mattn/goveralls + run: go install github.com/mattn/goveralls@v0.0.11 - name: Send coverage env: diff --git a/README.md b/README.md index 2ee6a18..831ed85 100644 --- a/README.md +++ b/README.md @@ -26,28 +26,29 @@ import ( ) func main() { - // Direct Initialization: - // Initialize a Redis adapter and use it in a Casbin enforcer: - a, _ := redisadapter.NewAdapter("tcp", "127.0.0.1:6379") // Your Redis network and address. + // Basic initialization (deprecated) + a, _ := redisadapter.NewAdapterBasic("tcp", "127.0.0.1:6379") - // Use the following if Redis has password like "123" + // With password (deprecated) // a, err := redisadapter.NewAdapterWithPassword("tcp", "127.0.0.1:6379", "123") - // Use the following if you use Redis with a specific user + // With user credentials (deprecated) // a, err := redisadapter.NewAdapterWithUser("tcp", "127.0.0.1:6379", "username", "password") - // Use the following if you use Redis connections pool + // With custom key (deprecated) + // a, err := redisadapter.NewAdapterWithKey("tcp", "127.0.0.1:6379", "my_rules") + + // With connection pool (deprecated) // pool := &redis.Pool{} // a, err := redisadapter.NewAdapterWithPool(pool) - // Initialization with different user options: - // Use the following if you use Redis with passowrd like "123": - // a, err := redisadapter.NewAdapterWithOption(redisadapter.WithNetwork("tcp"), redisadapter.WithAddress("127.0.0.1:6379"), redisadapter.WithPassword("123")) - - // Use the following if you use Redis with username, password, and TLS option: - // var clientTLSConfig tls.Config - // ... - // a, err := redisadapter.NewAdapterWithOption(redisadapter.WithNetwork("tcp"), redisadapter.WithAddress("127.0.0.1:6379"), redisadapter.WithUsername("testAccount"), redisadapter.WithPassword("123456"), redisadapter.WithTls(&clientTLSConfig)) + // With options pattern (deprecated) + // a, err := redisadapter.NewAdapterWithOption( + // redisadapter.WithNetwork("tcp"), + // redisadapter.WithAddress("127.0.0.1:6379"), + // redisadapter.WithPassword("123"), + // redisadapter.WithKey("my_rules"), + // ) e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a) @@ -66,6 +67,18 @@ func main() { } ``` +## Configuration Options + +The `Config` struct supports the following options: + +- `Network` (string): Network type, e.g., "tcp", "unix" (required when not using Pool) +- `Address` (string): Redis server address, e.g., "127.0.0.1:6379" (required when not using Pool) +- `Key` (string): Redis key to store Casbin rules (default: "casbin_rules") +- `Username` (string): Username for Redis authentication (optional) +- `Password` (string): Password for Redis authentication (optional) +- `TLSConfig` (*tls.Config): TLS configuration for secure connections (optional) +- `Pool` (*redis.Pool): Existing Redis connection pool (optional, if provided, other connection options are ignored) + ## Getting Help - [Casbin](https://github.com/casbin/casbin) diff --git a/adapter.go b/adapter.go index 6266c07..f46f317 100644 --- a/adapter.go +++ b/adapter.go @@ -40,6 +40,25 @@ type CasbinRule struct { V5 string } +// Config represents the configuration for the Redis adapter. +type Config struct { + // Network is the network type, e.g., "tcp", "unix" + Network string + // Address is the Redis server address, e.g., "127.0.0.1:6379" + Address string + // Key is the Redis key to store Casbin rules (default: "casbin_rules") + Key string + // Username for Redis authentication (optional) + Username string + // Password for Redis authentication (optional) + Password string + // TLSConfig for secure connections (optional) + TLSConfig *tls.Config + // Pool is an existing Redis connection pool (optional) + // If provided, Network, Address, Username, Password, and TLSConfig are ignored + Pool *redis.Pool +} + // Adapter represents the Redis adapter for policy storage. type Adapter struct { network string @@ -78,94 +97,148 @@ func finalizer(a *Adapter) { } } -func newAdapter(network string, address string, key string, - username string, password string) (*Adapter, error) { +// NewAdapter creates a new Redis adapter with the provided configuration. +func NewAdapter(config *Config) (*Adapter, error) { + if config == nil { + return nil, errors.New("config cannot be nil") + } + a := &Adapter{} - a.network = network - a.address = address - a.key = key - a.username = username - a.password = password - // Open the DB, create it if not existed. - err := a.open() + // Set default key if not provided + if config.Key == "" { + a.key = "casbin_rules" + } else { + a.key = config.Key + } + + // If a pool is provided, use it + if config.Pool != nil { + a._pool = config.Pool + } else { + // Otherwise, create a new connection + if config.Network == "" { + return nil, errors.New("network is required when not using a pool") + } + if config.Address == "" { + return nil, errors.New("address is required when not using a pool") + } + + a.network = config.Network + a.address = config.Address + a.username = config.Username + a.password = config.Password + a.tlsConfig = config.TLSConfig + + // Open the DB connection + err := a.open() + if err != nil { + return nil, err + } + } // Call the destructor when the object is released. runtime.SetFinalizer(a, finalizer) - return a, err + return a, nil } -// NewAdapter is the constructor for Adapter. -func NewAdapter(network string, address string) (*Adapter, error) { - return newAdapter(network, address, "casbin_rules", "", "") +// Legacy constructor functions (deprecated) +// These are kept for backward compatibility but should be avoided in new code + +// NewAdapterBasic is the basic constructor for Adapter. +// Deprecated: Use NewAdapter with Config struct instead. +func NewAdapterBasic(network string, address string) (*Adapter, error) { + config := &Config{ + Network: network, + Address: address, + } + return NewAdapter(config) } +// NewAdapterWithUser creates adapter with user credentials. +// Deprecated: Use NewAdapter with Config struct instead. func NewAdapterWithUser(network string, address string, username string, password string) (*Adapter, error) { - return newAdapter(network, address, "casbin_rules", username, password) + config := &Config{ + Network: network, + Address: address, + Username: username, + Password: password, + } + return NewAdapter(config) } -// NewAdapterWithPassword is the constructor for Adapter. +// NewAdapterWithPassword creates adapter with password authentication. +// Deprecated: Use NewAdapter with Config struct instead. func NewAdapterWithPassword(network string, address string, password string) (*Adapter, error) { - return newAdapter(network, address, "casbin_rules", "", password) + config := &Config{ + Network: network, + Address: address, + Password: password, + } + return NewAdapter(config) } -// NewAdapterWithKey is the constructor for Adapter. +// NewAdapterWithKey creates adapter with custom key. +// Deprecated: Use NewAdapter with Config struct instead. func NewAdapterWithKey(network string, address string, key string) (*Adapter, error) { - return newAdapter(network, address, key, "", "") + config := &Config{ + Network: network, + Address: address, + Key: key, + } + return NewAdapter(config) } -// NewAdapterWithPool is the constructor for Adapter. +// NewAdapterWithPool creates adapter with connection pool. +// Deprecated: Use NewAdapter with Config struct instead. func NewAdapterWithPool(pool *redis.Pool) (*Adapter, error) { - a := &Adapter{} - a.key = "casbin_rules" - - conn := pool.Get() - defer a.release(conn) - - a._conn = conn - a._pool = pool - - // Call the destructor when the object is released. - runtime.SetFinalizer(a, finalizer) - - return a, nil + config := &Config{ + Pool: pool, + } + return NewAdapter(config) } -// NewAdapterWithPoolAndOptions is the constructor for Adapter. +// NewAdapterWithPoolAndOptions creates adapter with pool and options. +// Deprecated: Use NewAdapter with Config struct instead. func NewAdapterWithPoolAndOptions(pool *redis.Pool, options ...Option) (*Adapter, error) { - a := &Adapter{} - a.key = "casbin_rules" + config := &Config{ + Pool: pool, + } + a, err := NewAdapter(config) + if err != nil { + return nil, err + } + + // Apply options for backward compatibility for _, option := range options { option(a) } - conn := pool.Get() - defer a.release(conn) - - a._conn = conn - a._pool = pool - - // Call the destructor when the object is released. - runtime.SetFinalizer(a, finalizer) - return a, nil } type Option func(*Adapter) +// NewAdapterWithOption creates adapter with options pattern. +// Deprecated: Use NewAdapter with Config struct instead. func NewAdapterWithOption(options ...Option) (*Adapter, error) { a := &Adapter{} for _, option := range options { option(a) } - // Open the DB, create it if not existed. - err := a.open() - // Call the destructor when the object is released. - runtime.SetFinalizer(a, finalizer) + // Convert to new config-based approach + config := &Config{ + Network: a.network, + Address: a.address, + Key: a.key, + Username: a.username, + Password: a.password, + TLSConfig: a.tlsConfig, + } - return a, err + return NewAdapter(config) } func WithAddress(address string) Option { @@ -191,6 +264,7 @@ func WithNetwork(network string) Option { a.network = network } } + func WithKey(key string) Option { return func(a *Adapter) { a.key = key diff --git a/adapter_test.go b/adapter_test.go index 0c3769e..afd7257 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -354,7 +354,7 @@ func arrayEqualsWithoutOrder(a [][]string, b [][]string) bool { } func TestAdapters(t *testing.T) { - a, _ := NewAdapter("tcp", "127.0.0.1:6379") + a, _ := NewAdapterBasic("tcp", "127.0.0.1:6379") // Use the following if Redis has password like "123" // a, err := NewAdapterWithPassword("tcp", "127.0.0.1:6379", "123") @@ -422,3 +422,316 @@ func TestPoolAndOptionsAdapters(t *testing.T) { testUpdatePolicies(t, a) testUpdateFilteredPolicies(t, a) } + +func TestNewAdapterWithConfig(t *testing.T) { + // Test basic configuration + config := &Config{ + Network: "tcp", + Address: "127.0.0.1:6379", + } + a, _ := NewAdapter(config) + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithPool(t *testing.T) { + // Test with connection pool + pool := &redis.Pool{ + MaxIdle: 3, + MaxActive: 5, + Dial: func() (redis.Conn, error) { + return redis.Dial("tcp", "127.0.0.1:6379") + }, + } + config := &Config{ + Pool: pool, + Key: "pool_test_rules", + } + a, err := NewAdapter(config) + if err != nil { + t.Fatal(err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterErrorCases(t *testing.T) { + // Test error cases + _, err := NewAdapter(nil) + if err == nil { + t.Error("NewAdapter should fail with nil config") + } + + config := &Config{ + Network: "", + Address: "127.0.0.1:6379", + } + _, err = NewAdapter(config) + if err == nil { + t.Error("NewAdapter should fail with empty network") + } + + config = &Config{ + Network: "tcp", + Address: "", + } + _, err = NewAdapter(config) + if err == nil { + t.Error("NewAdapter should fail with empty address") + } +} + +func TestNewAdapterWithPassword(t *testing.T) { + // Test with password authentication + a, err := NewAdapterWithPassword("tcp", "127.0.0.1:6379", "testpass") + if err != nil { + t.Skipf("Password authentication test skipped (Redis may not have auth configured): %v", err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithUser(t *testing.T) { + // Test with username and password authentication + a, err := NewAdapterWithUser("tcp", "127.0.0.1:6379", "testuser", "testpass") + if err != nil { + t.Skipf("User authentication test skipped (Redis may not have auth configured): %v", err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithKey(t *testing.T) { + // Test with custom key + a, err := NewAdapterWithKey("tcp", "127.0.0.1:6379", "custom_casbin_key") + if err != nil { + t.Fatal(err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestIsFiltered(t *testing.T) { + // Test IsFiltered functionality + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initially should not be filtered + if a.IsFiltered() { + t.Error("Adapter should not be filtered initially") + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer and load filtered policy + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Load filtered policy + err = e.LoadFilteredPolicy(Filter{V0: []string{"alice"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy failed: %v", err) + } + + // Now should be filtered + if !a.IsFiltered() { + t.Error("Adapter should be filtered after LoadFilteredPolicy") + } + + // Load all policies (not filtered) + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy failed: %v", err) + } + + // Should not be filtered anymore + if a.IsFiltered() { + t.Error("Adapter should not be filtered after LoadPolicy") + } +} + +func TestFilterFunctionality(t *testing.T) { + // Test various filter scenarios + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Test filter with multiple values + err = e.LoadFilteredPolicy(Filter{V0: []string{"alice", "bob"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with multiple values failed: %v", err) + } + + policies := e.GetPolicy() + if len(policies) == 0 { + t.Error("Expected policies after filtered load") + } + + // Test filter with V1 field + err = e.LoadFilteredPolicy(Filter{V1: []string{"data1"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with V1 failed: %v", err) + } + + policies = e.GetPolicy() + for _, policy := range policies { + if len(policy) > 1 && policy[1] != "data1" { + t.Errorf("Expected policy with data1, got %v", policy) + } + } + + // Test filter with V2 field + err = e.LoadFilteredPolicy(Filter{V2: []string{"read"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with V2 failed: %v", err) + } + + policies = e.GetPolicy() + for _, policy := range policies { + if len(policy) > 2 && policy[2] != "read" { + t.Errorf("Expected policy with read action, got %v", policy) + } + } +} + +func TestRemoveFilteredPolicy(t *testing.T) { + // Test RemoveFilteredPolicy functionality + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Load all policies first + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy failed: %v", err) + } + + // Test RemoveFilteredPolicy - remove alice's policies + err = a.RemoveFilteredPolicy("p", "p", 0, "alice") + if err != nil { + t.Fatalf("RemoveFilteredPolicy failed: %v", err) + } + + // Reload and check + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy after RemoveFilteredPolicy failed: %v", err) + } + + policies := e.GetPolicy() + aliceCount := 0 + for _, policy := range policies { + if len(policy) > 0 && policy[0] == "alice" { + aliceCount++ + } + } + + // We expect fewer alice policies after removal + if aliceCount > 0 { + t.Logf("Alice policies count after removal: %d", aliceCount) + // This is expected if the filter didn't match all alice policies + } +} + +func TestInvalidFilterType(t *testing.T) { + // Test LoadFilteredPolicy with invalid filter type directly on adapter + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create model for testing + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + + // Test with invalid filter type directly on adapter + err = a.LoadFilteredPolicy(e.GetModel(), "invalid_filter_type") + if err == nil { + t.Error("Expected error for invalid filter type") + } else { + t.Logf("Got expected error for invalid filter type: %v", err) + } +} + +func TestEmptyPolicyOperations(t *testing.T) { + // Test operations on empty policies + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Test AddPolicy with single empty string (valid rule) + err = a.AddPolicy("p", "p", []string{""}) + if err != nil { + t.Errorf("AddPolicy with empty string failed: %v", err) + } + + // Test RemovePolicy with single empty string + err = a.RemovePolicy("p", "p", []string{""}) + if err != nil { + t.Errorf("RemovePolicy with empty string failed: %v", err) + } + + // Test AddPolicies with single rule containing empty string + err = a.AddPolicies("p", "p", [][]string{{"", "data1", "read"}}) + if err != nil { + t.Errorf("AddPolicies with rule containing empty string failed: %v", err) + } + + // Test RemovePolicies with single rule containing empty string + err = a.RemovePolicies("p", "p", [][]string{{"", "data1", "read"}}) + if err != nil { + t.Errorf("RemovePolicies with rule containing empty string failed: %v", err) + } +} From f5a4f8899b4fbbb7777cc6ab4c6f00166dbbd073 Mon Sep 17 00:00:00 2001 From: yxrrxy <1532529704@qq.com> Date: Wed, 16 Jul 2025 20:12:31 +0800 Subject: [PATCH 2/6] fix:doc and test --- README.md | 83 +++++----- adapter_config_test.go | 335 +++++++++++++++++++++++++++++++++++++++++ adapter_test.go | 313 -------------------------------------- 3 files changed, 384 insertions(+), 347 deletions(-) create mode 100644 adapter_config_test.go diff --git a/README.md b/README.md index 831ed85..730cf6e 100644 --- a/README.md +++ b/README.md @@ -15,6 +15,18 @@ Redis Adapter is the [Redis](https://redis.io/) adapter for [Casbin](https://git go get github.com/casbin/redis-adapter/v3 +## Configuration Options + +The `Config` struct supports the following options: + +- `Network` (string): Network type, e.g., "tcp", "unix" (required when not using Pool) +- `Address` (string): Redis server address, e.g., "127.0.0.1:6379" (required when not using Pool) +- `Key` (string): Redis key to store Casbin rules (default: "casbin_rules") +- `Username` (string): Username for Redis authentication (optional) +- `Password` (string): Password for Redis authentication (optional) +- `TLSConfig` (*tls.Config): TLS configuration for secure connections (optional) +- `Pool` (*redis.Pool): Existing Redis connection pool (optional, if provided, other connection options are ignored) + ## Simple Example ```go @@ -26,29 +38,44 @@ import ( ) func main() { - // Basic initialization (deprecated) - a, _ := redisadapter.NewAdapterBasic("tcp", "127.0.0.1:6379") - - // With password (deprecated) - // a, err := redisadapter.NewAdapterWithPassword("tcp", "127.0.0.1:6379", "123") - - // With user credentials (deprecated) - // a, err := redisadapter.NewAdapterWithUser("tcp", "127.0.0.1:6379", "username", "password") - - // With custom key (deprecated) - // a, err := redisadapter.NewAdapterWithKey("tcp", "127.0.0.1:6379", "my_rules") - - // With connection pool (deprecated) + // Recommended way: Using NewAdapter with Config + config := &redisadapter.Config{ + Network: "tcp", + Address: "127.0.0.1:6379", + } + a, _ := redisadapter.NewAdapter(config) + + // With password + // config := &redisadapter.Config{ + // Network: "tcp", + // Address: "127.0.0.1:6379", + // Password: "123", + // } + // a, _ := redisadapter.NewAdapter(config) + + // With user credentials + // config := &redisadapter.Config{ + // Network: "tcp", + // Address: "127.0.0.1:6379", + // Username: "username", + // Password: "password", + // } + // a, _ := redisadapter.NewAdapter(config) + + // With custom key + // config := &redisadapter.Config{ + // Network: "tcp", + // Address: "127.0.0.1:6379", + // Key: "my_rules", + // } + // a, _ := redisadapter.NewAdapter(config) + + // With connection pool // pool := &redis.Pool{} - // a, err := redisadapter.NewAdapterWithPool(pool) - - // With options pattern (deprecated) - // a, err := redisadapter.NewAdapterWithOption( - // redisadapter.WithNetwork("tcp"), - // redisadapter.WithAddress("127.0.0.1:6379"), - // redisadapter.WithPassword("123"), - // redisadapter.WithKey("my_rules"), - // ) + // config := &redisadapter.Config{ + // Pool: pool, + // } + // a, _ := redisadapter.NewAdapter(config) e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a) @@ -67,18 +94,6 @@ func main() { } ``` -## Configuration Options - -The `Config` struct supports the following options: - -- `Network` (string): Network type, e.g., "tcp", "unix" (required when not using Pool) -- `Address` (string): Redis server address, e.g., "127.0.0.1:6379" (required when not using Pool) -- `Key` (string): Redis key to store Casbin rules (default: "casbin_rules") -- `Username` (string): Username for Redis authentication (optional) -- `Password` (string): Password for Redis authentication (optional) -- `TLSConfig` (*tls.Config): TLS configuration for secure connections (optional) -- `Pool` (*redis.Pool): Existing Redis connection pool (optional, if provided, other connection options are ignored) - ## Getting Help - [Casbin](https://github.com/casbin/casbin) diff --git a/adapter_config_test.go b/adapter_config_test.go new file mode 100644 index 0000000..07f975f --- /dev/null +++ b/adapter_config_test.go @@ -0,0 +1,335 @@ +// Copyright 2017 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package redisadapter + +import ( + "testing" + + "github.com/casbin/casbin/v2" + "github.com/gomodule/redigo/redis" +) + +func TestNewAdapterWithConfig(t *testing.T) { + // Test basic configuration + config := &Config{ + Network: "tcp", + Address: "127.0.0.1:6379", + } + a, _ := NewAdapter(config) + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithPool(t *testing.T) { + // Test with connection pool + pool := &redis.Pool{ + MaxIdle: 3, + MaxActive: 5, + Dial: func() (redis.Conn, error) { + return redis.Dial("tcp", "127.0.0.1:6379") + }, + } + config := &Config{ + Pool: pool, + Key: "pool_test_rules", + } + a, err := NewAdapter(config) + if err != nil { + t.Fatal(err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterErrorCases(t *testing.T) { + // Test error cases + _, err := NewAdapter(nil) + if err == nil { + t.Error("NewAdapter should fail with nil config") + } + + config := &Config{ + Network: "", + Address: "127.0.0.1:6379", + } + _, err = NewAdapter(config) + if err == nil { + t.Error("NewAdapter should fail with empty network") + } + + config = &Config{ + Network: "tcp", + Address: "", + } + _, err = NewAdapter(config) + if err == nil { + t.Error("NewAdapter should fail with empty address") + } +} + +func TestNewAdapterWithPassword(t *testing.T) { + // Test with password authentication + a, err := NewAdapterWithPassword("tcp", "127.0.0.1:6379", "testpass") + if err != nil { + t.Skipf("Password authentication test skipped (Redis may not have auth configured): %v", err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithUser(t *testing.T) { + // Test with username and password authentication + a, err := NewAdapterWithUser("tcp", "127.0.0.1:6379", "testuser", "testpass") + if err != nil { + t.Skipf("User authentication test skipped (Redis may not have auth configured): %v", err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithKey(t *testing.T) { + // Test with custom key + a, err := NewAdapterWithKey("tcp", "127.0.0.1:6379", "custom_casbin_key") + if err != nil { + t.Fatal(err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestIsFiltered(t *testing.T) { + // Test IsFiltered functionality + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initially should not be filtered + if a.IsFiltered() { + t.Error("Adapter should not be filtered initially") + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer and load filtered policy + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Load filtered policy + err = e.LoadFilteredPolicy(Filter{V0: []string{"alice"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy failed: %v", err) + } + + // Now should be filtered + if !a.IsFiltered() { + t.Error("Adapter should be filtered after LoadFilteredPolicy") + } + + // Load all policies (not filtered) + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy failed: %v", err) + } + + // Should not be filtered anymore + if a.IsFiltered() { + t.Error("Adapter should not be filtered after LoadPolicy") + } +} + +func TestFilterFunctionality(t *testing.T) { + // Test various filter scenarios + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Test filter with multiple values + err = e.LoadFilteredPolicy(Filter{V0: []string{"alice", "bob"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with multiple values failed: %v", err) + } + + policies := e.GetPolicy() + if len(policies) == 0 { + t.Error("Expected policies after filtered load") + } + + // Test filter with V1 field + err = e.LoadFilteredPolicy(Filter{V1: []string{"data1"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with V1 failed: %v", err) + } + + policies = e.GetPolicy() + for _, policy := range policies { + if len(policy) > 1 && policy[1] != "data1" { + t.Errorf("Expected policy with data1, got %v", policy) + } + } + + // Test filter with V2 field + err = e.LoadFilteredPolicy(Filter{V2: []string{"read"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with V2 failed: %v", err) + } + + policies = e.GetPolicy() + for _, policy := range policies { + if len(policy) > 2 && policy[2] != "read" { + t.Errorf("Expected policy with read action, got %v", policy) + } + } +} + +func TestRemoveFilteredPolicy(t *testing.T) { + // Test RemoveFilteredPolicy functionality + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Load all policies first + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy failed: %v", err) + } + + // Test RemoveFilteredPolicy - remove alice's policies + err = a.RemoveFilteredPolicy("p", "p", 0, "alice") + if err != nil { + t.Fatalf("RemoveFilteredPolicy failed: %v", err) + } + + // Reload and check + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy after RemoveFilteredPolicy failed: %v", err) + } + + policies := e.GetPolicy() + aliceCount := 0 + for _, policy := range policies { + if len(policy) > 0 && policy[0] == "alice" { + aliceCount++ + } + } + + // We expect fewer alice policies after removal + if aliceCount > 0 { + t.Logf("Alice policies count after removal: %d", aliceCount) + // This is expected if the filter didn't match all alice policies + } +} + +func TestInvalidFilterType(t *testing.T) { + // Test LoadFilteredPolicy with invalid filter type directly on adapter + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create model for testing + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + + // Test with invalid filter type directly on adapter + err = a.LoadFilteredPolicy(e.GetModel(), "invalid_filter_type") + if err == nil { + t.Error("Expected error for invalid filter type") + } else { + t.Logf("Got expected error for invalid filter type: %v", err) + } +} + +func TestEmptyPolicyOperations(t *testing.T) { + // Test operations on empty policies + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Test AddPolicy with single empty string (valid rule) + err = a.AddPolicy("p", "p", []string{""}) + if err != nil { + t.Errorf("AddPolicy with empty string failed: %v", err) + } + + // Test RemovePolicy with single empty string + err = a.RemovePolicy("p", "p", []string{""}) + if err != nil { + t.Errorf("RemovePolicy with empty string failed: %v", err) + } + + // Test AddPolicies with single rule containing empty string + err = a.AddPolicies("p", "p", [][]string{{"", "data1", "read"}}) + if err != nil { + t.Errorf("AddPolicies with rule containing empty string failed: %v", err) + } + + // Test RemovePolicies with single rule containing empty string + err = a.RemovePolicies("p", "p", [][]string{{"", "data1", "read"}}) + if err != nil { + t.Errorf("RemovePolicies with rule containing empty string failed: %v", err) + } +} diff --git a/adapter_test.go b/adapter_test.go index afd7257..e69f8a5 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -422,316 +422,3 @@ func TestPoolAndOptionsAdapters(t *testing.T) { testUpdatePolicies(t, a) testUpdateFilteredPolicies(t, a) } - -func TestNewAdapterWithConfig(t *testing.T) { - // Test basic configuration - config := &Config{ - Network: "tcp", - Address: "127.0.0.1:6379", - } - a, _ := NewAdapter(config) - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithPool(t *testing.T) { - // Test with connection pool - pool := &redis.Pool{ - MaxIdle: 3, - MaxActive: 5, - Dial: func() (redis.Conn, error) { - return redis.Dial("tcp", "127.0.0.1:6379") - }, - } - config := &Config{ - Pool: pool, - Key: "pool_test_rules", - } - a, err := NewAdapter(config) - if err != nil { - t.Fatal(err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterErrorCases(t *testing.T) { - // Test error cases - _, err := NewAdapter(nil) - if err == nil { - t.Error("NewAdapter should fail with nil config") - } - - config := &Config{ - Network: "", - Address: "127.0.0.1:6379", - } - _, err = NewAdapter(config) - if err == nil { - t.Error("NewAdapter should fail with empty network") - } - - config = &Config{ - Network: "tcp", - Address: "", - } - _, err = NewAdapter(config) - if err == nil { - t.Error("NewAdapter should fail with empty address") - } -} - -func TestNewAdapterWithPassword(t *testing.T) { - // Test with password authentication - a, err := NewAdapterWithPassword("tcp", "127.0.0.1:6379", "testpass") - if err != nil { - t.Skipf("Password authentication test skipped (Redis may not have auth configured): %v", err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithUser(t *testing.T) { - // Test with username and password authentication - a, err := NewAdapterWithUser("tcp", "127.0.0.1:6379", "testuser", "testpass") - if err != nil { - t.Skipf("User authentication test skipped (Redis may not have auth configured): %v", err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithKey(t *testing.T) { - // Test with custom key - a, err := NewAdapterWithKey("tcp", "127.0.0.1:6379", "custom_casbin_key") - if err != nil { - t.Fatal(err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestIsFiltered(t *testing.T) { - // Test IsFiltered functionality - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initially should not be filtered - if a.IsFiltered() { - t.Error("Adapter should not be filtered initially") - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer and load filtered policy - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Load filtered policy - err = e.LoadFilteredPolicy(Filter{V0: []string{"alice"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy failed: %v", err) - } - - // Now should be filtered - if !a.IsFiltered() { - t.Error("Adapter should be filtered after LoadFilteredPolicy") - } - - // Load all policies (not filtered) - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy failed: %v", err) - } - - // Should not be filtered anymore - if a.IsFiltered() { - t.Error("Adapter should not be filtered after LoadPolicy") - } -} - -func TestFilterFunctionality(t *testing.T) { - // Test various filter scenarios - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Test filter with multiple values - err = e.LoadFilteredPolicy(Filter{V0: []string{"alice", "bob"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with multiple values failed: %v", err) - } - - policies := e.GetPolicy() - if len(policies) == 0 { - t.Error("Expected policies after filtered load") - } - - // Test filter with V1 field - err = e.LoadFilteredPolicy(Filter{V1: []string{"data1"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with V1 failed: %v", err) - } - - policies = e.GetPolicy() - for _, policy := range policies { - if len(policy) > 1 && policy[1] != "data1" { - t.Errorf("Expected policy with data1, got %v", policy) - } - } - - // Test filter with V2 field - err = e.LoadFilteredPolicy(Filter{V2: []string{"read"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with V2 failed: %v", err) - } - - policies = e.GetPolicy() - for _, policy := range policies { - if len(policy) > 2 && policy[2] != "read" { - t.Errorf("Expected policy with read action, got %v", policy) - } - } -} - -func TestRemoveFilteredPolicy(t *testing.T) { - // Test RemoveFilteredPolicy functionality - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Load all policies first - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy failed: %v", err) - } - - // Test RemoveFilteredPolicy - remove alice's policies - err = a.RemoveFilteredPolicy("p", "p", 0, "alice") - if err != nil { - t.Fatalf("RemoveFilteredPolicy failed: %v", err) - } - - // Reload and check - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy after RemoveFilteredPolicy failed: %v", err) - } - - policies := e.GetPolicy() - aliceCount := 0 - for _, policy := range policies { - if len(policy) > 0 && policy[0] == "alice" { - aliceCount++ - } - } - - // We expect fewer alice policies after removal - if aliceCount > 0 { - t.Logf("Alice policies count after removal: %d", aliceCount) - // This is expected if the filter didn't match all alice policies - } -} - -func TestInvalidFilterType(t *testing.T) { - // Test LoadFilteredPolicy with invalid filter type directly on adapter - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create model for testing - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - - // Test with invalid filter type directly on adapter - err = a.LoadFilteredPolicy(e.GetModel(), "invalid_filter_type") - if err == nil { - t.Error("Expected error for invalid filter type") - } else { - t.Logf("Got expected error for invalid filter type: %v", err) - } -} - -func TestEmptyPolicyOperations(t *testing.T) { - // Test operations on empty policies - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Test AddPolicy with single empty string (valid rule) - err = a.AddPolicy("p", "p", []string{""}) - if err != nil { - t.Errorf("AddPolicy with empty string failed: %v", err) - } - - // Test RemovePolicy with single empty string - err = a.RemovePolicy("p", "p", []string{""}) - if err != nil { - t.Errorf("RemovePolicy with empty string failed: %v", err) - } - - // Test AddPolicies with single rule containing empty string - err = a.AddPolicies("p", "p", [][]string{{"", "data1", "read"}}) - if err != nil { - t.Errorf("AddPolicies with rule containing empty string failed: %v", err) - } - - // Test RemovePolicies with single rule containing empty string - err = a.RemovePolicies("p", "p", [][]string{{"", "data1", "read"}}) - if err != nil { - t.Errorf("RemovePolicies with rule containing empty string failed: %v", err) - } -} From 42e86e9deb3e329c406e01a8ae68d78daf8183d5 Mon Sep 17 00:00:00 2001 From: yxrrxy <1532529704@qq.com> Date: Wed, 16 Jul 2025 20:29:06 +0800 Subject: [PATCH 3/6] fix:doc and test --- README.md | 68 ++++----- adapter_config_test.go | 335 ----------------------------------------- adapter_test.go | 312 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 342 insertions(+), 373 deletions(-) delete mode 100644 adapter_config_test.go diff --git a/README.md b/README.md index 730cf6e..b5cbb4f 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,9 @@ The `Config` struct supports the following options: - `TLSConfig` (*tls.Config): TLS configuration for secure connections (optional) - `Pool` (*redis.Pool): Existing Redis connection pool (optional, if provided, other connection options are ignored) -## Simple Example +## Usage Examples + +### Basic Usage ```go package main @@ -38,58 +40,48 @@ import ( ) func main() { - // Recommended way: Using NewAdapter with Config - config := &redisadapter.Config{ - Network: "tcp", - Address: "127.0.0.1:6379", - } + // Recommended approach using Config + config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379"} a, _ := redisadapter.NewAdapter(config) - // With password - // config := &redisadapter.Config{ - // Network: "tcp", - // Address: "127.0.0.1:6379", - // Password: "123", - // } + // With password authentication + // config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Password: "123"} // a, _ := redisadapter.NewAdapter(config) // With user credentials - // config := &redisadapter.Config{ - // Network: "tcp", - // Address: "127.0.0.1:6379", - // Username: "username", - // Password: "password", - // } + // config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Username: "user", Password: "pass"} // a, _ := redisadapter.NewAdapter(config) // With custom key - // config := &redisadapter.Config{ - // Network: "tcp", - // Address: "127.0.0.1:6379", - // Key: "my_rules", - // } - // a, _ := redisadapter.NewAdapter(config) - - // With connection pool - // pool := &redis.Pool{} - // config := &redisadapter.Config{ - // Pool: pool, - // } + // config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Key: "my_rules"} // a, _ := redisadapter.NewAdapter(config) e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a) - - // Load the policy from DB. e.LoadPolicy() - - // Check the permission. e.Enforce("alice", "data1", "read") + e.SavePolicy() +} +``` + +### With Connection Pool + +```go +package main - // Modify the policy. - // e.AddPolicy(...) - // e.RemovePolicy(...) +import ( + "github.com/casbin/casbin/v2" + "github.com/casbin/redis-adapter/v3" + "github.com/gomodule/redigo/redis" +) - // Save the policy back to DB. +func main() { + pool := &redis.Pool{Dial: func() (redis.Conn, error) { return redis.Dial("tcp", "127.0.0.1:6379") }} + config := &redisadapter.Config{Pool: pool, Key: "casbin_rules"} + a, _ := redisadapter.NewAdapter(config) + + e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a) + e.LoadPolicy() + e.Enforce("alice", "data1", "read") e.SavePolicy() } ``` diff --git a/adapter_config_test.go b/adapter_config_test.go deleted file mode 100644 index 07f975f..0000000 --- a/adapter_config_test.go +++ /dev/null @@ -1,335 +0,0 @@ -// Copyright 2017 The casbin Authors. All Rights Reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package redisadapter - -import ( - "testing" - - "github.com/casbin/casbin/v2" - "github.com/gomodule/redigo/redis" -) - -func TestNewAdapterWithConfig(t *testing.T) { - // Test basic configuration - config := &Config{ - Network: "tcp", - Address: "127.0.0.1:6379", - } - a, _ := NewAdapter(config) - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithPool(t *testing.T) { - // Test with connection pool - pool := &redis.Pool{ - MaxIdle: 3, - MaxActive: 5, - Dial: func() (redis.Conn, error) { - return redis.Dial("tcp", "127.0.0.1:6379") - }, - } - config := &Config{ - Pool: pool, - Key: "pool_test_rules", - } - a, err := NewAdapter(config) - if err != nil { - t.Fatal(err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterErrorCases(t *testing.T) { - // Test error cases - _, err := NewAdapter(nil) - if err == nil { - t.Error("NewAdapter should fail with nil config") - } - - config := &Config{ - Network: "", - Address: "127.0.0.1:6379", - } - _, err = NewAdapter(config) - if err == nil { - t.Error("NewAdapter should fail with empty network") - } - - config = &Config{ - Network: "tcp", - Address: "", - } - _, err = NewAdapter(config) - if err == nil { - t.Error("NewAdapter should fail with empty address") - } -} - -func TestNewAdapterWithPassword(t *testing.T) { - // Test with password authentication - a, err := NewAdapterWithPassword("tcp", "127.0.0.1:6379", "testpass") - if err != nil { - t.Skipf("Password authentication test skipped (Redis may not have auth configured): %v", err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithUser(t *testing.T) { - // Test with username and password authentication - a, err := NewAdapterWithUser("tcp", "127.0.0.1:6379", "testuser", "testpass") - if err != nil { - t.Skipf("User authentication test skipped (Redis may not have auth configured): %v", err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithKey(t *testing.T) { - // Test with custom key - a, err := NewAdapterWithKey("tcp", "127.0.0.1:6379", "custom_casbin_key") - if err != nil { - t.Fatal(err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestIsFiltered(t *testing.T) { - // Test IsFiltered functionality - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initially should not be filtered - if a.IsFiltered() { - t.Error("Adapter should not be filtered initially") - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer and load filtered policy - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Load filtered policy - err = e.LoadFilteredPolicy(Filter{V0: []string{"alice"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy failed: %v", err) - } - - // Now should be filtered - if !a.IsFiltered() { - t.Error("Adapter should be filtered after LoadFilteredPolicy") - } - - // Load all policies (not filtered) - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy failed: %v", err) - } - - // Should not be filtered anymore - if a.IsFiltered() { - t.Error("Adapter should not be filtered after LoadPolicy") - } -} - -func TestFilterFunctionality(t *testing.T) { - // Test various filter scenarios - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Test filter with multiple values - err = e.LoadFilteredPolicy(Filter{V0: []string{"alice", "bob"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with multiple values failed: %v", err) - } - - policies := e.GetPolicy() - if len(policies) == 0 { - t.Error("Expected policies after filtered load") - } - - // Test filter with V1 field - err = e.LoadFilteredPolicy(Filter{V1: []string{"data1"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with V1 failed: %v", err) - } - - policies = e.GetPolicy() - for _, policy := range policies { - if len(policy) > 1 && policy[1] != "data1" { - t.Errorf("Expected policy with data1, got %v", policy) - } - } - - // Test filter with V2 field - err = e.LoadFilteredPolicy(Filter{V2: []string{"read"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with V2 failed: %v", err) - } - - policies = e.GetPolicy() - for _, policy := range policies { - if len(policy) > 2 && policy[2] != "read" { - t.Errorf("Expected policy with read action, got %v", policy) - } - } -} - -func TestRemoveFilteredPolicy(t *testing.T) { - // Test RemoveFilteredPolicy functionality - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Load all policies first - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy failed: %v", err) - } - - // Test RemoveFilteredPolicy - remove alice's policies - err = a.RemoveFilteredPolicy("p", "p", 0, "alice") - if err != nil { - t.Fatalf("RemoveFilteredPolicy failed: %v", err) - } - - // Reload and check - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy after RemoveFilteredPolicy failed: %v", err) - } - - policies := e.GetPolicy() - aliceCount := 0 - for _, policy := range policies { - if len(policy) > 0 && policy[0] == "alice" { - aliceCount++ - } - } - - // We expect fewer alice policies after removal - if aliceCount > 0 { - t.Logf("Alice policies count after removal: %d", aliceCount) - // This is expected if the filter didn't match all alice policies - } -} - -func TestInvalidFilterType(t *testing.T) { - // Test LoadFilteredPolicy with invalid filter type directly on adapter - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create model for testing - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - - // Test with invalid filter type directly on adapter - err = a.LoadFilteredPolicy(e.GetModel(), "invalid_filter_type") - if err == nil { - t.Error("Expected error for invalid filter type") - } else { - t.Logf("Got expected error for invalid filter type: %v", err) - } -} - -func TestEmptyPolicyOperations(t *testing.T) { - // Test operations on empty policies - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Test AddPolicy with single empty string (valid rule) - err = a.AddPolicy("p", "p", []string{""}) - if err != nil { - t.Errorf("AddPolicy with empty string failed: %v", err) - } - - // Test RemovePolicy with single empty string - err = a.RemovePolicy("p", "p", []string{""}) - if err != nil { - t.Errorf("RemovePolicy with empty string failed: %v", err) - } - - // Test AddPolicies with single rule containing empty string - err = a.AddPolicies("p", "p", [][]string{{"", "data1", "read"}}) - if err != nil { - t.Errorf("AddPolicies with rule containing empty string failed: %v", err) - } - - // Test RemovePolicies with single rule containing empty string - err = a.RemovePolicies("p", "p", [][]string{{"", "data1", "read"}}) - if err != nil { - t.Errorf("RemovePolicies with rule containing empty string failed: %v", err) - } -} diff --git a/adapter_test.go b/adapter_test.go index e69f8a5..25b4c59 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -422,3 +422,315 @@ func TestPoolAndOptionsAdapters(t *testing.T) { testUpdatePolicies(t, a) testUpdateFilteredPolicies(t, a) } +func TestNewAdapterWithConfig(t *testing.T) { + // Test basic configuration + config := &Config{ + Network: "tcp", + Address: "127.0.0.1:6379", + } + a, _ := NewAdapter(config) + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithPool(t *testing.T) { + // Test with connection pool + pool := &redis.Pool{ + MaxIdle: 3, + MaxActive: 5, + Dial: func() (redis.Conn, error) { + return redis.Dial("tcp", "127.0.0.1:6379") + }, + } + config := &Config{ + Pool: pool, + Key: "pool_test_rules", + } + a, err := NewAdapter(config) + if err != nil { + t.Fatal(err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterErrorCases(t *testing.T) { + // Test error cases + _, err := NewAdapter(nil) + if err == nil { + t.Error("NewAdapter should fail with nil config") + } + + config := &Config{ + Network: "", + Address: "127.0.0.1:6379", + } + _, err = NewAdapter(config) + if err == nil { + t.Error("NewAdapter should fail with empty network") + } + + config = &Config{ + Network: "tcp", + Address: "", + } + _, err = NewAdapter(config) + if err == nil { + t.Error("NewAdapter should fail with empty address") + } +} + +func TestNewAdapterWithPassword(t *testing.T) { + // Test with password authentication + a, err := NewAdapterWithPassword("tcp", "127.0.0.1:6379", "testpass") + if err != nil { + t.Skipf("Password authentication test skipped (Redis may not have auth configured): %v", err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithUser(t *testing.T) { + // Test with username and password authentication + a, err := NewAdapterWithUser("tcp", "127.0.0.1:6379", "testuser", "testpass") + if err != nil { + t.Skipf("User authentication test skipped (Redis may not have auth configured): %v", err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithKey(t *testing.T) { + // Test with custom key + a, err := NewAdapterWithKey("tcp", "127.0.0.1:6379", "custom_casbin_key") + if err != nil { + t.Fatal(err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestIsFiltered(t *testing.T) { + // Test IsFiltered functionality + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initially should not be filtered + if a.IsFiltered() { + t.Error("Adapter should not be filtered initially") + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer and load filtered policy + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Load filtered policy + err = e.LoadFilteredPolicy(Filter{V0: []string{"alice"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy failed: %v", err) + } + + // Now should be filtered + if !a.IsFiltered() { + t.Error("Adapter should be filtered after LoadFilteredPolicy") + } + + // Load all policies (not filtered) + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy failed: %v", err) + } + + // Should not be filtered anymore + if a.IsFiltered() { + t.Error("Adapter should not be filtered after LoadPolicy") + } +} + +func TestFilterFunctionality(t *testing.T) { + // Test various filter scenarios + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Test filter with multiple values + err = e.LoadFilteredPolicy(Filter{V0: []string{"alice", "bob"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with multiple values failed: %v", err) + } + + policies := e.GetPolicy() + if len(policies) == 0 { + t.Error("Expected policies after filtered load") + } + + // Test filter with V1 field + err = e.LoadFilteredPolicy(Filter{V1: []string{"data1"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with V1 failed: %v", err) + } + + policies = e.GetPolicy() + for _, policy := range policies { + if len(policy) > 1 && policy[1] != "data1" { + t.Errorf("Expected policy with data1, got %v", policy) + } + } + + // Test filter with V2 field + err = e.LoadFilteredPolicy(Filter{V2: []string{"read"}}) + if err != nil { + t.Fatalf("LoadFilteredPolicy with V2 failed: %v", err) + } + + policies = e.GetPolicy() + for _, policy := range policies { + if len(policy) > 2 && policy[2] != "read" { + t.Errorf("Expected policy with read action, got %v", policy) + } + } +} + +func TestRemoveFilteredPolicy(t *testing.T) { + // Test RemoveFilteredPolicy functionality + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Load all policies first + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy failed: %v", err) + } + + // Test RemoveFilteredPolicy - remove alice's policies + err = a.RemoveFilteredPolicy("p", "p", 0, "alice") + if err != nil { + t.Fatalf("RemoveFilteredPolicy failed: %v", err) + } + + // Reload and check + err = a.LoadPolicy(e.GetModel()) + if err != nil { + t.Fatalf("LoadPolicy after RemoveFilteredPolicy failed: %v", err) + } + + policies := e.GetPolicy() + aliceCount := 0 + for _, policy := range policies { + if len(policy) > 0 && policy[0] == "alice" { + aliceCount++ + } + } + + // We expect fewer alice policies after removal + if aliceCount > 0 { + t.Logf("Alice policies count after removal: %d", aliceCount) + // This is expected if the filter didn't match all alice policies + } +} + +func TestInvalidFilterType(t *testing.T) { + // Test LoadFilteredPolicy with invalid filter type directly on adapter + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create model for testing + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + + // Test with invalid filter type directly on adapter + err = a.LoadFilteredPolicy(e.GetModel(), "invalid_filter_type") + if err == nil { + t.Error("Expected error for invalid filter type") + } else { + t.Logf("Got expected error for invalid filter type: %v", err) + } +} + +func TestEmptyPolicyOperations(t *testing.T) { + // Test operations on empty policies + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Test AddPolicy with single empty string (valid rule) + err = a.AddPolicy("p", "p", []string{""}) + if err != nil { + t.Errorf("AddPolicy with empty string failed: %v", err) + } + + // Test RemovePolicy with single empty string + err = a.RemovePolicy("p", "p", []string{""}) + if err != nil { + t.Errorf("RemovePolicy with empty string failed: %v", err) + } + + // Test AddPolicies with single rule containing empty string + err = a.AddPolicies("p", "p", [][]string{{"", "data1", "read"}}) + if err != nil { + t.Errorf("AddPolicies with rule containing empty string failed: %v", err) + } + + // Test RemovePolicies with single rule containing empty string + err = a.RemovePolicies("p", "p", [][]string{{"", "data1", "read"}}) + if err != nil { + t.Errorf("RemovePolicies with rule containing empty string failed: %v", err) + } +} From 9d2b5522c18e05e05cee180e876875ebb1a2ed9e Mon Sep 17 00:00:00 2001 From: yxrrxy <1532529704@qq.com> Date: Wed, 16 Jul 2025 20:40:32 +0800 Subject: [PATCH 4/6] enhance:coverage --- README.md | 12 ++ adapter_config_test.go | 163 +++++++++++++++++++++ adapter_test.go | 312 ----------------------------------------- 3 files changed, 175 insertions(+), 312 deletions(-) create mode 100644 adapter_config_test.go diff --git a/README.md b/README.md index b5cbb4f..54e26fe 100644 --- a/README.md +++ b/README.md @@ -57,8 +57,14 @@ func main() { // a, _ := redisadapter.NewAdapter(config) e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a) + + // Load the policy from DB. e.LoadPolicy() + + // Check permissions e.Enforce("alice", "data1", "read") + + // Save the policy e.SavePolicy() } ``` @@ -80,8 +86,14 @@ func main() { a, _ := redisadapter.NewAdapter(config) e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a) + + // Load the policy from DB. e.LoadPolicy() + + // Check permissions e.Enforce("alice", "data1", "read") + + // Save the policy e.SavePolicy() } ``` diff --git a/adapter_config_test.go b/adapter_config_test.go new file mode 100644 index 0000000..032ba1f --- /dev/null +++ b/adapter_config_test.go @@ -0,0 +1,163 @@ +package redisadapter + +import ( + "testing" + + "github.com/casbin/casbin/v2" + "github.com/gomodule/redigo/redis" +) + +func TestNewAdapterWithConfig(t *testing.T) { + // Test basic configuration + config := &Config{ + Network: "tcp", + Address: "127.0.0.1:6379", + } + a, _ := NewAdapter(config) + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithPool(t *testing.T) { + // Test with connection pool + pool := &redis.Pool{ + MaxIdle: 3, + MaxActive: 5, + Dial: func() (redis.Conn, error) { + return redis.Dial("tcp", "127.0.0.1:6379") + }, + } + config := &Config{ + Pool: pool, + Key: "pool_test_rules", + } + a, err := NewAdapter(config) + if err != nil { + t.Fatal(err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterErrorCases(t *testing.T) { + // Test error cases + _, err := NewAdapter(nil) + if err == nil { + t.Error("NewAdapter should fail with nil config") + } + + config := &Config{ + Network: "", + Address: "127.0.0.1:6379", + } + _, err = NewAdapter(config) + if err == nil { + t.Error("NewAdapter should fail with empty network") + } + + config = &Config{ + Network: "tcp", + Address: "", + } + _, err = NewAdapter(config) + if err == nil { + t.Error("NewAdapter should fail with empty address") + } +} + +func TestNewAdapterWithPassword(t *testing.T) { + // Test with password authentication + a, err := NewAdapterWithPassword("tcp", "127.0.0.1:6379", "testpass") + if err != nil { + t.Skipf("Password authentication test skipped (Redis may not have auth configured): %v", err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithUser(t *testing.T) { + // Test with username and password authentication + a, err := NewAdapterWithUser("tcp", "127.0.0.1:6379", "testuser", "testpass") + if err != nil { + t.Skipf("User authentication test skipped (Redis may not have auth configured): %v", err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestNewAdapterWithKey(t *testing.T) { + // Test with custom key + a, err := NewAdapterWithKey("tcp", "127.0.0.1:6379", "custom_casbin_rules") + if err != nil { + t.Fatal(err) + } + + testSaveLoad(t, a) + testAutoSave(t, a) + testFilteredPolicy(t, a) + testAddPolicies(t, a) + testRemovePolicies(t, a) + testUpdatePolicies(t, a) + testUpdateFilteredPolicies(t, a) +} + +func TestFilterFunctionality(t *testing.T) { + // Test various filter functionality + a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") + if err != nil { + t.Fatal(err) + } + + // Initialize policy + initPolicy(t, a) + + // Create enforcer + e, _ := casbin.NewEnforcer("examples/rbac_model.conf") + e.SetAdapter(a) + + // Test filtering by subject + filter := &Filter{V0: []string{"alice"}} + err = a.LoadFilteredPolicy(e.GetModel(), filter) + if err != nil { + t.Fatal(err) + } + + policies := e.GetPolicy() + if len(policies) == 0 { + t.Log("No policies found for alice (this might be expected)") + } + + // Test filtering by object + filter = &Filter{V0: []string{"", "data1"}} + err = a.LoadFilteredPolicy(e.GetModel(), filter) + if err != nil { + t.Fatal(err) + } + + policies = e.GetPolicy() + t.Logf("Found %d policies for data1", len(policies)) +} diff --git a/adapter_test.go b/adapter_test.go index 25b4c59..e69f8a5 100644 --- a/adapter_test.go +++ b/adapter_test.go @@ -422,315 +422,3 @@ func TestPoolAndOptionsAdapters(t *testing.T) { testUpdatePolicies(t, a) testUpdateFilteredPolicies(t, a) } -func TestNewAdapterWithConfig(t *testing.T) { - // Test basic configuration - config := &Config{ - Network: "tcp", - Address: "127.0.0.1:6379", - } - a, _ := NewAdapter(config) - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithPool(t *testing.T) { - // Test with connection pool - pool := &redis.Pool{ - MaxIdle: 3, - MaxActive: 5, - Dial: func() (redis.Conn, error) { - return redis.Dial("tcp", "127.0.0.1:6379") - }, - } - config := &Config{ - Pool: pool, - Key: "pool_test_rules", - } - a, err := NewAdapter(config) - if err != nil { - t.Fatal(err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterErrorCases(t *testing.T) { - // Test error cases - _, err := NewAdapter(nil) - if err == nil { - t.Error("NewAdapter should fail with nil config") - } - - config := &Config{ - Network: "", - Address: "127.0.0.1:6379", - } - _, err = NewAdapter(config) - if err == nil { - t.Error("NewAdapter should fail with empty network") - } - - config = &Config{ - Network: "tcp", - Address: "", - } - _, err = NewAdapter(config) - if err == nil { - t.Error("NewAdapter should fail with empty address") - } -} - -func TestNewAdapterWithPassword(t *testing.T) { - // Test with password authentication - a, err := NewAdapterWithPassword("tcp", "127.0.0.1:6379", "testpass") - if err != nil { - t.Skipf("Password authentication test skipped (Redis may not have auth configured): %v", err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithUser(t *testing.T) { - // Test with username and password authentication - a, err := NewAdapterWithUser("tcp", "127.0.0.1:6379", "testuser", "testpass") - if err != nil { - t.Skipf("User authentication test skipped (Redis may not have auth configured): %v", err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestNewAdapterWithKey(t *testing.T) { - // Test with custom key - a, err := NewAdapterWithKey("tcp", "127.0.0.1:6379", "custom_casbin_key") - if err != nil { - t.Fatal(err) - } - - testSaveLoad(t, a) - testAutoSave(t, a) - testFilteredPolicy(t, a) - testAddPolicies(t, a) - testRemovePolicies(t, a) - testUpdatePolicies(t, a) - testUpdateFilteredPolicies(t, a) -} - -func TestIsFiltered(t *testing.T) { - // Test IsFiltered functionality - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initially should not be filtered - if a.IsFiltered() { - t.Error("Adapter should not be filtered initially") - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer and load filtered policy - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Load filtered policy - err = e.LoadFilteredPolicy(Filter{V0: []string{"alice"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy failed: %v", err) - } - - // Now should be filtered - if !a.IsFiltered() { - t.Error("Adapter should be filtered after LoadFilteredPolicy") - } - - // Load all policies (not filtered) - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy failed: %v", err) - } - - // Should not be filtered anymore - if a.IsFiltered() { - t.Error("Adapter should not be filtered after LoadPolicy") - } -} - -func TestFilterFunctionality(t *testing.T) { - // Test various filter scenarios - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Test filter with multiple values - err = e.LoadFilteredPolicy(Filter{V0: []string{"alice", "bob"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with multiple values failed: %v", err) - } - - policies := e.GetPolicy() - if len(policies) == 0 { - t.Error("Expected policies after filtered load") - } - - // Test filter with V1 field - err = e.LoadFilteredPolicy(Filter{V1: []string{"data1"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with V1 failed: %v", err) - } - - policies = e.GetPolicy() - for _, policy := range policies { - if len(policy) > 1 && policy[1] != "data1" { - t.Errorf("Expected policy with data1, got %v", policy) - } - } - - // Test filter with V2 field - err = e.LoadFilteredPolicy(Filter{V2: []string{"read"}}) - if err != nil { - t.Fatalf("LoadFilteredPolicy with V2 failed: %v", err) - } - - policies = e.GetPolicy() - for _, policy := range policies { - if len(policy) > 2 && policy[2] != "read" { - t.Errorf("Expected policy with read action, got %v", policy) - } - } -} - -func TestRemoveFilteredPolicy(t *testing.T) { - // Test RemoveFilteredPolicy functionality - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create enforcer - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - e.SetAdapter(a) - - // Load all policies first - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy failed: %v", err) - } - - // Test RemoveFilteredPolicy - remove alice's policies - err = a.RemoveFilteredPolicy("p", "p", 0, "alice") - if err != nil { - t.Fatalf("RemoveFilteredPolicy failed: %v", err) - } - - // Reload and check - err = a.LoadPolicy(e.GetModel()) - if err != nil { - t.Fatalf("LoadPolicy after RemoveFilteredPolicy failed: %v", err) - } - - policies := e.GetPolicy() - aliceCount := 0 - for _, policy := range policies { - if len(policy) > 0 && policy[0] == "alice" { - aliceCount++ - } - } - - // We expect fewer alice policies after removal - if aliceCount > 0 { - t.Logf("Alice policies count after removal: %d", aliceCount) - // This is expected if the filter didn't match all alice policies - } -} - -func TestInvalidFilterType(t *testing.T) { - // Test LoadFilteredPolicy with invalid filter type directly on adapter - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Initialize policy - initPolicy(t, a) - - // Create model for testing - e, _ := casbin.NewEnforcer("examples/rbac_model.conf") - - // Test with invalid filter type directly on adapter - err = a.LoadFilteredPolicy(e.GetModel(), "invalid_filter_type") - if err == nil { - t.Error("Expected error for invalid filter type") - } else { - t.Logf("Got expected error for invalid filter type: %v", err) - } -} - -func TestEmptyPolicyOperations(t *testing.T) { - // Test operations on empty policies - a, err := NewAdapterBasic("tcp", "127.0.0.1:6379") - if err != nil { - t.Fatal(err) - } - - // Test AddPolicy with single empty string (valid rule) - err = a.AddPolicy("p", "p", []string{""}) - if err != nil { - t.Errorf("AddPolicy with empty string failed: %v", err) - } - - // Test RemovePolicy with single empty string - err = a.RemovePolicy("p", "p", []string{""}) - if err != nil { - t.Errorf("RemovePolicy with empty string failed: %v", err) - } - - // Test AddPolicies with single rule containing empty string - err = a.AddPolicies("p", "p", [][]string{{"", "data1", "read"}}) - if err != nil { - t.Errorf("AddPolicies with rule containing empty string failed: %v", err) - } - - // Test RemovePolicies with single rule containing empty string - err = a.RemovePolicies("p", "p", [][]string{{"", "data1", "read"}}) - if err != nil { - t.Errorf("RemovePolicies with rule containing empty string failed: %v", err) - } -} From 927578a11e44fd10d8224afb09feb127a9e21111 Mon Sep 17 00:00:00 2001 From: yxrrxy <1532529704@qq.com> Date: Wed, 16 Jul 2025 22:17:38 +0800 Subject: [PATCH 5/6] enhance:coverage --- README.md | 12 +++++++++--- adapter_config_test.go | 14 ++++++++++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 54e26fe..92cb982 100644 --- a/README.md +++ b/README.md @@ -52,8 +52,10 @@ func main() { // config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Username: "user", Password: "pass"} // a, _ := redisadapter.NewAdapter(config) - // With custom key - // config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Key: "my_rules"} + // With TLS configuration + // var clientTLSConfig tls.Config + // ... + // config := &redisadapter.Config{Network: "tcp", Address: "127.0.0.1:6379", Username: "testAccount", Password: "123456", TLSConfig: &clientTLSConfig} // a, _ := redisadapter.NewAdapter(config) e, _ := casbin.NewEnforcer("examples/rbac_model.conf", a) @@ -61,9 +63,13 @@ func main() { // Load the policy from DB. e.LoadPolicy() - // Check permissions + // Check the permission. e.Enforce("alice", "data1", "read") + // Modify the policy. + // e.AddPolicy(...) + // e.RemovePolicy(...) + // Save the policy e.SavePolicy() } diff --git a/adapter_config_test.go b/adapter_config_test.go index 032ba1f..226e8e0 100644 --- a/adapter_config_test.go +++ b/adapter_config_test.go @@ -1,3 +1,17 @@ +// Copyright 2025 The casbin Authors. All Rights Reserved. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + package redisadapter import ( From fcb0849c0fdabac5c318c8ab020020a30802c3f2 Mon Sep 17 00:00:00 2001 From: Yang Luo Date: Wed, 16 Jul 2025 22:27:33 +0800 Subject: [PATCH 6/6] Update README.md --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 92cb982..b3b311f 100644 --- a/README.md +++ b/README.md @@ -70,7 +70,7 @@ func main() { // e.AddPolicy(...) // e.RemovePolicy(...) - // Save the policy + // Save the policy back to DB. e.SavePolicy() } ``` @@ -96,10 +96,10 @@ func main() { // Load the policy from DB. e.LoadPolicy() - // Check permissions + // Check the permission. e.Enforce("alice", "data1", "read") - // Save the policy + // Save the policy back to DB. e.SavePolicy() } ```