package api import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "os" "strings" "testing" "git.ipao.vip/rogee/creator-hub/internal/account" "git.ipao.vip/rogee/creator-hub/internal/creator" "github.com/gofiber/fiber/v3" ) func TestPrivateMessageRoutes(t *testing.T) { databaseURL := os.Getenv("CREATORHUB_POSTGRES_TEST_URL") if databaseURL == "" { t.Skip("requires CREATORHUB_POSTGRES_TEST_URL") } store, accounts, ctx := openCreatorIntegrationStoreForAPITest(t, databaseURL) owner := account.Account{ID: "private-owner", Platform: creator.PlatformDouyin, Name: "本人", PlatformAccountKey: "123", CredentialReference: account.CredentialReference{ID: "private-credential", Provider: "os_keyring"}, CredentialKey: "creatorhub/private-owner/cookies"} bridge := &testCredentialBridge{values: make(map[string]string)} if err := accounts.CreateAccount(ctx, owner, bridge); err != nil { t.Fatal(err) } if _, err := store.RecordVerifiedLoginResult(ctx, owner.ID, "123"); err != nil { t.Fatal(err) } if _, err := store.SetListenerEnabled(ctx, owner.ID, true); err != nil { t.Fatal(err) } calls := 0 send := func(ctx context.Context, i creator.PrivateMessageSendInput, r creator.PrivateMessageReservation) PrivateSendResult { calls++ return PrivateSendResult{State: "succeeded", ServerID: "1001"} } app := fiber.New() registerPrivateMessageRoutes(app, store, send) input := fmt.Sprintf(`{"request_id":"one","account_id":%q,"peer_uid":"456","text":"你好"}`, owner.ID) for n := 0; n < 2; n++ { req := httptest.NewRequest("POST", "/api/creator/private-messages/send", strings.NewReader(input)) req.Header.Set("Content-Type", "application/json") res, err := app.Test(req) if err != nil { t.Fatal(err) } if res.StatusCode != 200 { t.Fatalf("send status=%d", res.StatusCode) } var msg creator.PrivateMessage if err := json.NewDecoder(res.Body).Decode(&msg); err != nil { t.Fatal(err) } res.Body.Close() if msg.State != "succeeded" { t.Fatalf("message=%+v", msg) } } if calls != 1 { t.Fatalf("send calls=%d", calls) } for _, path := range []string{"/api/creator/private-messages/conversations?page=1&page_size=25", "/api/creator/private-messages/messages?account_id=" + owner.ID + "&peer_uid=456", "/api/creator/private-messages/status"} { res, err := app.Test(httptest.NewRequest("GET", path, nil)) if err != nil { t.Fatal(err) } if res.StatusCode != 200 { t.Fatalf("%s status=%d", path, res.StatusCode) } res.Body.Close() } res, err := app.Test(httptest.NewRequest("GET", "/api/creator/private-messages/messages?account_id="+owner.ID+"&peer_uid=bad", nil)) if err != nil { t.Fatal(err) } if res.StatusCode != http.StatusBadRequest { t.Fatalf("bad uid=%d", res.StatusCode) } res.Body.Close() if _, err := store.SetListenerEnabled(ctx, owner.ID, false); err != nil { t.Fatal(err) } input = strings.Replace(input, `"one"`, `"two"`, 1) req := httptest.NewRequest("POST", "/api/creator/private-messages/send", strings.NewReader(input)) req.Header.Set("Content-Type", "application/json") res, err = app.Test(req) if err != nil { t.Fatal(err) } res.Body.Close() if res.StatusCode != 409 || calls != 1 { t.Fatalf("disabled status=%d calls=%d", res.StatusCode, calls) } } func TestPrivateNetworkFailureIsUnconfirmedNotRejected(t *testing.T) { result := decodePrivateSendResult([]byte(`{"status":"failed","code":"MESSAGE_REJECTED","status_code":1008}`)) if result.State != "unknown" || !strings.Contains(result.Error, "1008") { t.Fatalf("network error=%+v", result) } } func TestPrivateSendRejectionPreservesReason(t *testing.T) { result := decodePrivateSendResult([]byte(`{"status":"failed","code":"MESSAGE_REJECTED","status_code":401,"check_code":"restricted","check_message":"暂时无法发送"}`)) if result.State != "failed" || !strings.Contains(result.Error, "restricted") || !strings.Contains(result.Error, "401") || !strings.Contains(result.Error, "暂时无法发送") { t.Fatalf("reason=%+v", result) } } func TestDecodePrivateSendResult(t *testing.T) { cases := []struct { raw string state string id string }{ {`{"status":"succeeded","success":true,"message":{"server_id":"100"}}`, "succeeded", "100"}, {`{"status":"succeeded","success":false,"message":{"server_id":"100"}}`, "unknown", ""}, {`{"status":"succeeded","success":true,"message":{}}`, "unknown", ""}, {`{"status":"failed","code":"IM_SEND_REJECTED"}`, "failed", ""}, {`{}`, "unknown", ""}, {`bad`, "unknown", ""}, } for _, c := range cases { r := decodePrivateSendResult([]byte(c.raw)) if r.State != c.state || r.ServerID != c.id { t.Fatalf("raw=%s result=%+v", c.raw, r) } } }