server: 平台抽象层+bilibili、服务端加密凭据库、角色中间件;web: 精简页面/路由、macOS 客户端(Swift)与多份方案文档;移除误入库的编译产物
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
package ws
|
||||
|
||||
import (
|
||||
"context"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"everypublish/server/internal/config"
|
||||
"everypublish/server/internal/models"
|
||||
"everypublish/server/internal/platform"
|
||||
"everypublish/server/internal/task"
|
||||
|
||||
gormsqlite "gorm.io/driver/sqlite"
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
type testPlatformAdapter struct {
|
||||
mu sync.Mutex
|
||||
called int
|
||||
block bool
|
||||
canceled chan struct{}
|
||||
}
|
||||
|
||||
func (a *testPlatformAdapter) Platform() string { return "test-platform" }
|
||||
func (a *testPlatformAdapter) BeginLogin(context.Context) (platform.LoginSession, error) {
|
||||
return platform.LoginSession{}, nil
|
||||
}
|
||||
func (a *testPlatformAdapter) PollLogin(context.Context, string) (platform.LoginPoll, error) {
|
||||
return platform.LoginPoll{}, nil
|
||||
}
|
||||
func (a *testPlatformAdapter) SaveCredentials(uint64, uint64, string) error { return nil }
|
||||
func (a *testPlatformAdapter) Publish(ctx context.Context, in platform.PublishInput) (platform.PublishResult, error) {
|
||||
a.mu.Lock()
|
||||
a.called++
|
||||
a.mu.Unlock()
|
||||
if a.block {
|
||||
select {
|
||||
case <-ctx.Done():
|
||||
close(a.canceled)
|
||||
return platform.PublishResult{}, ctx.Err()
|
||||
case <-time.After(2 * time.Second):
|
||||
}
|
||||
}
|
||||
return platform.PublishResult{URL: "https://example.test/task/" + in.Title, Receipt: "receipt"}, nil
|
||||
}
|
||||
|
||||
func platformTestDB(t *testing.T) *gorm.DB {
|
||||
t.Helper()
|
||||
db, err := gorm.Open(gormsqlite.Open(filepath.Join(t.TempDir(), "dispatcher.db")), &gorm.Config{})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err = db.AutoMigrate(&models.Task{}, &models.TaskEvent{}, &models.Account{}, &models.Material{}, &models.TransferToken{}, &models.Notification{}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
return db
|
||||
}
|
||||
|
||||
func TestDispatcherUsesRegisteredPlatformAndPublishesPerAccount(t *testing.T) {
|
||||
db := platformTestDB(t)
|
||||
account := models.Account{WorkspaceID: 1, Platform: "test-platform", Status: "active"}
|
||||
material := models.Material{WorkspaceID: 1, Name: "video.mp4", Status: "ready", StorageKey: "1/video.mp4"}
|
||||
if err := db.Create(&account).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := db.Create(&material).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
row := models.Task{WorkspaceID: 1, Title: "platform", Status: task.Queued, AccountIDs: "[1]", MaterialIDs: "[1]"}
|
||||
if err := db.Create(&row).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
adapter := &testPlatformAdapter{}
|
||||
registry := platform.NewRegistry()
|
||||
if err := registry.Register(adapter); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
d := NewDispatcher(db, NewHub(), &config.Config{ExecutorMode: "web", BaseURL: "http://127.0.0.1:8090"})
|
||||
d.Registry = registry
|
||||
if !d.TryDispatch(row.ID) {
|
||||
t.Fatal("registered adapter should accept queued task")
|
||||
}
|
||||
deadline := time.Now().Add(2 * time.Second)
|
||||
for time.Now().Before(deadline) {
|
||||
var got models.Task
|
||||
if err := db.First(&got, row.ID).Error; err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got.Status == task.Success {
|
||||
if got.PublishedURLs != `["https://example.test/task/platform"]` || got.Receipts != `["receipt"]` {
|
||||
t.Fatalf("unexpected platform result: urls=%s receipts=%s", got.PublishedURLs, got.Receipts)
|
||||
}
|
||||
adapter.mu.Lock()
|
||||
called := adapter.called
|
||||
adapter.mu.Unlock()
|
||||
if called != 1 {
|
||||
t.Fatalf("adapter called %d times", called)
|
||||
}
|
||||
return
|
||||
}
|
||||
time.Sleep(20 * time.Millisecond)
|
||||
}
|
||||
t.Fatal("platform task did not reach success")
|
||||
}
|
||||
|
||||
func TestDispatcherCancelStopsRegisteredPlatform(t *testing.T) {
|
||||
db := platformTestDB(t)
|
||||
account := models.Account{WorkspaceID: 1, Platform: "test-platform", Status: "active"}
|
||||
material := models.Material{WorkspaceID: 1, Name: "video.mp4", Status: "ready", StorageKey: "1/video.mp4"}
|
||||
_ = db.Create(&account).Error
|
||||
_ = db.Create(&material).Error
|
||||
row := models.Task{WorkspaceID: 1, Title: "cancel", Status: task.Queued, AccountIDs: "[1]", MaterialIDs: "[1]"}
|
||||
_ = db.Create(&row).Error
|
||||
adapter := &testPlatformAdapter{block: true, canceled: make(chan struct{})}
|
||||
registry := platform.NewRegistry()
|
||||
_ = registry.Register(adapter)
|
||||
d := NewDispatcher(db, NewHub(), &config.Config{ExecutorMode: "web", BaseURL: "http://127.0.0.1:8090"})
|
||||
d.Registry = registry
|
||||
if !d.TryDispatch(row.ID) {
|
||||
t.Fatal("registered adapter should accept queued task")
|
||||
}
|
||||
time.Sleep(60 * time.Millisecond)
|
||||
d.Cancel(row.ID)
|
||||
select {
|
||||
case <-adapter.canceled:
|
||||
case <-time.After(time.Second):
|
||||
t.Fatal("adapter context was not cancelled")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user