Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
123 lines
5.5 KiB
Go
123 lines
5.5 KiB
Go
package model
|
|
|
|
import (
|
|
"errors"
|
|
"maps"
|
|
"math"
|
|
"os"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"github.com/QuantumNous/new-api/common"
|
|
"github.com/QuantumNous/new-api/setting/operation_setting"
|
|
"github.com/glebarez/sqlite"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/mysql"
|
|
"gorm.io/driver/postgres"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/schema"
|
|
)
|
|
|
|
func TestRequestPolicyDatabaseMatrix(t *testing.T) {
|
|
for _, dialect := range []string{"sqlite", "mysql", "postgres"} {
|
|
t.Run(dialect, func(t *testing.T) {
|
|
var driver gorm.Dialector
|
|
switch dialect {
|
|
case "sqlite":
|
|
driver = sqlite.Open(":memory:")
|
|
case "mysql":
|
|
dsn := os.Getenv("TEST_MYSQL_DSN")
|
|
if dsn == "" {
|
|
t.Skip("TEST_MYSQL_DSN not configured")
|
|
}
|
|
driver = mysql.Open(dsn)
|
|
case "postgres":
|
|
dsn := os.Getenv("TEST_POSTGRES_DSN")
|
|
if dsn == "" {
|
|
t.Skip("TEST_POSTGRES_DSN not configured")
|
|
}
|
|
driver = postgres.Open(dsn)
|
|
}
|
|
db, err := gorm.Open(driver, &gorm.Config{NamingStrategy: schema.NamingStrategy{TablePrefix: "policy_test_"}})
|
|
require.NoError(t, err)
|
|
sqlDB, err := db.DB()
|
|
require.NoError(t, err)
|
|
sqlDB.SetMaxOpenConns(1)
|
|
previousDB, previousType, previousSnapshot := DB, common.MainDatabaseType(), CurrentRequestPolicy()
|
|
common.OptionMapRWMutex.Lock()
|
|
previousOptions := maps.Clone(common.OptionMap)
|
|
common.OptionMap = maps.Clone(previousSnapshot.Options)
|
|
common.OptionMapRWMutex.Unlock()
|
|
DB = db
|
|
common.SetMainDatabaseType(common.DatabaseType(dialect))
|
|
initCol()
|
|
t.Cleanup(func() {
|
|
require.NoError(t, db.Migrator().DropTable(&Option{}))
|
|
for k, v := range previousSnapshot.Options {
|
|
require.NoError(t, updateOptionMap(k, v))
|
|
}
|
|
requestPolicySnapshot.Store(previousSnapshot)
|
|
common.OptionMapRWMutex.Lock()
|
|
common.OptionMap = previousOptions
|
|
common.OptionMapRWMutex.Unlock()
|
|
DB = previousDB
|
|
common.SetMainDatabaseType(previousType)
|
|
initCol()
|
|
require.NoError(t, sqlDB.Close())
|
|
})
|
|
require.NoError(t, db.AutoMigrate(&Option{}))
|
|
var version string
|
|
query := "SELECT version()"
|
|
if dialect == "sqlite" {
|
|
query = "SELECT sqlite_version()"
|
|
}
|
|
require.NoError(t, db.Raw(query).Scan(&version).Error)
|
|
t.Logf("database version: %s", version)
|
|
rules := `[{"name":"session","model_regex":[".*"],"key_sources":[{"type":"request_header","key":"X-Session"}],"ttl_seconds":0,"skip_retry_on_failure":false,"include_using_group":false,"param_override_template":{"temperature":0},"future_field":{"enabled":false}}]`
|
|
require.NoError(t, UpdateRequestPolicyOptions(map[string]string{"RetryTimes": "2", "channel_affinity_setting.rules": rules}))
|
|
assert.Equal(t, 2, CurrentRequestPolicy().RetryTimes)
|
|
assert.Equal(t, 2, common.RetryTimes, "the runtime global follows the same write")
|
|
require.NoError(t, UpdateOption("AutomaticRetryStatusCodes", "429,500-503"))
|
|
assert.Equal(t, "429,500-503", CurrentRequestPolicy().Options["AutomaticRetryStatusCodes"])
|
|
assert.Equal(t, "429,500-503", operation_setting.AutomaticRetryStatusCodesToString())
|
|
assert.Error(t, UpdateRequestPolicyOptions(map[string]string{"request_policy_setting.mode": "off"}), "the removed mode switch is not a policy option")
|
|
assert.False(t, CurrentRequestPolicy().Affinity.Rules[0].SkipRetryOnFailure)
|
|
assert.Equal(t, rules, CurrentRequestPolicy().Options["channel_affinity_setting.rules"])
|
|
loadOptionsFromDatabase()
|
|
loadOptionsFromDatabase()
|
|
assert.Equal(t, rules, CurrentRequestPolicy().Options["channel_affinity_setting.rules"], "legacy rule JSON survives reloads without dropping extension fields")
|
|
require.NoError(t, UpdateRequestPolicyOptions(map[string]string{"channel_affinity_setting.session_mode": "strict"}))
|
|
loadOptionsFromDatabase()
|
|
loadOptionsFromDatabase()
|
|
assert.Equal(t, "strict", CurrentRequestPolicy().Affinity.SessionMode)
|
|
assert.Equal(t, rules, CurrentRequestPolicy().Options["channel_affinity_setting.rules"], "a global mode never rewrites rule fields")
|
|
snapshot := CurrentRequestPolicy()
|
|
assert.Error(t, UpdateRequestPolicyOptions(map[string]string{"channel_affinity_setting.session_mode": "unknown"}))
|
|
assert.Same(t, snapshot, CurrentRequestPolicy())
|
|
for _, value := range []string{"-1", "1.5", "bad", strconv.Itoa(math.MaxInt)} {
|
|
assert.Error(t, UpdateRequestPolicyOptions(map[string]string{"RetryTimes": value}))
|
|
assert.Same(t, snapshot, CurrentRequestPolicy())
|
|
}
|
|
assert.Error(t, UpdateRequestPolicyOptions(map[string]string{"channel_affinity_setting.rules": "[", "RetryTimes": "8"}))
|
|
assert.Same(t, snapshot, CurrentRequestPolicy())
|
|
var option Option
|
|
require.NoError(t, db.Where(map[string]any{"key": "RetryTimes"}).First(&option).Error)
|
|
assert.Equal(t, "2", option.Value)
|
|
require.NoError(t, db.Callback().Update().Before("gorm:update").Register("policy_fail", func(tx *gorm.DB) {
|
|
if tx.Statement.Table == "policy_test_options" {
|
|
tx.AddError(errors.New("save rejected"))
|
|
}
|
|
}))
|
|
assert.Error(t, UpdateRequestPolicyOptions(map[string]string{"RetryTimes": "8", "channel_affinity_setting.session_mode": "off"}))
|
|
assert.Same(t, snapshot, CurrentRequestPolicy())
|
|
require.NoError(t, db.Callback().Update().Remove("policy_fail"))
|
|
option = Option{}
|
|
require.NoError(t, db.Where(map[string]any{"key": "RetryTimes"}).First(&option).Error)
|
|
assert.Equal(t, "2", option.Value)
|
|
loadOptionsFromDatabase()
|
|
assert.Equal(t, "strict", CurrentRequestPolicy().Affinity.SessionMode, "a failed save keeps the persisted global mode")
|
|
})
|
|
}
|
|
}
|