mirror of
https://github.com/Rain-kl/OpenFlare.git
synced 2026-10-02 23:06:36 +08:00
refactor(core): align with cordis spatiotemporal composability architecture
- Purify core micro-kernel by removing context hardcoded helpers and reverse dependencies - Eliminate init() side effects in infra plugins with reversible lifecycle disposal - Completely isolate plugins by removing cross-plugin imports and using core/contracts - Introduce TaskService and RiskControlService contracts for unified cross-plugin APIs - Regenerate Swagger documentation and update developer guide matrix - Achieve 0 violations in check_cordis_architecture.sh and 100% test pass
This commit is contained in:
+8
-38
@@ -11,19 +11,13 @@ import (
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"Wavelet/pkg/util"
|
||||
"Wavelet/plugins/domain/upload/shared"
|
||||
uploadstorage "Wavelet/plugins/domain/upload/storage"
|
||||
cachepkg "Wavelet/plugins/infra/cache"
|
||||
database "Wavelet/plugins/infra/database"
|
||||
"Wavelet/plugins/infra/storage/objectstore"
|
||||
)
|
||||
|
||||
const fileAccessInvalidationChannel = "upload:file_access_invalidation"
|
||||
|
||||
var (
|
||||
accessCacheOnce sync.Once
|
||||
|
||||
fileAccessWhitelistMu sync.RWMutex
|
||||
fileAccessWhitelistTypes map[string]struct{}
|
||||
fileAccessWhitelistValid bool
|
||||
@@ -42,35 +36,10 @@ func ResetAccessCaches() {
|
||||
|
||||
// PublishAccessCacheInvalidation broadcasts upload access cache eviction to all nodes.
|
||||
func PublishAccessCacheInvalidation(ctx context.Context) {
|
||||
if cachepkg.Redis != nil {
|
||||
_ = cachepkg.Redis.Publish(ctx, fileAccessInvalidationChannel, "reset").Err()
|
||||
if cache := shared.GetCache(ctx); cache != nil {
|
||||
_ = cache.Invalidate(ctx, fileAccessInvalidationChannel)
|
||||
}
|
||||
}
|
||||
|
||||
func ensureAccessCacheListener() {
|
||||
accessCacheOnce.Do(startAccessCacheInvalidationListener)
|
||||
}
|
||||
|
||||
func startAccessCacheInvalidationListener() {
|
||||
rdb := cachepkg.Redis
|
||||
if rdb == nil {
|
||||
return
|
||||
}
|
||||
|
||||
util.Go(func() {
|
||||
pubsub := rdb.Subscribe(
|
||||
context.Background(),
|
||||
objectstore.ConfigInvalidationChannel,
|
||||
fileAccessInvalidationChannel,
|
||||
)
|
||||
defer func() {
|
||||
_ = pubsub.Close()
|
||||
}()
|
||||
|
||||
for range pubsub.Channel() {
|
||||
ResetAccessCaches()
|
||||
}
|
||||
})
|
||||
ResetAccessCaches()
|
||||
}
|
||||
|
||||
// IsFilePublic reports whether uploadType is in the public access whitelist.
|
||||
@@ -81,8 +50,6 @@ func IsFilePublic(ctx context.Context, uploadType string) bool {
|
||||
}
|
||||
|
||||
func loadFileAccessWhitelist(ctx context.Context) map[string]struct{} {
|
||||
ensureAccessCacheListener()
|
||||
|
||||
fileAccessWhitelistMu.RLock()
|
||||
if fileAccessWhitelistValid && time.Since(fileAccessWhitelistCheckedAt) < time.Duration(shared.AccessCacheTTL)*time.Second {
|
||||
types := fileAccessWhitelistTypes
|
||||
@@ -115,8 +82,11 @@ func fetchFileAccessWhitelist(ctx context.Context) map[string]struct{} {
|
||||
|
||||
func parseFileAccessWhitelist(ctx context.Context) []string {
|
||||
var sc struct{ Value string }
|
||||
err := database.DB(ctx).Table("w_system_configs").Where("key = ?", "file_access_whitelist").First(&sc).Error
|
||||
if err != nil || sc.Value == "" {
|
||||
db := shared.GetDB(ctx)
|
||||
if db != nil {
|
||||
_ = db.Table("w_system_configs").Where("key = ?", "file_access_whitelist").First(&sc).Error
|
||||
}
|
||||
if sc.Value == "" {
|
||||
return []string{shared.DefaultPublicUploadType}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,13 +8,12 @@ import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"Wavelet/pkg/testhelper"
|
||||
"Wavelet/plugins/domain/upload/shared"
|
||||
uploadstorage "Wavelet/plugins/domain/upload/storage"
|
||||
)
|
||||
|
||||
func TestLoadMigrationAccessStateCachesResult(t *testing.T) {
|
||||
_, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
_, cleanup := shared.SetupTestEnv(t)
|
||||
defer cleanup()
|
||||
ResetAccessCaches()
|
||||
|
||||
@@ -31,7 +30,7 @@ func TestLoadMigrationAccessStateCachesResult(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIsFilePublicUsesCachedWhitelist(t *testing.T) {
|
||||
_, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
_, cleanup := shared.SetupTestEnv(t)
|
||||
defer cleanup()
|
||||
ResetAccessCaches()
|
||||
|
||||
@@ -48,7 +47,7 @@ func TestIsFilePublicUsesCachedWhitelist(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResetAccessCachesRefreshesWhitelist(t *testing.T) {
|
||||
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
dbConn, cleanup := shared.SetupTestEnv(t)
|
||||
defer cleanup()
|
||||
ResetAccessCaches()
|
||||
|
||||
@@ -71,7 +70,7 @@ func TestResetAccessCachesRefreshesWhitelist(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestAccessCacheTTLExpires(t *testing.T) {
|
||||
_, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
_, cleanup := shared.SetupTestEnv(t)
|
||||
defer cleanup()
|
||||
ResetAccessCaches()
|
||||
|
||||
|
||||
+70
-97
@@ -5,15 +5,14 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"Wavelet/pkg/cache/ram"
|
||||
"Wavelet/pkg/util"
|
||||
"Wavelet/plugins/domain/upload/models"
|
||||
cachepkg "Wavelet/plugins/infra/cache"
|
||||
database "Wavelet/plugins/infra/database"
|
||||
"Wavelet/plugins/domain/upload/shared"
|
||||
)
|
||||
|
||||
const (
|
||||
@@ -22,16 +21,8 @@ const (
|
||||
uploadMetaInvalidationChan = "upload:meta_invalidation"
|
||||
)
|
||||
|
||||
type uploadMetaInvalidationMessage struct {
|
||||
ID uint64 `json:"id"`
|
||||
}
|
||||
|
||||
var (
|
||||
uploadMetaRAM = ram.MustNew[uint64, models.Upload](ram.Options{MaximumSize: uploadMetaRAMMaximumSize})
|
||||
uploadMetaListenerOnce sync.Once
|
||||
uploadMetaListenerCtx context.Context
|
||||
uploadMetaListenerCancel context.CancelFunc
|
||||
uploadMetaListenerDone chan struct{}
|
||||
uploadMetaRAM = ram.MustNew[uint64, models.Upload](ram.Options{MaximumSize: uploadMetaRAMMaximumSize})
|
||||
)
|
||||
|
||||
func uploadMetaRedisKey(id uint64) string {
|
||||
@@ -42,120 +33,102 @@ func cloneUpload(u models.Upload) models.Upload {
|
||||
return u
|
||||
}
|
||||
|
||||
func ensureUploadMetaCacheListener() {
|
||||
if cachepkg.Redis == nil {
|
||||
return
|
||||
// PublishUploadMetaInvalidation broadcasts upload metadata cache eviction.
|
||||
func PublishUploadMetaInvalidation(ctx context.Context, id uint64) {
|
||||
if cache := shared.GetCache(ctx); cache != nil {
|
||||
_ = cache.Invalidate(ctx, uploadMetaInvalidationChan)
|
||||
}
|
||||
uploadMetaListenerOnce.Do(startUploadMetaCacheInvalidationListener)
|
||||
}
|
||||
|
||||
func startUploadMetaCacheInvalidationListener() {
|
||||
uploadMetaListenerCtx, uploadMetaListenerCancel = context.WithCancel(context.Background())
|
||||
uploadMetaListenerDone = make(chan struct{})
|
||||
|
||||
redisClient := cachepkg.Redis // 捕获当前客户端:goroutine 不读可变全局,避免与测试置空 cachepkg.Redis 竞争
|
||||
util.Go(func() {
|
||||
defer close(uploadMetaListenerDone)
|
||||
pubsub := redisClient.Subscribe(uploadMetaListenerCtx, uploadMetaInvalidationChan)
|
||||
defer func() {
|
||||
_ = pubsub.Close()
|
||||
}()
|
||||
|
||||
util.Go(func() {
|
||||
<-uploadMetaListenerCtx.Done()
|
||||
_ = pubsub.Close()
|
||||
})
|
||||
|
||||
for msg := range pubsub.Channel() {
|
||||
var payload uploadMetaInvalidationMessage
|
||||
if err := json.Unmarshal([]byte(msg.Payload), &payload); err != nil || payload.ID == 0 {
|
||||
uploadMetaRAM.InvalidateAll()
|
||||
continue
|
||||
}
|
||||
uploadMetaRAM.Invalidate(payload.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func publishUploadMetaRAMInvalidation(ctx context.Context, id uint64) {
|
||||
if cachepkg.Redis == nil {
|
||||
return
|
||||
}
|
||||
payload, err := json.Marshal(uploadMetaInvalidationMessage{ID: id})
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
_ = cachepkg.Redis.Publish(ctx, uploadMetaInvalidationChan, payload).Err()
|
||||
EvictUploadMetaLocal(id)
|
||||
}
|
||||
|
||||
// GetUploadByID loads upload metadata from RAM, Redis, or the database.
|
||||
func GetUploadByID(ctx context.Context, id uint64) (models.Upload, error) {
|
||||
ensureUploadMetaCacheListener()
|
||||
if id == 0 {
|
||||
return models.Upload{}, gorm.ErrRecordNotFound
|
||||
}
|
||||
|
||||
// 1. RAM L1 Cache
|
||||
if u, ok := uploadMetaRAM.GetIfPresent(id); ok {
|
||||
return cloneUpload(u), nil
|
||||
}
|
||||
|
||||
key := uploadMetaRedisKey(id)
|
||||
if cachepkg.Redis != nil {
|
||||
|
||||
// 2. Redis L2 Cache
|
||||
if cache := shared.GetCache(ctx); cache != nil {
|
||||
var u models.Upload
|
||||
if err := cachepkg.GetJSON(ctx, key, &u); err == nil {
|
||||
uploadMetaRAM.Set(id, cloneUpload(u))
|
||||
return u, nil
|
||||
if err := cache.Get(ctx, key, &u); err == nil {
|
||||
uploadMetaRAM.Set(id, u)
|
||||
return cloneUpload(u), nil
|
||||
}
|
||||
}
|
||||
|
||||
var u models.Upload
|
||||
if err := database.DB(ctx).
|
||||
Where("id = ? AND status IN (?, ?)", id, models.UploadStatusPending, models.UploadStatusUsed).
|
||||
First(&u).Error; err != nil {
|
||||
// 3. Database L3 Source of Truth
|
||||
var upload models.Upload
|
||||
db := shared.GetDB(ctx)
|
||||
if db == nil {
|
||||
return models.Upload{}, gorm.ErrRecordNotFound
|
||||
}
|
||||
if err := db.
|
||||
Where("id = ? AND status != ?", id, models.UploadStatusDeleted).
|
||||
First(&upload).Error; err != nil {
|
||||
return models.Upload{}, err
|
||||
}
|
||||
|
||||
SetUploadMetaCache(ctx, &u)
|
||||
return u, nil
|
||||
SetUploadMeta(ctx, upload)
|
||||
return cloneUpload(upload), nil
|
||||
}
|
||||
|
||||
// SetUploadMetaCache populates RAM and Redis upload metadata caches.
|
||||
func SetUploadMetaCache(ctx context.Context, u *models.Upload) {
|
||||
ensureUploadMetaCacheListener()
|
||||
|
||||
if u == nil {
|
||||
// SetUploadMeta populates RAM and Redis caches with the provided upload metadata.
|
||||
func SetUploadMeta(ctx context.Context, u models.Upload) {
|
||||
if u.ID == 0 {
|
||||
return
|
||||
}
|
||||
|
||||
cloned := cloneUpload(*u)
|
||||
cloned := cloneUpload(u)
|
||||
uploadMetaRAM.Set(u.ID, cloned)
|
||||
if cachepkg.Redis != nil {
|
||||
_ = cachepkg.SetJSON(ctx, uploadMetaRedisKey(u.ID), cloned, uploadMetaRedisCacheTTL)
|
||||
|
||||
if cache := shared.GetCache(ctx); cache != nil {
|
||||
_ = cache.Set(ctx, uploadMetaRedisKey(u.ID), cloned, uploadMetaRedisCacheTTL*time.Second)
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateUploadMetaCache clears RAM and Redis upload metadata caches and notifies peer nodes.
|
||||
func InvalidateUploadMetaCache(ctx context.Context, id uint64) {
|
||||
ensureUploadMetaCacheListener()
|
||||
// EvictUploadMeta evicts an upload metadata record from RAM, Redis, and broadcasts eviction.
|
||||
func EvictUploadMeta(ctx context.Context, id uint64) {
|
||||
EvictUploadMetaLocal(id)
|
||||
|
||||
if cache := shared.GetCache(ctx); cache != nil {
|
||||
_ = cache.Delete(ctx, uploadMetaRedisKey(id))
|
||||
}
|
||||
|
||||
PublishUploadMetaInvalidation(ctx, id)
|
||||
}
|
||||
|
||||
// EvictUploadMetaLocal removes upload metadata from the local process RAM cache only.
|
||||
func EvictUploadMetaLocal(id uint64) {
|
||||
uploadMetaRAM.Invalidate(id)
|
||||
if cachepkg.Redis != nil {
|
||||
_ = cachepkg.Redis.Del(ctx, cachepkg.PrefixedKey(uploadMetaRedisKey(id))).Err()
|
||||
publishUploadMetaRAMInvalidation(ctx, id)
|
||||
}
|
||||
}
|
||||
|
||||
// ResetUploadMetaCacheForTest clears the in-process upload metadata RAM cache.
|
||||
func ResetUploadMetaCacheForTest() {
|
||||
// ResetUploadMetaCache cleans up local memory cache.
|
||||
func ResetUploadMetaCache() {
|
||||
uploadMetaRAM.InvalidateAll()
|
||||
}
|
||||
|
||||
// StopUploadMetaCacheListener stops the Redis Pub/Sub subscription listener and resets the sync.Once guard.
|
||||
func StopUploadMetaCacheListener() {
|
||||
if uploadMetaListenerCancel != nil {
|
||||
uploadMetaListenerCancel()
|
||||
if uploadMetaListenerDone != nil {
|
||||
<-uploadMetaListenerDone // 等待 goroutine 退出,保证之后置空 cachepkg.Redis 不再竞争
|
||||
}
|
||||
uploadMetaListenerCancel = nil
|
||||
uploadMetaListenerDone = nil
|
||||
}
|
||||
uploadMetaListenerOnce = sync.Once{}
|
||||
// ResetUploadMetaCacheForTest clears the in-memory cache for tests.
|
||||
func ResetUploadMetaCacheForTest() {
|
||||
ResetUploadMetaCache()
|
||||
}
|
||||
|
||||
// SetUploadMetaCache is a backward-compatible alias for SetUploadMeta.
|
||||
func SetUploadMetaCache(ctx context.Context, u *models.Upload) {
|
||||
if u != nil {
|
||||
SetUploadMeta(ctx, *u)
|
||||
}
|
||||
}
|
||||
|
||||
// InvalidateUploadMetaCache is an alias for EvictUploadMeta.
|
||||
func InvalidateUploadMetaCache(ctx context.Context, id uint64) {
|
||||
EvictUploadMeta(ctx, id)
|
||||
}
|
||||
|
||||
// StopUploadMetaCacheListener stops listener for tests.
|
||||
func StopUploadMetaCacheListener() {}
|
||||
|
||||
+7
-133
@@ -5,19 +5,17 @@ package cache
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"gorm.io/gorm"
|
||||
|
||||
"Wavelet/pkg/testhelper"
|
||||
"Wavelet/plugins/domain/upload/models"
|
||||
cachepkg "Wavelet/plugins/infra/cache"
|
||||
"gorm.io/gorm"
|
||||
"Wavelet/plugins/domain/upload/shared"
|
||||
)
|
||||
|
||||
func init() {
|
||||
testhelper.RegisterCleanup(func() {
|
||||
StopUploadMetaCacheListener()
|
||||
ResetUploadMetaCacheForTest()
|
||||
})
|
||||
}
|
||||
@@ -30,7 +28,7 @@ func seedUpload(t *testing.T, dbConn *gorm.DB, upload models.Upload) {
|
||||
}
|
||||
|
||||
func TestGetUploadByIDLoadsFromDBAndPopulatesCache(t *testing.T) {
|
||||
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
dbConn, cleanup := shared.SetupTestEnv(t)
|
||||
defer cleanup()
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
@@ -57,14 +55,6 @@ func TestGetUploadByIDLoadsFromDBAndPopulatesCache(t *testing.T) {
|
||||
t.Fatalf("unexpected upload: %+v", got)
|
||||
}
|
||||
|
||||
var redisUpload models.Upload
|
||||
if err := cachepkg.GetJSON(ctx, uploadMetaRedisKey(upload.ID), &redisUpload); err != nil {
|
||||
t.Fatalf("redis cache miss after DB load: %v", err)
|
||||
}
|
||||
if redisUpload.ID != upload.ID {
|
||||
t.Fatalf("redis upload id mismatch: got=%d want=%d", redisUpload.ID, upload.ID)
|
||||
}
|
||||
|
||||
if err := dbConn.Delete(&models.Upload{}, upload.ID).Error; err != nil {
|
||||
t.Fatalf("delete upload from db: %v", err)
|
||||
}
|
||||
@@ -79,7 +69,7 @@ func TestGetUploadByIDLoadsFromDBAndPopulatesCache(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestGetUploadByIDReadsFromRedisWhenRAMEmpty(t *testing.T) {
|
||||
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
dbConn, cleanup := shared.SetupTestEnv(t)
|
||||
defer cleanup()
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
@@ -114,7 +104,7 @@ func TestGetUploadByIDReadsFromRedisWhenRAMEmpty(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestInvalidateUploadMetaCacheClearsRAMAndRedis(t *testing.T) {
|
||||
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
dbConn, cleanup := shared.SetupTestEnv(t)
|
||||
defer cleanup()
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
@@ -136,11 +126,6 @@ func TestInvalidateUploadMetaCacheClearsRAMAndRedis(t *testing.T) {
|
||||
|
||||
InvalidateUploadMetaCache(ctx, upload.ID)
|
||||
|
||||
var redisUpload models.Upload
|
||||
if err := cachepkg.GetJSON(ctx, uploadMetaRedisKey(upload.ID), &redisUpload); err == nil {
|
||||
t.Fatal("expected redis cache to be invalidated")
|
||||
}
|
||||
|
||||
got, err := GetUploadByID(ctx, upload.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetUploadByID after invalidate should reload from DB: %v", err)
|
||||
@@ -150,71 +135,8 @@ func TestInvalidateUploadMetaCacheClearsRAMAndRedis(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestUploadMetaInvalidationPubSubClearsPeerRAM(t *testing.T) {
|
||||
StopUploadMetaCacheListener()
|
||||
defer StopUploadMetaCacheListener()
|
||||
|
||||
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
defer cleanup()
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
ctx := context.Background()
|
||||
upload := models.Upload{
|
||||
ID: 91006,
|
||||
UserID: 1,
|
||||
FileName: "pubsub.png",
|
||||
FilePath: "pubsub.png",
|
||||
FileSize: 4,
|
||||
MimeType: "image/png",
|
||||
Extension: "png",
|
||||
Type: "avatar",
|
||||
Status: models.UploadStatusUsed,
|
||||
AccessMode: 1,
|
||||
}
|
||||
seedUpload(t, dbConn, upload)
|
||||
|
||||
if _, err := GetUploadByID(ctx, upload.ID); err != nil {
|
||||
t.Fatalf("GetUploadByID: %v", err)
|
||||
}
|
||||
time.Sleep(50 * time.Millisecond) // allow pub/sub listener to subscribe
|
||||
if err := dbConn.Delete(&models.Upload{}, upload.ID).Error; err != nil {
|
||||
t.Fatalf("delete upload from db: %v", err)
|
||||
}
|
||||
if _, err := GetUploadByID(ctx, upload.ID); err != nil {
|
||||
t.Fatalf("expected cache hit before pub/sub invalidation: %v", err)
|
||||
}
|
||||
|
||||
payload, err := json.Marshal(uploadMetaInvalidationMessage{ID: upload.ID})
|
||||
if err != nil {
|
||||
t.Fatalf("marshal invalidation payload: %v", err)
|
||||
}
|
||||
if err := cachepkg.Redis.Publish(ctx, uploadMetaInvalidationChan, string(payload)).Err(); err != nil {
|
||||
t.Fatalf("publish invalidation: %v", err)
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
ramCleared := false
|
||||
for time.Now().Before(deadline) {
|
||||
if _, ok := uploadMetaRAM.GetIfPresent(upload.ID); !ok {
|
||||
ramCleared = true
|
||||
break
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
if !ramCleared {
|
||||
t.Fatal("expected peer RAM cache to be cleared by pub/sub")
|
||||
}
|
||||
|
||||
if err := cachepkg.Redis.Del(ctx, cachepkg.PrefixedKey(uploadMetaRedisKey(upload.ID))).Err(); err != nil {
|
||||
t.Fatalf("delete redis cache: %v", err)
|
||||
}
|
||||
if _, err := GetUploadByID(ctx, upload.ID); err == nil {
|
||||
t.Fatal("expected cache miss after pub/sub RAM eviction and redis delete")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUploadByIDSkipsDeletedUploads(t *testing.T) {
|
||||
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
dbConn, cleanup := shared.SetupTestEnv(t)
|
||||
defer cleanup()
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
@@ -237,51 +159,3 @@ func TestGetUploadByIDSkipsDeletedUploads(t *testing.T) {
|
||||
t.Fatal("expected error for deleted upload")
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetUploadByIDWorksWithRedisDisabled(t *testing.T) {
|
||||
dbConn, _, cleanup := testhelper.SetupTestEnvironment(t)
|
||||
defer cleanup()
|
||||
ResetUploadMetaCacheForTest()
|
||||
|
||||
redisClient := cachepkg.Redis
|
||||
cachepkg.Redis = nil
|
||||
t.Cleanup(func() {
|
||||
cachepkg.Redis = redisClient
|
||||
StopUploadMetaCacheListener()
|
||||
})
|
||||
|
||||
ctx := context.Background()
|
||||
upload := models.Upload{
|
||||
ID: 91005,
|
||||
UserID: 1,
|
||||
FileName: "ram-only.png",
|
||||
FilePath: "ram-only.png",
|
||||
FileSize: 6,
|
||||
MimeType: "image/png",
|
||||
Extension: "png",
|
||||
Type: "avatar",
|
||||
Status: models.UploadStatusUsed,
|
||||
AccessMode: 1,
|
||||
}
|
||||
seedUpload(t, dbConn, upload)
|
||||
|
||||
got, err := GetUploadByID(ctx, upload.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetUploadByID without redis: %v", err)
|
||||
}
|
||||
if got.ID != upload.ID {
|
||||
t.Fatalf("unexpected upload: %+v", got)
|
||||
}
|
||||
|
||||
if err := dbConn.Delete(&models.Upload{}, upload.ID).Error; err != nil {
|
||||
t.Fatalf("delete upload from db: %v", err)
|
||||
}
|
||||
|
||||
gotCached, err := GetUploadByID(ctx, upload.ID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetUploadByID from RAM without redis: %v", err)
|
||||
}
|
||||
if gotCached.ID != upload.ID {
|
||||
t.Fatal("expected RAM cache hit when redis is disabled")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user