package httpx import ( "context" "encoding/json" "net/http" "strconv" "strings" "testing" "time" "go.uber.org/zap" "spark-server/internal/infrastructure/id" authlogic "spark-server/internal/module/auth/logic" convlogic "spark-server/internal/module/conversation/logic" convmodel "spark-server/internal/module/conversation/model" favlogic "spark-server/internal/module/favorite/logic" favmodel "spark-server/internal/module/favorite/model" msgentity "spark-server/internal/module/message/entity" msgmodel "spark-server/internal/module/message/model" uploadlogic "spark-server/internal/module/upload/logic" userlogic "spark-server/internal/module/user/logic" usermodel "spark-server/internal/module/user/model" "spark-server/internal/pkg/errs" ) type stubPresign struct{} func (s *stubPresign) PresignPut(_ context.Context, key, _ string, _ int64, _ time.Duration) (string, error) { return "http://rustfs.test/" + key + "?sig=1", nil } func (s *stubPresign) PresignGet(_ context.Context, key string, _ time.Duration) (string, error) { return "http://rustfs.test/" + key + "?get=1", nil } func testFileEngine(t *testing.T) (*ginEngineBundle, *id.Generator, *msgmodel.MemoryRepo, *convmodel.MemoryP2P) { t.Helper() j, err := authlogic.NewJWT(&authlogic.Options{Secret: "test-secret-test-secret", Issuer: "spark-im"}) if err != nil { t.Fatal(err) } ids, err := id.New(1) if err != nil { t.Fatal(err) } users := userlogic.NewService(usermodel.NewMemoryRepo(), usermodel.NewMemoryCodes(), ids, nil) msgs := msgmodel.NewMemoryRepo() convs := convmodel.NewMemoryP2P() files := uploadlogic.NewService(&stubPresign{}, uploadlogic.Options{ Now: func() time.Time { return time.Date(2026, 9, 17, 0, 0, 0, 0, time.UTC) }, }) favs := favlogic.NewService(favmodel.NewMemoryRepo(), msgs, convs, nil, ids) r := NewEngine(zap.NewNop(), &EngineDeps{ Auth: j, Login: authlogic.NewLoginService(j, users), Users: users, Files: files, Favorites: favs, }) return &ginEngineBundle{engine: r}, ids, msgs, convs } func TestUploadCredentialHTTP(t *testing.T) { bundle, _, _, _ := testFileEngine(t) h := bundle.engine tok := loginUser(t, h, "test01@spark.com", 1) env := authJSON(t, h, &tok, http.MethodPost, "/v1/files", `{"purpose":"chat_image","mime":"image/jpeg","size":1024}`) if env.Code != "0" { t.Fatalf("%+v", env) } var cred struct { ObjectKey string `json:"object_key"` Method string `json:"method"` } if err := json.Unmarshal(env.Data, &cred); err != nil || cred.Method != "PUT" || !strings.Contains(cred.ObjectKey, "chat/image/20260917/") { t.Fatalf("%s %v", env.Data, err) } bad := authJSON(t, h, &tok, http.MethodPost, "/v1/files", `{"purpose":"avatar","mime":"application/pdf","size":10}`) if bad.Code != errs.ErrInvalidArgument.Code { t.Fatalf("mime %+v", bad) } batch := authJSON(t, h, &tok, http.MethodPost, "/v1/files/batch", `{"items":[{"purpose":"chat_image","mime":"image/jpeg","size":1024},{"purpose":"chat_image","mime":"image/jpeg","size":2048}]}`) if batch.Code != "0" { t.Fatalf("batch %+v", batch) } var set struct { Items []struct { ObjectKey string `json:"object_key"` } `json:"items"` } if err := json.Unmarshal(batch.Data, &set); err != nil || len(set.Items) != 2 || set.Items[0].ObjectKey == set.Items[1].ObjectKey { t.Fatalf("%s %v", batch.Data, err) } } func TestFavoriteTextHTTP(t *testing.T) { bundle, ids, msgs, convs := testFileEngine(t) h := bundle.engine tok := loginUser(t, h, "test01@spark.com", 1) p2p := convlogic.NewP2PService(convs, ids) convID, err := p2p.EnsureP2P(t.Context(), tok.UserID, tok.UserID+1) if err != nil { t.Fatal(err) } raw, err := msgentity.NewText("keep").Marshal() if err != nil { t.Fatal(err) } msgID, err := ids.NextID() if err != nil { t.Fatal(err) } if err := msgs.Create(t.Context(), &msgentity.Message{ ID: msgID, ConversationID: convID, SenderID: tok.UserID, ClientMsgID: "http-1", Type: msgentity.TypeText, Content: raw, Status: msgentity.StatusNormal, }); err != nil { t.Fatal(err) } body := `{"msg_id":` + strconv.FormatInt(msgID, 10) + `}` env := authJSON(t, h, &tok, http.MethodPost, "/v1/favorites", body) if env.Code != "0" { t.Fatalf("fav %+v", env) } list := authJSON(t, h, &tok, http.MethodGet, "/v1/favorites", "") if list.Code != "0" || !strings.Contains(string(list.Data), `"keep"`) { t.Fatalf("list %+v", list) } }