kra-new/internal/data/integration_config_test.go

289 lines
9.6 KiB
Go

package data
import (
"context"
"os"
"path/filepath"
"strings"
"testing"
integrationbiz "kra/internal/biz/integration"
"kra/internal/config"
dataintegration "kra/internal/data/integration"
"kra/internal/integration/storage"
"gopkg.in/yaml.v3"
"gorm.io/gorm"
)
func openIntegrationConfigTestDB(t *testing.T) *gorm.DB {
t.Helper()
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared", nil)
if err != nil {
t.Fatal(err)
}
sqlDB, err := db.DB()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = sqlDB.Close() })
if err = db.AutoMigrate(&dataintegration.ConfigPO{}); err != nil {
t.Fatal(err)
}
return db
}
func TestMigrateAllCreatesIntegrationConfigTable(t *testing.T) {
db, err := openWithDriver("sqlite", "file:"+t.Name()+"?mode=memory&cache=shared", nil)
if err != nil {
t.Fatal(err)
}
sqlDB, err := db.DB()
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { _ = sqlDB.Close() })
if err = migrateAll(db, testCatalog()); err != nil {
t.Fatal(err)
}
if !db.Migrator().HasTable(&dataintegration.ConfigPO{}) {
t.Fatal("migrateAll did not create sys_integration_configs")
}
}
func TestStorageIntegrationConfigRoundTrip(t *testing.T) {
db := openIntegrationConfigTestDB(t)
storage := &config.Storage{
Type: "aliyun-oss",
Qiniu: &config.Qiniu{
Zone: "ZoneHuadong", Bucket: "qiniu-bucket", AccessKey: "qiniu-key", SecretKey: "qiniu-secret",
},
AliyunOSS: &config.ObjectStore{
Endpoint: "oss-cn-hangzhou.aliyuncs.com", Region: "cn-hangzhou", Bucket: "assets",
AccessKey: "aliyun-key", SecretKey: "aliyun-secret", BaseURL: "https://cdn.example.com", PathPrefix: "uploads",
},
Minio: &config.ObjectStore{Endpoint: "127.0.0.1:9000", Bucket: "local", ForcePathStyle: true},
}
if err := saveStorageIntegrationConfig(db, storage); err != nil {
t.Fatal(err)
}
email := &config.Email{
To: "ops@example.com", From: "mailer@example.com", Host: "smtp.example.com",
Secret: "smtp-secret", Nickname: "Kra", Port: 465, IsSSL: true,
}
if err := saveEmailIntegrationConfig(db, email); err != nil {
t.Fatal(err)
}
if err := db.Create(&dataintegration.ConfigPO{Kind: integrationbiz.IntegrationKindPayment, Provider: "wechat-pay", Config: `{"merchant_id":"123"}`}).Error; err != nil {
t.Fatal(err)
}
loaded, found, err := loadStorageIntegrationConfig(db)
if err != nil {
t.Fatal(err)
}
if !found {
t.Fatal("storage integration configuration was not found")
}
if loaded.Type != "aliyun-oss" {
t.Fatalf("storage type = %q, want aliyun-oss", loaded.Type)
}
if loaded.AliyunOSS == nil || loaded.AliyunOSS.SecretKey != "aliyun-secret" || loaded.AliyunOSS.PathPrefix != "uploads" {
t.Fatalf("aliyun configuration = %#v", loaded.AliyunOSS)
}
if loaded.Qiniu == nil || loaded.Qiniu.SecretKey != "qiniu-secret" {
t.Fatalf("qiniu configuration = %#v", loaded.Qiniu)
}
loadedEmail, found, err := loadEmailIntegrationConfig(db)
if err != nil || !found {
t.Fatalf("email configuration found=%v, err=%v", found, err)
}
if loadedEmail.Host != "smtp.example.com" || loadedEmail.Secret != "smtp-secret" || loadedEmail.Port != 465 {
t.Fatalf("email configuration = %#v", loadedEmail)
}
var storageCount, emailCount, paymentCount int64
if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationKindStorage).Count(&storageCount).Error; err != nil {
t.Fatal(err)
}
if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationbiz.IntegrationKindPayment).Count(&paymentCount).Error; err != nil {
t.Fatal(err)
}
if err = db.Model(&dataintegration.ConfigPO{}).Where("kind = ?", integrationKindEmail).Count(&emailCount).Error; err != nil {
t.Fatal(err)
}
if storageCount != int64(len(storageProviderNames)) {
t.Fatalf("storage row count = %d, want %d", storageCount, len(storageProviderNames))
}
if paymentCount != 1 {
t.Fatalf("payment row count = %d, want 1", paymentCount)
}
if emailCount != 1 {
t.Fatalf("email row count = %d, want 1", emailCount)
}
}
func TestResolveStorageIntegrationConfigMigratesLegacyOnlyOnce(t *testing.T) {
db := openIntegrationConfigTestDB(t)
legacy := &config.Storage{
Type: "qiniu",
Qiniu: &config.Qiniu{Bucket: "legacy", SecretKey: "legacy-secret"},
}
loaded, err := resolveStorageIntegrationConfig(db, legacy)
if err != nil {
t.Fatal(err)
}
if loaded.Type != "qiniu" || loaded.Qiniu.Bucket != "legacy" {
t.Fatalf("migrated storage = %#v", loaded)
}
other := &config.Storage{
Type: "minio",
Minio: &config.ObjectStore{Bucket: "must-not-replace-database"},
}
loaded, err = resolveStorageIntegrationConfig(db, other)
if err != nil {
t.Fatal(err)
}
if loaded.Type != "qiniu" || loaded.Qiniu.SecretKey != "legacy-secret" {
t.Fatalf("database configuration was replaced by legacy config: %#v", loaded)
}
}
func TestMaskStorageSecretsLeavesUnconfiguredProvidersEmpty(t *testing.T) {
storage := &config.Storage{
Qiniu: &config.Qiniu{},
AliyunOSS: &config.ObjectStore{SecretKey: "configured-secret"},
Minio: &config.ObjectStore{},
}
config.MaskStorageSecrets(storage)
if storage.Qiniu.SecretKey != "" || storage.Minio.SecretKey != "" {
t.Fatalf("empty provider secrets were masked: %#v", storage)
}
if storage.AliyunOSS.SecretKey != config.MaskedSecret {
t.Fatalf("configured secret was not masked: %q", storage.AliyunOSS.SecretKey)
}
}
func TestResolveEmailIntegrationConfigMigratesLegacyOnlyOnce(t *testing.T) {
db := openIntegrationConfigTestDB(t)
legacy := &config.Email{To: "ops@example.com", From: "old@example.com", Host: "smtp.old.example.com", Secret: "old-secret", Port: 465, IsSSL: true}
loaded, err := resolveEmailIntegrationConfig(db, legacy)
if err != nil {
t.Fatal(err)
}
if loaded.Host != legacy.Host || loaded.Secret != legacy.Secret {
t.Fatalf("migrated email = %#v", loaded)
}
loaded, err = resolveEmailIntegrationConfig(db, &config.Email{Host: "must-not-replace.example.com"})
if err != nil {
t.Fatal(err)
}
if loaded.Host != legacy.Host || loaded.Secret != legacy.Secret {
t.Fatalf("database email was replaced by legacy config: %#v", loaded)
}
}
func TestPersistConfigValuesRemovesStorageFromYAML(t *testing.T) {
path := filepath.Join(t.TempDir(), "config.yaml")
input := []byte("data: {}\nadmin:\n router_prefix: /old\n storage:\n type: qiniu\n qiniu:\n secret_key: legacy-secret\n email:\n host: smtp.legacy.example.com\n secret: legacy-email-secret\n extension_key: retained\n")
if err := os.WriteFile(path, input, 0o600); err != nil {
t.Fatal(err)
}
d := &Data{}
admin := &config.Admin{
ConfigPath: path,
RouterPrefix: "/api",
Storage: &config.Storage{
Type: "qiniu",
Qiniu: &config.Qiniu{SecretKey: "database-only-secret"},
},
Email: &config.Email{Host: "smtp.database.example.com", Secret: "database-only-email-secret"},
}
if err := d.persistConfigValues(&config.Data{}, admin); err != nil {
t.Fatal(err)
}
raw, err := os.ReadFile(path)
if err != nil {
t.Fatal(err)
}
var document map[string]any
if err = yaml.Unmarshal(raw, &document); err != nil {
t.Fatal(err)
}
adminValue, ok := document["admin"].(map[string]any)
if !ok {
t.Fatalf("admin config = %#v", document["admin"])
}
if _, exists := adminValue["storage"]; exists {
t.Fatalf("storage remained in YAML: %s", raw)
}
if _, exists := adminValue["email"]; exists {
t.Fatalf("email remained in YAML: %s", raw)
}
if adminValue["extension_key"] != "retained" {
t.Fatalf("extension key was not retained: %#v", adminValue)
}
}
func TestPersistRuntimeConfigReplacesActiveStorage(t *testing.T) {
db := openIntegrationConfigTestDB(t)
configPath := filepath.Join(t.TempDir(), "config.yaml")
if err := os.WriteFile(configPath, []byte("data: {}\nadmin: {}\n"), 0o600); err != nil {
t.Fatal(err)
}
oldRoot := filepath.Join(t.TempDir(), "old")
newRoot := filepath.Join(t.TempDir(), "new")
currentAdmin := &config.Admin{
ConfigPath: configPath,
Local: &config.Local{StorePath: oldRoot, PathPrefix: "old-files"},
Storage: &config.Storage{Type: "local"},
}
currentStorage, err := storage.NewReloadable(currentAdmin)
if err != nil {
t.Fatal(err)
}
reloadableDB := &reloadableDB{}
reloadableDB.current.Store(db)
d := &Data{
runtime: config.NewStore(&config.Config{Data: &config.Data{}, Admin: currentAdmin}),
gormDB: reloadableDB,
storage: currentStorage,
}
d.databaseReady.Store(true)
nextAdmin := cloneAdminConfig(currentAdmin)
nextAdmin.Local = &config.Local{StorePath: newRoot, PathPrefix: "new-files"}
nextAdmin.Email = &config.Email{
To: "ops@example.com", From: "mailer@example.com", Host: "smtp.example.com",
Secret: "runtime-secret", Port: 465, IsSSL: true,
}
if err = d.PersistRuntimeConfig(context.Background(), &config.Config{Data: &config.Data{}, Admin: nextAdmin}); err != nil {
t.Fatal(err)
}
stored, err := d.storage.Put(context.Background(), "active.txt", strings.NewReader("active"))
if err != nil {
t.Fatal(err)
}
if stored.URL != "/new-files/active.txt" {
t.Fatalf("active storage URL = %q, want /new-files/active.txt", stored.URL)
}
if _, err = os.Stat(filepath.Join(newRoot, "active.txt")); err != nil {
t.Fatalf("active storage did not write to the new root: %v", err)
}
loaded, found, err := loadStorageIntegrationConfig(db)
if err != nil || !found || loaded.Type != "local" {
t.Fatalf("database storage config = %#v, found=%v, err=%v", loaded, found, err)
}
loadedEmail, found, err := loadEmailIntegrationConfig(db)
if err != nil || !found || loadedEmail.Secret != "runtime-secret" {
t.Fatalf("database email config = %#v, found=%v, err=%v", loadedEmail, found, err)
}
if runtimeEmail := d.runtime.Admin().Email; runtimeEmail == nil || runtimeEmail.Host != "smtp.example.com" {
t.Fatalf("runtime email config = %#v", runtimeEmail)
}
}