Files
creator-hub/internal/controlplane/api/private_messages_test.go
T
rogee e50bed9ad8
douyin-release-gate / verify (push) Failing after 18m54s
feat: add account events and private message management
Add Douyin notification polling, event details, and manual multi-account private messaging.

Refine environment memory settings, account operations, login collection recovery, and message UI; update tests and documentation.
2026-10-07 10:23:05 +08:00

131 lines
4.6 KiB
Go

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)
}
}
}