8761e47f78
Add cache-aside + singleflight to KOL marketplace list/detail, my-subscription prompts, and generation schema lookups. Invalidate the shared public-facing caches when subscriptions are added/revoked (ops) or KOL profile is updated. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
87 lines
2.6 KiB
Go
87 lines
2.6 KiB
Go
package app
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/geo-platform/tenant-api/internal/shared/auth"
|
|
sharedcache "github.com/geo-platform/tenant-api/internal/shared/cache"
|
|
"github.com/geo-platform/tenant-api/internal/tenant/repository"
|
|
)
|
|
|
|
type stubKolProfileRepository struct {
|
|
profile repository.KolProfile
|
|
}
|
|
|
|
func (r *stubKolProfileRepository) GetByUser(ctx context.Context, tenantID, userID int64) (*repository.KolProfile, error) {
|
|
if r.profile.TenantID != tenantID || r.profile.UserID != userID {
|
|
return nil, nil
|
|
}
|
|
profile := r.profile
|
|
return &profile, nil
|
|
}
|
|
|
|
func (r *stubKolProfileRepository) GetByID(ctx context.Context, id int64) (*repository.KolProfile, error) {
|
|
if r.profile.ID != id {
|
|
return nil, nil
|
|
}
|
|
profile := r.profile
|
|
return &profile, nil
|
|
}
|
|
|
|
func (r *stubKolProfileRepository) Update(ctx context.Context, tenantID int64, input repository.UpdateKolProfileInput) error {
|
|
if r.profile.TenantID != tenantID || r.profile.ID != input.ID {
|
|
return errors.New("unexpected profile update target")
|
|
}
|
|
r.profile.DisplayName = input.DisplayName
|
|
r.profile.Bio = input.Bio
|
|
r.profile.AvatarURL = input.AvatarURL
|
|
r.profile.UpdatedAt = time.Now()
|
|
return nil
|
|
}
|
|
|
|
func TestUpdateKolProfileInvalidatesPublicCaches(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
cache := sharedcache.New("memory", nil, sharedcache.WithNamespace("test"), sharedcache.WithMetrics(false), sharedcache.WithJitterRatio(0))
|
|
tenantID := int64(42)
|
|
|
|
keys := []string{
|
|
workspaceKolCardsCacheKey(tenantID),
|
|
kolMarketplacePackageListCacheKey(tenantID, MarketFilter{Limit: 20}),
|
|
kolMarketplacePackageDetailCacheKey(tenantID, 7),
|
|
kolSubscriptionPromptListCacheKey(tenantID),
|
|
kolSubscriptionPromptSchemaCacheKey(tenantID, 9),
|
|
}
|
|
for _, key := range keys {
|
|
if err := cache.Set(ctx, key, []byte(`{"value":"stale"}`), time.Minute); err != nil {
|
|
t.Fatalf("seed cache %s: %v", key, err)
|
|
}
|
|
}
|
|
|
|
repo := &stubKolProfileRepository{profile: repository.KolProfile{
|
|
ID: 3,
|
|
TenantID: tenantID,
|
|
UserID: 5,
|
|
DisplayName: "old name",
|
|
MarketEnabled: true,
|
|
Status: "active",
|
|
CreatedAt: time.Now(),
|
|
UpdatedAt: time.Now(),
|
|
}}
|
|
svc := NewKolProfileService(repo).WithCache(cache)
|
|
|
|
if _, err := svc.UpdateProfile(ctx, auth.Actor{TenantID: tenantID, UserID: 5}, UpdateKolProfileServiceInput{DisplayName: "new name"}); err != nil {
|
|
t.Fatalf("UpdateProfile() error = %v", err)
|
|
}
|
|
|
|
for _, key := range keys {
|
|
if _, err := cache.Get(ctx, key); !errors.Is(err, sharedcache.ErrNotFound) {
|
|
t.Fatalf("cache key %s should be invalidated, got err %v", key, err)
|
|
}
|
|
}
|
|
}
|