170 lines
6.3 KiB
Go
170 lines
6.3 KiB
Go
package resource
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"hyapp-admin-server/internal/appctx"
|
|
"hyapp-admin-server/internal/integration/walletclient"
|
|
"hyapp-admin-server/internal/middleware"
|
|
walletv1 "hyapp.local/api/proto/wallet/v1"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func TestUpdateMP4ResourceLayoutsPreservesResourceFields(t *testing.T) {
|
|
wallet := &mockResourceWallet{resources: map[int64]*walletv1.Resource{
|
|
11: {
|
|
AppCode: "lalu",
|
|
ResourceId: 11,
|
|
ResourceCode: "profile_card_star",
|
|
ResourceType: resourceTypeProfileCard,
|
|
Name: "星光资料卡",
|
|
Status: "active",
|
|
Grantable: true,
|
|
GrantStrategy: "extend_expiry",
|
|
WalletAssetType: "RESOURCE",
|
|
WalletAssetAmount: 1,
|
|
UsageScopes: []string{resourceTypeProfileCard},
|
|
AssetUrl: "https://cdn.example.test/profile_card.png",
|
|
PreviewUrl: "https://cdn.example.test/profile_card_cover.png",
|
|
AnimationUrl: "https://cdn.example.test/profile_card.mp4?token=1",
|
|
MetadataJson: `{"profile_card_layout":{"source_width":1136,"source_height":1680,"color_content_width":750,"content_top":117,"content_bottom":1666,"content_height":1550,"detect_version":1}}`,
|
|
SortOrder: 9,
|
|
ManagerGrantEnabled: true,
|
|
PriceType: resourcePriceTypeCoin,
|
|
CoinPrice: 100,
|
|
},
|
|
}}
|
|
router := newResourceHandlerTestRouter(New(wallet, nil, nil, time.Second, nil))
|
|
metadataJSON := `{
|
|
"animation_format":"mp4",
|
|
"mp4_alpha_layout":{
|
|
"alpha_layout":"alpha_left_rgb_right",
|
|
"video_w":1500,
|
|
"video_h":1334,
|
|
"rgb_frame":[750,0,750,1334],
|
|
"alpha_frame":[0,0,750,1334],
|
|
"confirmed":true,
|
|
"detect_version":1
|
|
},
|
|
"debug":true
|
|
}`
|
|
body := fmt.Sprintf(`{"items":[{"resourceId":11,"metadataJson":%q}]}`, metadataJSON)
|
|
|
|
recorder := httptest.NewRecorder()
|
|
request := httptest.NewRequest(http.MethodPut, "/admin/resources/mp4-layouts", strings.NewReader(body))
|
|
request.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(recorder, request)
|
|
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("batch mp4 layout update status mismatch: %d %s", recorder.Code, recorder.Body.String())
|
|
}
|
|
if len(wallet.updates) != 1 {
|
|
t.Fatalf("expected one wallet update, got %d", len(wallet.updates))
|
|
}
|
|
update := wallet.updates[0]
|
|
if update.GetResourceId() != 11 || update.GetName() != "星光资料卡" || update.GetStatus() != "active" || update.GetCoinPrice() != 100 {
|
|
t.Fatalf("resource fields should be preserved: %+v", update)
|
|
}
|
|
if update.ManagerGrantEnabled == nil || !update.GetManagerGrantEnabled() || update.GetOperatorUserId() != 7 {
|
|
t.Fatalf("operator and manager grant should be preserved: %+v", update)
|
|
}
|
|
if !strings.Contains(update.GetMetadataJson(), `"profile_card_layout"`) || !strings.Contains(update.GetMetadataJson(), `"mp4_alpha_layout"`) {
|
|
t.Fatalf("profile card and mp4 metadata should both be kept: %s", update.GetMetadataJson())
|
|
}
|
|
if strings.Contains(update.GetMetadataJson(), "debug") {
|
|
t.Fatalf("debug metadata should be dropped: %s", update.GetMetadataJson())
|
|
}
|
|
}
|
|
|
|
func TestUpdateMP4ResourceLayoutsIgnoresNonMP4Resource(t *testing.T) {
|
|
wallet := &mockResourceWallet{resources: map[int64]*walletv1.Resource{
|
|
12: {
|
|
AppCode: "lalu",
|
|
ResourceId: 12,
|
|
ResourceCode: "gift_rose",
|
|
ResourceType: "gift",
|
|
Name: "玫瑰",
|
|
Status: "active",
|
|
AnimationUrl: "https://cdn.example.test/gift.svga",
|
|
MetadataJson: "{}",
|
|
GrantStrategy: "increase_quantity",
|
|
PriceType: resourcePriceTypeCoin,
|
|
CoinPrice: 10,
|
|
},
|
|
}}
|
|
router := newResourceHandlerTestRouter(New(wallet, nil, nil, time.Second, nil))
|
|
body := fmt.Sprintf(`{"items":[{"resourceId":12,"metadataJson":%q}]}`, mp4AlphaLayoutMetadataJSON("alpha_left_rgb_right", true))
|
|
|
|
recorder := httptest.NewRecorder()
|
|
request := httptest.NewRequest(http.MethodPut, "/admin/resources/mp4-layouts", strings.NewReader(body))
|
|
request.Header.Set("Content-Type", "application/json")
|
|
router.ServeHTTP(recorder, request)
|
|
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("non-mp4 resource should be ignored, got %d %s", recorder.Code, recorder.Body.String())
|
|
}
|
|
if len(wallet.updates) != 0 {
|
|
t.Fatalf("non-mp4 resource should not update wallet")
|
|
}
|
|
}
|
|
|
|
func newResourceHandlerTestRouter(handler *Handler) *gin.Engine {
|
|
gin.SetMode(gin.TestMode)
|
|
router := gin.New()
|
|
router.Use(func(c *gin.Context) {
|
|
c.Request = c.Request.WithContext(appctx.WithContext(c.Request.Context(), "lalu"))
|
|
c.Set(middleware.ContextRequestID, "resource-handler-test")
|
|
c.Set(middleware.ContextUserID, uint(7))
|
|
c.Set(middleware.ContextUsername, "tester")
|
|
c.Next()
|
|
})
|
|
router.PUT("/admin/resources/mp4-layouts", handler.UpdateMP4ResourceLayouts)
|
|
return router
|
|
}
|
|
|
|
type mockResourceWallet struct {
|
|
walletclient.Client
|
|
resources map[int64]*walletv1.Resource
|
|
updates []*walletv1.UpdateResourceRequest
|
|
}
|
|
|
|
func (m *mockResourceWallet) GetResource(ctx context.Context, req *walletv1.GetResourceRequest) (*walletv1.GetResourceResponse, error) {
|
|
resource := m.resources[req.GetResourceId()]
|
|
if resource == nil {
|
|
return nil, fmt.Errorf("resource not found")
|
|
}
|
|
return &walletv1.GetResourceResponse{Resource: resource}, nil
|
|
}
|
|
|
|
func (m *mockResourceWallet) UpdateResource(ctx context.Context, req *walletv1.UpdateResourceRequest) (*walletv1.ResourceResponse, error) {
|
|
m.updates = append(m.updates, req)
|
|
return &walletv1.ResourceResponse{Resource: &walletv1.Resource{
|
|
AppCode: req.GetAppCode(),
|
|
ResourceId: req.GetResourceId(),
|
|
ResourceCode: req.GetResourceCode(),
|
|
ResourceType: req.GetResourceType(),
|
|
Name: req.GetName(),
|
|
Status: req.GetStatus(),
|
|
Grantable: req.GetGrantable(),
|
|
GrantStrategy: req.GetGrantStrategy(),
|
|
WalletAssetType: req.GetWalletAssetType(),
|
|
WalletAssetAmount: req.GetWalletAssetAmount(),
|
|
UsageScopes: req.GetUsageScopes(),
|
|
AssetUrl: req.GetAssetUrl(),
|
|
PreviewUrl: req.GetPreviewUrl(),
|
|
AnimationUrl: req.GetAnimationUrl(),
|
|
MetadataJson: req.GetMetadataJson(),
|
|
SortOrder: req.GetSortOrder(),
|
|
ManagerGrantEnabled: req.GetManagerGrantEnabled(),
|
|
PriceType: req.GetPriceType(),
|
|
CoinPrice: req.GetCoinPrice(),
|
|
}}, nil
|
|
}
|