113 lines
3.3 KiB
Go
113 lines
3.3 KiB
Go
package handlers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"sync"
|
|
"testing"
|
|
"time"
|
|
|
|
"everypublish/server/internal/models"
|
|
"everypublish/server/internal/platform"
|
|
"github.com/gin-gonic/gin"
|
|
|
|
gormsqlite "gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
type loginTestAdapter struct {
|
|
mu sync.Mutex
|
|
polls int
|
|
saved string
|
|
}
|
|
|
|
func (a *loginTestAdapter) Platform() string { return "test-platform" }
|
|
func (a *loginTestAdapter) BeginLogin(context.Context) (platform.LoginSession, error) {
|
|
return platform.LoginSession{Token: "token-1", URL: "https://qr.test/1"}, nil
|
|
}
|
|
func (a *loginTestAdapter) PollLogin(context.Context, string) (platform.LoginPoll, error) {
|
|
a.mu.Lock()
|
|
defer a.mu.Unlock()
|
|
a.polls++
|
|
if a.polls == 1 {
|
|
return platform.LoginPoll{State: platform.LoginPending, Prompt: "等待扫码"}, nil
|
|
}
|
|
return platform.LoginPoll{State: platform.LoginConfirmed, Cookies: "cookie-value"}, nil
|
|
}
|
|
func (a *loginTestAdapter) SaveCredentials(_ uint64, _ uint64, cookies string) error {
|
|
a.mu.Lock()
|
|
a.saved = cookies
|
|
a.mu.Unlock()
|
|
return nil
|
|
}
|
|
func (a *loginTestAdapter) Publish(context.Context, platform.PublishInput) (platform.PublishResult, error) {
|
|
return platform.PublishResult{}, nil
|
|
}
|
|
|
|
func TestAccountBindPollsAdapterAndActivatesAccount(t *testing.T) {
|
|
db, err := gorm.Open(gormsqlite.Open(t.TempDir()+"/account.db"), &gorm.Config{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.AutoMigrate(&models.Account{}, &models.Challenge{}, &models.Notification{}, &models.AuditLog{}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
account := models.Account{WorkspaceID: 9, Platform: "test-platform", Status: "unbound"}
|
|
if err = db.Create(&account).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
adapter := &loginTestAdapter{}
|
|
registry := platform.NewRegistry()
|
|
if err = registry.Register(adapter); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
h := &AccountHandler{DB: db, Registry: registry, ExecutorMode: "web", PollInterval: 5 * time.Millisecond}
|
|
gin.SetMode(gin.TestMode)
|
|
req := httptest.NewRequest(http.MethodPost, "/accounts/1/bind", nil)
|
|
recorder := httptest.NewRecorder()
|
|
ctx, _ := gin.CreateTestContext(recorder)
|
|
ctx.Request = req
|
|
ctx.Params = gin.Params{{Key: "id", Value: "1"}}
|
|
ctx.Set("wsid", uint64(9))
|
|
ctx.Set("uid", uint64(3))
|
|
h.Bind(ctx)
|
|
if recorder.Code != http.StatusOK {
|
|
t.Fatalf("bind status=%d body=%s", recorder.Code, recorder.Body.String())
|
|
}
|
|
var envelope struct {
|
|
Data struct {
|
|
ChallengeID uint64 `json:"challengeId"`
|
|
QRURL string `json:"qrUrl"`
|
|
} `json:"data"`
|
|
}
|
|
if err = json.Unmarshal(recorder.Body.Bytes(), &envelope); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if envelope.Data.ChallengeID == 0 || envelope.Data.QRURL != "https://qr.test/1" {
|
|
t.Fatalf("unexpected bind response: %s", recorder.Body.String())
|
|
}
|
|
deadline := time.Now().Add(time.Second)
|
|
for time.Now().Before(deadline) {
|
|
var got models.Account
|
|
_ = db.First(&got, account.ID).Error
|
|
if got.Status == "active" {
|
|
adapter.mu.Lock()
|
|
saved := adapter.saved
|
|
adapter.mu.Unlock()
|
|
if saved != "cookie-value" {
|
|
t.Fatalf("credentials not saved: %q", saved)
|
|
}
|
|
var challenge models.Challenge
|
|
_ = db.First(&challenge, envelope.Data.ChallengeID).Error
|
|
if challenge.Status != "solved" {
|
|
t.Fatalf("challenge status=%s", challenge.Status)
|
|
}
|
|
return
|
|
}
|
|
time.Sleep(10 * time.Millisecond)
|
|
}
|
|
t.Fatal("adapter login worker did not activate account")
|
|
}
|