douyin-release-gate / verify (push) Failing after 18m54s
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.
131 lines
4.6 KiB
Go
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)
|
|
}
|
|
}
|
|
}
|