Files
OpenFlare/backend/plugins/domain/admin/service/system_config_test.go
T
ryan 455e2f8be5 fix(config): serve public settings and enforce login CAP
Public config now comes from admin as a flat visibility=1 map instead of
a cross-plugin query that compared an integer column to "visible". Login
and register resolve CaptchaService per request so CAP is not skipped
when user applies before cap.
2026-09-02 17:07:07 +08:00

205 lines
5.7 KiB
Go

// Copyright 2026 Arctel.net
// SPDX-License-Identifier: Apache-2.0
package service_test
import (
"Wavelet/plugins/domain/admin/model"
"Wavelet/plugins/domain/admin/repository"
"Wavelet/plugins/domain/admin/service"
"context"
"testing"
"time"
"github.com/glebarez/sqlite"
"gorm.io/gorm"
)
type testDBService struct {
db *gorm.DB
}
func (s *testDBService) DB(ctx context.Context) *gorm.DB {
return s.db
}
func (s *testDBService) MasterDB(ctx context.Context) *gorm.DB {
return s.db
}
func (s *testDBService) GORM() *gorm.DB {
return s.db
}
func (s *testDBService) Named(_ string) *gorm.DB {
return s.db
}
func setupSystemConfigTest(t *testing.T) (*gorm.DB, func()) {
t.Helper()
sqliteDB, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
DisableForeignKeyConstraintWhenMigrating: true,
})
if err != nil {
t.Fatalf("gorm.Open(sqlite) error = %v", err)
}
if err := sqliteDB.AutoMigrate(&model.SystemConfig{}); err != nil {
t.Fatalf("AutoMigrate(SystemConfig) error = %v", err)
}
siteConfig := model.SystemConfig{
Key: model.ConfigKeySiteName,
Value: "Wavelet",
Type: "system",
Description: "系统平台的展示名称",
}
if err := sqliteDB.Create(&siteConfig).Error; err != nil {
t.Fatalf("Create(site_name) error = %v", err)
}
service.SetDBService(&testDBService{db: sqliteDB})
cleanup := func() {
repository.StopSystemConfigCacheListener()
repository.ResetSystemConfigRAMCacheForTest()
service.ResetServices()
}
return sqliteDB, cleanup
}
func TestPublicSystemConfigsExposesVisibleKeys(t *testing.T) {
dbConn, cleanup := setupSystemConfigTest(t)
defer cleanup()
repository.ResetSystemConfigRAMCacheForTest()
ctx := context.Background()
hidden := model.SystemConfig{
Key: "secret_key",
Value: "nope",
Type: "system",
Visibility: model.ConfigVisibilityHidden,
}
visible := model.SystemConfig{
Key: model.ConfigKeyCapLoginEnabled,
Value: "true",
Type: "system",
Visibility: model.ConfigVisibilityVisible,
}
if err := dbConn.Create(&hidden).Error; err != nil {
t.Fatalf("Create(hidden) error = %v", err)
}
if err := dbConn.Create(&visible).Error; err != nil {
t.Fatalf("Create(visible) error = %v", err)
}
got, err := service.PublicSystemConfigs(ctx)
if err != nil {
t.Fatalf("PublicSystemConfigs() error = %v", err)
}
if got[model.ConfigKeyCapLoginEnabled] != "true" {
t.Fatalf("PublicSystemConfigs()[%s] = %q, want %q", model.ConfigKeyCapLoginEnabled, got[model.ConfigKeyCapLoginEnabled], "true")
}
if _, ok := got["secret_key"]; ok {
t.Fatalf("PublicSystemConfigs() leaked hidden key secret_key")
}
viaProvider, err := service.PublicConfigAdapter{}.PublicConfig(ctx)
if err != nil {
t.Fatalf("PublicConfigAdapter.PublicConfig() error = %v", err)
}
if viaProvider[model.ConfigKeyCapLoginEnabled] != "true" {
t.Fatalf("PublicConfigAdapter.PublicConfig()[%s] = %q, want %q", model.ConfigKeyCapLoginEnabled, viaProvider[model.ConfigKeyCapLoginEnabled], "true")
}
}
func TestListSystemConfigsByKeys_EmptyKeys(t *testing.T) {
result, err := repository.ListSystemConfigsByKeys(context.Background(), nil)
if err != nil {
t.Fatalf("ListSystemConfigsByKeys(nil) error = %v", err)
}
if len(result) != 0 {
t.Fatalf("ListSystemConfigsByKeys(nil) = %#v, want empty map", result)
}
}
func TestListSystemConfigsByKeys_LoadsFromRAMCache(t *testing.T) {
dbConn, cleanup := setupSystemConfigTest(t)
defer cleanup()
ctx := context.Background()
repository.ResetSystemConfigRAMCacheForTest()
// Initial load
warm, err := repository.GetSystemConfigByKey(ctx, model.ConfigKeySiteName)
if err != nil {
t.Fatalf("GetSystemConfigByKey(site_name) warm error = %v", err)
}
if warm.Value != "Wavelet" {
t.Fatalf("GetSystemConfigByKey(site_name).Value = %q, want %q", warm.Value, "Wavelet")
}
// Update DB directly
if err := dbConn.Model(&model.SystemConfig{}).
Where("key = ?", model.ConfigKeySiteName).
Update("value", "db_only_value").Error; err != nil {
t.Fatalf("Update(site_name) error = %v", err)
}
// Fetch via ListSystemConfigsByKeys should serve from local store (meaning the old value "Wavelet")
configs, err := repository.ListSystemConfigsByKeys(ctx, []string{model.ConfigKeySiteName})
if err != nil {
t.Fatalf("ListSystemConfigsByKeys(site_name) error = %v", err)
}
sc, ok := configs[model.ConfigKeySiteName]
if !ok {
t.Fatal("ListSystemConfigsByKeys(site_name) missing site_name entry")
}
if sc.Value != "Wavelet" {
t.Fatalf("ListSystemConfigsByKeys(site_name).Value = %q, want cached value %q", sc.Value, "Wavelet")
}
}
func TestGetSystemConfigByGroupAndInvalidation(t *testing.T) {
dbConn, cleanup := setupSystemConfigTest(t)
defer cleanup()
ctx := context.Background()
repository.ResetSystemConfigRAMCacheForTest()
// Get via specific group/type
cfg, err := repository.GetSystemConfigByGroup(ctx, repository.ConfigCacheType, model.ConfigKeySiteName)
if err != nil {
t.Fatalf("GetSystemConfigByGroup error = %v", err)
}
if cfg.Value != "Wavelet" {
t.Fatalf("value = %q, want %q", cfg.Value, "Wavelet")
}
// Direct DB update
if err := dbConn.Model(&model.SystemConfig{}).
Where("key = ?", model.ConfigKeySiteName).
Update("value", "new_site_name").Error; err != nil {
t.Fatalf("DB Update error = %v", err)
}
// Invalidate
if err := repository.InvalidateSystemConfigCache(ctx, model.ConfigKeySiteName); err != nil {
t.Fatalf("InvalidateSystemConfigCache error = %v", err)
}
// Wait for broadcast execution
time.Sleep(100 * time.Millisecond)
// Fetch again
updated, err := repository.GetSystemConfigByKey(ctx, model.ConfigKeySiteName)
if err != nil {
t.Fatalf("GetSystemConfigByKey error = %v", err)
}
if updated.Value != "new_site_name" {
t.Fatalf("value = %q, want %q", updated.Value, "new_site_name")
}
}