You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
paopao-ce/internal/infra/settings/service_test.go

223 lines
6.9 KiB

package settings
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
"github.com/rocboss/paopao-ce/internal/conf"
"github.com/rocboss/paopao-ce/internal/model/web"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
func TestGetProfileUsesBootstrapDefaultsWhenNoOverride(t *testing.T) {
svc := newTestService(t)
profile, err := svc.GetProfile(context.Background())
if err != nil {
t.Fatalf("GetProfile() error = %v", err)
}
if !profile.AllowUserRegister {
t.Fatalf("AllowUserRegister = false, want bootstrap true")
}
if profile.DefaultTweetVisibility != "friend" {
t.Fatalf("DefaultTweetVisibility = %q, want friend", profile.DefaultTweetVisibility)
}
if profile.CopyrightRight != "fallback-right" {
t.Fatalf("CopyrightRight = %q, want fallback-right", profile.CopyrightRight)
}
}
func TestUpdateEditableProfilePersistsOnlyEditableKeys(t *testing.T) {
svc := newTestService(t)
profile, err := svc.UpdateEditableProfile(context.Background(), EditableProfile{
UseFriendship: false,
EnableTrendsBar: true,
EnableWallet: true,
AllowTweetAttachment: false,
AllowTweetAttachmentPrice: false,
AllowTweetVideo: false,
DefaultTweetMaxLength: 1200,
TweetWebEllipsisSize: 300,
TweetMobileEllipsisSize: 200,
DefaultTweetVisibility: "public",
DefaultMsgLoopInterval: 3000,
CopyrightTop: "top",
CopyrightLeft: "left",
CopyrightLeftLink: "https://left.example.com",
CopyrightRight: "right",
CopyrightRightLink: "https://right.example.com",
})
if err != nil {
t.Fatalf("UpdateEditableProfile() error = %v", err)
}
if !profile.AllowUserRegister {
t.Fatalf("AllowUserRegister = false, want bootstrap true")
}
if !profile.AllowPhoneBind {
t.Fatalf("AllowPhoneBind = false, want bootstrap true")
}
if profile.DefaultTweetVisibility != "public" {
t.Fatalf("DefaultTweetVisibility = %q, want public", profile.DefaultTweetVisibility)
}
values, err := svc.GetValues(context.Background())
if err != nil {
t.Fatalf("GetValues() error = %v", err)
}
if !hasValue(values.Items, "web_profile.enable_wallet", true, false) {
t.Fatalf("web_profile.enable_wallet override not found")
}
}
func TestSaveValuesEncryptsSecretsAtRest(t *testing.T) {
svc := newTestService(t)
conf.AdminSettingsSetting.EncryptionKey = "bootstrap-test-encryption-key"
svc.codec = newSecretCodec()
_, err := svc.SaveValues(context.Background(), []web.AdminSettingValueInput{{
Key: "meili.api_key",
Value: []byte(`"top-secret-key"`),
}})
if err != nil {
t.Fatalf("SaveValues() error = %v", err)
}
var record settingRecord
if err := svc.db.WithContext(context.Background()).First(&record, "key = ?", "meili.api_key").Error; err != nil {
t.Fatalf("load record error = %v", err)
}
if !record.IsEncrypted {
t.Fatalf("IsEncrypted = false, want true")
}
if strings.Contains(record.Value, "top-secret-key") {
t.Fatalf("record.Value stored plaintext = %q", record.Value)
}
}
func TestRestartRequiredValuesReportPendingRestart(t *testing.T) {
svc := newTestService(t)
resp, err := svc.SaveValues(context.Background(), []web.AdminSettingValueInput{{
Key: "meili.host",
Value: []byte(`"pending-restart:7700"`),
}})
if err != nil {
t.Fatalf("SaveValues() error = %v", err)
}
if !resp.HasPendingRestart {
t.Fatal("HasPendingRestart = false, want true")
}
if !hasValue(resp.Items, "meili.host", "pending-restart:7700", true) {
t.Fatalf("meili.host pending_restart not reported")
}
}
func TestSaveValuesRejectsInvalidSingleIntUpdate(t *testing.T) {
svc := newTestService(t)
_, err := svc.SaveValues(context.Background(), []web.AdminSettingValueInput{{
Key: "web_profile.default_tweet_max_length",
Value: []byte(`120`),
}})
if err == nil {
t.Fatal("SaveValues() error = nil, want invalid params error")
}
}
func TestSaveValuesAcceptsCoupledProfileUpdate(t *testing.T) {
svc := newTestService(t)
resp, err := svc.SaveValues(context.Background(), []web.AdminSettingValueInput{
{Key: "web_profile.default_tweet_max_length", Value: []byte(`120`)},
{Key: "web_profile.tweet_web_ellipsis_size", Value: []byte(`120`)},
{Key: "web_profile.tweet_mobile_ellipsis_size", Value: []byte(`120`)},
})
if err != nil {
t.Fatalf("SaveValues() error = %v", err)
}
if !hasValue(resp.Items, "web_profile.default_tweet_max_length", 120, false) {
t.Fatalf("web_profile.default_tweet_max_length override not found")
}
if conf.WebProfileSetting.DefaultTweetMaxLength != 120 {
t.Fatalf("DefaultTweetMaxLength = %d, want 120", conf.WebProfileSetting.DefaultTweetMaxLength)
}
}
func TestBootstrapAppliesPersistedOverrides(t *testing.T) {
svc := newTestService(t)
conf.AdminSettingsSetting.EncryptionKey = "bootstrap-test-encryption-key"
svc.codec = newSecretCodec()
_, err := svc.SaveValues(context.Background(), []web.AdminSettingValueInput{{
Key: "web_profile.enable_wallet",
Value: []byte(`true`),
}})
if err != nil {
t.Fatalf("SaveValues() error = %v", err)
}
conf.WebProfileSetting.EnableWallet = false
Bootstrap(context.Background(), svc.db)
if !conf.WebProfileSetting.EnableWallet {
t.Fatal("Bootstrap() did not apply persisted web_profile.enable_wallet override")
}
}
func newTestService(t *testing.T) *Service {
t.Helper()
wd, err := os.Getwd()
if err != nil {
t.Fatalf("os.Getwd() error = %v", err)
}
root := filepath.Clean(filepath.Join(wd, "../../.."))
if err := os.Chdir(root); err != nil {
t.Fatalf("os.Chdir(%q) error = %v", root, err)
}
t.Cleanup(func() {
_ = os.Chdir(wd)
})
conf.Initial(nil, false)
conf.WebProfileSetting = &conf.WebProfileConf{
UseFriendship: true,
EnableTrendsBar: false,
EnableWallet: false,
AllowTweetAttachment: true,
AllowTweetAttachmentPrice: true,
AllowTweetVideo: true,
AllowUserRegister: true,
AllowPhoneBind: true,
DefaultTweetMaxLength: 2000,
TweetWebEllipsisSize: 400,
TweetMobileEllipsisSize: 300,
DefaultTweetVisibility: "friend",
DefaultMsgLoopInterval: 5000,
CopyrightTop: "fallback-top",
CopyrightLeft: "fallback-left",
CopyrightLeftLink: "",
CopyrightRight: "fallback-right",
CopyrightRightLink: "https://fallback.example.com",
}
bootstrapConfig = nil
db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{NamingStrategy: schema.NamingStrategy{TablePrefix: "p_", SingularTable: true}})
if err != nil {
t.Fatalf("gorm.Open() error = %v", err)
}
if err := db.AutoMigrate(&settingRecord{}); err != nil {
t.Fatalf("AutoMigrate() error = %v", err)
}
return NewService(db)
}
func hasValue(items []web.AdminSettingValue, key string, expected any, pending bool) bool {
for _, item := range items {
if item.Key != key {
continue
}
return item.Value == expected && item.PendingRestart == pending
}
return false
}