server: 平台抽象层+bilibili、服务端加密凭据库、角色中间件;web: 精简页面/路由、macOS 客户端(Swift)与多份方案文档;移除误入库的编译产物
This commit is contained in:
@@ -0,0 +1,112 @@
|
||||
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")
|
||||
}
|
||||
Reference in New Issue
Block a user