Files
2026-04-08 18:48:42 +02:00

289 lines
7.3 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package cache_test
import (
"context"
"fmt"
"os"
"testing"
"time"
"github.com/absmach/magistrala/auth"
"github.com/absmach/magistrala/auth/cache"
"github.com/absmach/magistrala/internal/testsutil"
"github.com/absmach/magistrala/pkg/errors"
"github.com/redis/go-redis/v9"
"github.com/stretchr/testify/assert"
)
var (
storeClient *redis.Client
storeURL string
)
func TestMain(m *testing.M) {
code := testsutil.RunRedisTest(m, &storeClient, &storeURL)
os.Exit(code)
}
func setupRedisTokensClient() auth.UserActiveTokensCache {
tc, err := cache.NewUserActiveTokensCache(storeClient)
if err != nil {
panic(err)
}
return tc
}
func TestTokenSave(t *testing.T) {
storeClient.FlushAll(context.Background())
tokensCache := setupRedisTokensClient()
userID := testsutil.GenerateUUID(t)
tokenID := testsutil.GenerateUUID(t)
cases := []struct {
desc string
userID string
tokenID string
description string
expiry time.Time
err error
}{
{
desc: "Save active token",
userID: userID,
tokenID: tokenID,
description: "Test token",
expiry: time.Now().Add(10 * time.Minute),
err: nil,
},
{
desc: "Save already cached token",
userID: userID,
tokenID: tokenID,
description: "Updated token",
expiry: time.Now().Add(10 * time.Minute),
err: nil,
},
{
desc: "Save another token for same user",
userID: userID,
tokenID: testsutil.GenerateUUID(t),
description: "Another token",
expiry: time.Now().Add(10 * time.Minute),
err: nil,
},
{
desc: "Save token with empty id",
userID: userID,
tokenID: "",
description: "Empty ID token",
expiry: time.Now().Add(10 * time.Minute),
err: nil,
},
{
desc: "Save token with empty description",
userID: userID,
tokenID: testsutil.GenerateUUID(t),
description: "",
expiry: time.Now().Add(10 * time.Minute),
err: nil,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
err := tokensCache.SaveActive(context.Background(), tc.userID, tc.tokenID, tc.description, tc.expiry)
if err == nil {
ok, err := tokensCache.IsActive(context.Background(), tc.tokenID)
assert.NoError(t, err)
assert.True(t, ok)
}
assert.True(t, errors.Contains(err, tc.err))
})
}
}
func TestTokenContains(t *testing.T) {
storeClient.FlushAll(context.Background())
tokensCache := setupRedisTokensClient()
userID := testsutil.GenerateUUID(t)
tokenID := testsutil.GenerateUUID(t)
err := tokensCache.SaveActive(context.Background(), userID, tokenID, "Test token", time.Now().Add(10*time.Minute))
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
cases := []struct {
desc string
userID string
tokenID string
ok bool
}{
{
desc: "IsActive for existing token",
userID: userID,
tokenID: tokenID,
ok: true,
},
{
desc: "IsActive for non existing token",
userID: userID,
tokenID: testsutil.GenerateUUID(t),
},
{
desc: "IsActive with empty token id",
userID: userID,
tokenID: "",
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
ok, err := tokensCache.IsActive(context.Background(), tc.tokenID)
if tc.ok {
assert.NoError(t, err)
}
assert.Equal(t, tc.ok, ok)
})
}
}
func TestTokenRemove(t *testing.T) {
storeClient.FlushAll(context.Background())
tokensCache := setupRedisTokensClient()
userID := testsutil.GenerateUUID(t)
num := 10
var tokenIDs []string
for i := range num {
tokenID := testsutil.GenerateUUID(t)
err := tokensCache.SaveActive(context.Background(), userID, tokenID, fmt.Sprintf("Token %d", i), time.Now().Add(10*time.Minute))
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
tokenIDs = append(tokenIDs, tokenID)
}
cases := []struct {
desc string
userID string
tokenID string
err error
}{
{
desc: "Remove an existing token from cache",
userID: userID,
tokenID: tokenIDs[0],
err: nil,
},
{
desc: "Remove token with empty id from cache",
userID: userID,
tokenID: "",
err: nil,
},
{
desc: "Remove non existing id from cache",
userID: userID,
tokenID: testsutil.GenerateUUID(t),
err: nil,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
err := tokensCache.RemoveActive(context.Background(), tc.userID, tc.tokenID)
assert.True(t, errors.Contains(err, tc.err))
if err == nil {
ok, err := tokensCache.IsActive(context.Background(), tc.tokenID)
assert.NoError(t, err)
assert.False(t, ok)
}
})
}
}
func TestListUserTokens(t *testing.T) {
storeClient.FlushAll(context.Background())
tokensCache := setupRedisTokensClient()
userID := testsutil.GenerateUUID(t)
userID2 := testsutil.GenerateUUID(t)
num := 5
var expectedTokens []auth.TokenInfo
for i := range num {
tokenID := testsutil.GenerateUUID(t)
description := fmt.Sprintf("Token %d", i)
err := tokensCache.SaveActive(context.Background(), userID, tokenID, description, time.Now().Add(10*time.Minute))
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
expectedTokens = append(expectedTokens, auth.TokenInfo{
ID: tokenID,
Description: description,
})
}
tokenID2 := testsutil.GenerateUUID(t)
desc2 := "User 2 token"
err := tokensCache.SaveActive(context.Background(), userID2, tokenID2, desc2, time.Now().Add(10*time.Minute))
assert.Nil(t, err, fmt.Sprintf("Unexpected error while trying to save: %s", err))
cases := []struct {
desc string
userID string
expectedCount int
expectedTokens []auth.TokenInfo
err error
}{
{
desc: "List all tokens for user with multiple tokens",
userID: userID,
expectedCount: num,
expectedTokens: expectedTokens,
err: nil,
},
{
desc: "List tokens for user with single token",
userID: userID2,
expectedCount: 1,
expectedTokens: []auth.TokenInfo{{ID: tokenID2, Description: desc2}},
err: nil,
},
{
desc: "List tokens for user with no tokens",
userID: testsutil.GenerateUUID(t),
expectedCount: 0,
expectedTokens: nil,
err: nil,
},
}
for _, tc := range cases {
t.Run(tc.desc, func(t *testing.T) {
tokens, err := tokensCache.ListUserTokens(context.Background(), tc.userID)
assert.True(t, errors.Contains(err, tc.err))
assert.Equal(t, tc.expectedCount, len(tokens))
if tc.expectedTokens != nil {
assert.ElementsMatch(t, tc.expectedTokens, tokens)
}
})
}
t.Run("Cleanup expired tokens from list", func(t *testing.T) {
// Remove one token directly from Redis to simulate expiration
err := tokensCache.RemoveActive(context.Background(), userID, expectedTokens[0].ID)
assert.NoError(t, err)
// List should now return only valid tokens
tokens, err := tokensCache.ListUserTokens(context.Background(), userID)
assert.NoError(t, err)
assert.Equal(t, num-1, len(tokens))
// Check that the removed token is not in the list
for _, token := range tokens {
assert.NotEqual(t, expectedTokens[0].ID, token.ID)
}
})
}