feat: migrate workbench services
This commit is contained in:
+52
-209
@@ -15,12 +15,8 @@ import (
|
||||
type Config struct {
|
||||
Listen string `json:"listen"`
|
||||
MySQL MySQLConfig `json:"mysql"`
|
||||
CoreMySQL MySQLConfig `json:"core_mysql"`
|
||||
Redis RedisConfig `json:"redis"`
|
||||
Kafka KafkaConfig `json:"kafka"`
|
||||
Auth AuthConfig `json:"auth"`
|
||||
Manager ManagerConfig `json:"manager"`
|
||||
AI AIConfig `json:"ai"`
|
||||
ReadTimeout time.Duration `json:"-"`
|
||||
WriteTimeout time.Duration `json:"-"`
|
||||
}
|
||||
@@ -32,65 +28,26 @@ type MySQLConfig struct {
|
||||
}
|
||||
|
||||
type RedisConfig struct {
|
||||
Addr string `json:"addr"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
DB int `json:"db"`
|
||||
KeyPrefix string `json:"key_prefix"`
|
||||
}
|
||||
|
||||
type KafkaConfig struct {
|
||||
Enabled bool `json:"enabled"`
|
||||
Brokers []string `json:"brokers"`
|
||||
Topic string `json:"topic"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
SASL string `json:"sasl"`
|
||||
ClientID string `json:"client_id"`
|
||||
BatchSize int `json:"batch_size"`
|
||||
Addr string `json:"addr"`
|
||||
Username string `json:"username"`
|
||||
Password string `json:"password"`
|
||||
DB int `json:"db"`
|
||||
}
|
||||
|
||||
type AuthConfig struct {
|
||||
APIKeys []string `json:"api_keys"`
|
||||
SessionKeyPattern string `json:"session_key_pattern"`
|
||||
UIDKey string `json:"uid_key"`
|
||||
AllowDevHeaderAuth bool `json:"allow_dev_header_auth"`
|
||||
RequiredTenantCode bool `json:"required_tenant_code"`
|
||||
RequiredAPIKey bool `json:"required_api_key"`
|
||||
SkipSessionInDev bool `json:"skip_session_in_dev"`
|
||||
TrustedInternalAddr string `json:"trusted_internal_addr"`
|
||||
InternalServiceName string `json:"internal_service_name"`
|
||||
InternalSecret string `json:"internal_secret"`
|
||||
}
|
||||
|
||||
type ManagerConfig struct {
|
||||
BaseURL string `json:"base_url"`
|
||||
ServiceName string `json:"service_name"`
|
||||
Secret string `json:"secret"`
|
||||
UIDKeyGroup string `json:"uid_key_group"`
|
||||
UIDKeyKeys []string `json:"uid_key_keys"`
|
||||
TimeoutMS int `json:"timeout_ms"`
|
||||
}
|
||||
|
||||
type AIConfig struct {
|
||||
Provider string `json:"provider"`
|
||||
DeepSeek DeepSeekConfig `json:"deepseek"`
|
||||
DefaultModel string `json:"default_model"`
|
||||
ComplexModel string `json:"complex_model"`
|
||||
Temperature float64 `json:"temperature"`
|
||||
MaxOutputTokens int `json:"max_output_tokens"`
|
||||
}
|
||||
|
||||
type DeepSeekConfig struct {
|
||||
BaseURL string `json:"base_url"`
|
||||
APIKey string `json:"api_key"`
|
||||
TimeoutMS int `json:"timeout_ms"`
|
||||
MaxRetries int `json:"max_retries"`
|
||||
APIKeys []string `json:"api_keys"`
|
||||
SessionKeyPattern string `json:"session_key_pattern"`
|
||||
SessionTTLSeconds int `json:"session_ttl_seconds"`
|
||||
DefaultTenantID int64 `json:"default_tenant_id"`
|
||||
DataEncryptionKey string `json:"data_encryption_key"`
|
||||
LookupHMACKey string `json:"lookup_hmac_key"`
|
||||
AllowDevHeaderAuth bool `json:"allow_dev_header_auth"`
|
||||
RequiredAPIKey bool `json:"required_api_key"`
|
||||
}
|
||||
|
||||
func Load() (*Config, error) {
|
||||
cfg := defaultConfig()
|
||||
if path := strings.TrimSpace(os.Getenv("IM_WORKBENCH_CONFIG")); path != "" {
|
||||
if path := strings.TrimSpace(os.Getenv("WORKBENCH_CONFIG")); path != "" {
|
||||
data, err := os.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -110,41 +67,16 @@ func defaultConfig() *Config {
|
||||
return &Config{
|
||||
Listen: ":6061",
|
||||
MySQL: MySQLConfig{
|
||||
DSN: "root:root@tcp(127.0.0.1:3306)/workbench?parseTime=true&charset=utf8mb4,utf8&loc=Local",
|
||||
DSN: "root:root@tcp(127.0.0.1:3306)/olons_admin?parseTime=true&charset=utf8mb4&loc=Local",
|
||||
MaxOpenConns: 15,
|
||||
MaxIdleConns: 5,
|
||||
},
|
||||
Redis: RedisConfig{
|
||||
Addr: "127.0.0.1:6379",
|
||||
KeyPrefix: "im:",
|
||||
},
|
||||
Kafka: KafkaConfig{
|
||||
Topic: "im.workbench.event",
|
||||
ClientID: "im_workbench",
|
||||
},
|
||||
Redis: RedisConfig{Addr: "127.0.0.1:6379"},
|
||||
Auth: AuthConfig{
|
||||
SessionKeyPattern: "im:session:token:%s",
|
||||
UIDKey: "dkXxWLwqUNRfYmdXRzOkZw==",
|
||||
RequiredTenantCode: true,
|
||||
RequiredAPIKey: true,
|
||||
},
|
||||
Manager: ManagerConfig{
|
||||
ServiceName: "im-workbench",
|
||||
UIDKeyGroup: "im",
|
||||
UIDKeyKeys: []string{"uid_encryption_key", "UID_ENCRYPTION_KEY", "uid_key"},
|
||||
TimeoutMS: 3000,
|
||||
},
|
||||
AI: AIConfig{
|
||||
Provider: "deepseek",
|
||||
DefaultModel: "deepseek-flash",
|
||||
ComplexModel: "deepseek-v4-pro",
|
||||
Temperature: 0.2,
|
||||
MaxOutputTokens: 4096,
|
||||
DeepSeek: DeepSeekConfig{
|
||||
BaseURL: "https://api.deepseek.com",
|
||||
TimeoutMS: 60000,
|
||||
MaxRetries: 2,
|
||||
},
|
||||
SessionKeyPattern: "workbench:session:token:%s",
|
||||
SessionTTLSeconds: 14 * 24 * 60 * 60,
|
||||
DefaultTenantID: 0,
|
||||
RequiredAPIKey: true,
|
||||
},
|
||||
ReadTimeout: 8 * time.Second,
|
||||
WriteTimeout: 8 * time.Second,
|
||||
@@ -152,90 +84,24 @@ func defaultConfig() *Config {
|
||||
}
|
||||
|
||||
func applyEnv(cfg *Config) {
|
||||
setString(&cfg.Listen, "IM_WORKBENCH_LISTEN")
|
||||
setString(&cfg.MySQL.DSN, "IM_WORKBENCH_MYSQL_DSN")
|
||||
setInt(&cfg.MySQL.MaxOpenConns, "IM_WORKBENCH_MYSQL_MAX_OPEN_CONNS")
|
||||
setInt(&cfg.MySQL.MaxIdleConns, "IM_WORKBENCH_MYSQL_MAX_IDLE_CONNS")
|
||||
setString(&cfg.CoreMySQL.DSN, "IM_WORKBENCH_CORE_MYSQL_DSN")
|
||||
setInt(&cfg.CoreMySQL.MaxOpenConns, "IM_WORKBENCH_CORE_MYSQL_MAX_OPEN_CONNS")
|
||||
setInt(&cfg.CoreMySQL.MaxIdleConns, "IM_WORKBENCH_CORE_MYSQL_MAX_IDLE_CONNS")
|
||||
if strings.TrimSpace(cfg.CoreMySQL.DSN) == "" {
|
||||
cfg.CoreMySQL.DSN = cfg.MySQL.DSN
|
||||
}
|
||||
if cfg.CoreMySQL.MaxOpenConns == 0 {
|
||||
cfg.CoreMySQL.MaxOpenConns = cfg.MySQL.MaxOpenConns
|
||||
}
|
||||
if cfg.CoreMySQL.MaxIdleConns == 0 {
|
||||
cfg.CoreMySQL.MaxIdleConns = cfg.MySQL.MaxIdleConns
|
||||
}
|
||||
setString(&cfg.Redis.Addr, "IM_WORKBENCH_REDIS_ADDR")
|
||||
setString(&cfg.Redis.Username, "IM_WORKBENCH_REDIS_USERNAME")
|
||||
setString(&cfg.Redis.Password, "IM_WORKBENCH_REDIS_PASSWORD")
|
||||
setString(&cfg.Redis.KeyPrefix, "IM_WORKBENCH_REDIS_KEY_PREFIX")
|
||||
setString(&cfg.Kafka.Topic, "IM_WORKBENCH_KAFKA_TOPIC")
|
||||
setString(&cfg.Kafka.Username, "IM_WORKBENCH_KAFKA_USERNAME")
|
||||
setString(&cfg.Kafka.Password, "IM_WORKBENCH_KAFKA_PASSWORD")
|
||||
setString(&cfg.Kafka.SASL, "IM_WORKBENCH_KAFKA_SASL")
|
||||
setString(&cfg.Kafka.ClientID, "IM_WORKBENCH_KAFKA_CLIENT_ID")
|
||||
setString(&cfg.Auth.SessionKeyPattern, "IM_WORKBENCH_SESSION_KEY_PATTERN")
|
||||
setString(&cfg.Auth.UIDKey, "IM_WORKBENCH_UID_KEY")
|
||||
setString(&cfg.Auth.TrustedInternalAddr, "IM_WORKBENCH_TRUSTED_INTERNAL_ADDR")
|
||||
setString(&cfg.Auth.InternalServiceName, "IM_WORKBENCH_INTERNAL_SERVICE_NAME")
|
||||
setString(&cfg.Auth.InternalSecret, "IM_WORKBENCH_INTERNAL_SECRET")
|
||||
setString(&cfg.Manager.BaseURL, "IM_WORKBENCH_MANAGER_BASE_URL")
|
||||
setString(&cfg.Manager.ServiceName, "IM_WORKBENCH_MANAGER_SERVICE_NAME")
|
||||
setString(&cfg.Manager.Secret, "IM_WORKBENCH_MANAGER_SECRET")
|
||||
setString(&cfg.Manager.UIDKeyGroup, "IM_WORKBENCH_MANAGER_UID_KEY_GROUP")
|
||||
setString(&cfg.AI.Provider, "IM_WORKBENCH_AI_PROVIDER")
|
||||
setString(&cfg.AI.DeepSeek.BaseURL, "DEEPSEEK_BASE_URL")
|
||||
setString(&cfg.AI.DeepSeek.APIKey, "DEEPSEEK_API_KEY")
|
||||
setString(&cfg.AI.DefaultModel, "DEEPSEEK_DEFAULT_MODEL")
|
||||
setString(&cfg.AI.ComplexModel, "DEEPSEEK_COMPLEX_MODEL")
|
||||
setInt(&cfg.AI.DeepSeek.TimeoutMS, "DEEPSEEK_TIMEOUT_MS")
|
||||
setInt(&cfg.AI.DeepSeek.MaxRetries, "DEEPSEEK_MAX_RETRIES")
|
||||
setInt(&cfg.AI.MaxOutputTokens, "DEEPSEEK_MAX_OUTPUT_TOKENS")
|
||||
setFloat(&cfg.AI.Temperature, "DEEPSEEK_TEMPERATURE")
|
||||
if strings.TrimSpace(cfg.Auth.InternalSecret) == "" {
|
||||
setString(&cfg.Auth.InternalSecret, "IM_INTERNAL_API_SECRET")
|
||||
}
|
||||
if strings.TrimSpace(cfg.Auth.InternalSecret) == "" {
|
||||
setString(&cfg.Auth.InternalSecret, "IM_ADMIN_API_SECRET")
|
||||
}
|
||||
if strings.TrimSpace(cfg.Manager.Secret) == "" {
|
||||
setString(&cfg.Manager.Secret, "IM_CONFIG_API_SECRET")
|
||||
}
|
||||
if strings.TrimSpace(cfg.Manager.Secret) == "" {
|
||||
setString(&cfg.Manager.Secret, "IM_INTERNAL_API_SECRET")
|
||||
}
|
||||
if brokers := strings.TrimSpace(os.Getenv("IM_WORKBENCH_KAFKA_BROKERS")); brokers != "" {
|
||||
cfg.Kafka.Brokers = splitCSV(brokers)
|
||||
}
|
||||
if keys := strings.TrimSpace(os.Getenv("IM_WORKBENCH_API_KEYS")); keys != "" {
|
||||
setString(&cfg.Listen, "WORKBENCH_LISTEN")
|
||||
setString(&cfg.MySQL.DSN, "WORKBENCH_MYSQL_DSN")
|
||||
setInt(&cfg.MySQL.MaxOpenConns, "WORKBENCH_MYSQL_MAX_OPEN_CONNS")
|
||||
setInt(&cfg.MySQL.MaxIdleConns, "WORKBENCH_MYSQL_MAX_IDLE_CONNS")
|
||||
setString(&cfg.Redis.Addr, "WORKBENCH_REDIS_ADDR")
|
||||
setString(&cfg.Redis.Username, "WORKBENCH_REDIS_USERNAME")
|
||||
setString(&cfg.Redis.Password, "WORKBENCH_REDIS_PASSWORD")
|
||||
setInt(&cfg.Redis.DB, "WORKBENCH_REDIS_DB")
|
||||
setString(&cfg.Auth.SessionKeyPattern, "WORKBENCH_SESSION_KEY_PATTERN")
|
||||
setInt(&cfg.Auth.SessionTTLSeconds, "WORKBENCH_SESSION_TTL_SECONDS")
|
||||
setInt64(&cfg.Auth.DefaultTenantID, "WORKBENCH_DEFAULT_TENANT_ID")
|
||||
setString(&cfg.Auth.DataEncryptionKey, "WORKBENCH_DATA_ENCRYPTION_KEY")
|
||||
setString(&cfg.Auth.LookupHMACKey, "WORKBENCH_LOOKUP_HMAC_KEY")
|
||||
setBool(&cfg.Auth.AllowDevHeaderAuth, "WORKBENCH_ALLOW_DEV_HEADER_AUTH")
|
||||
setBool(&cfg.Auth.RequiredAPIKey, "WORKBENCH_REQUIRED_API_KEY")
|
||||
if keys := strings.TrimSpace(os.Getenv("WORKBENCH_API_KEYS")); keys != "" {
|
||||
cfg.Auth.APIKeys = splitCSV(keys)
|
||||
}
|
||||
if keys := strings.TrimSpace(os.Getenv("IM_WORKBENCH_MANAGER_UID_KEY_KEYS")); keys != "" {
|
||||
cfg.Manager.UIDKeyKeys = splitCSV(keys)
|
||||
}
|
||||
if value := strings.TrimSpace(os.Getenv("IM_WORKBENCH_KAFKA_BATCH_SIZE")); value != "" {
|
||||
if parsed, err := strconv.Atoi(value); err == nil {
|
||||
cfg.Kafka.BatchSize = parsed
|
||||
}
|
||||
}
|
||||
if value := strings.TrimSpace(os.Getenv("IM_WORKBENCH_MANAGER_TIMEOUT_MS")); value != "" {
|
||||
if parsed, err := strconv.Atoi(value); err == nil {
|
||||
cfg.Manager.TimeoutMS = parsed
|
||||
}
|
||||
}
|
||||
if value := strings.TrimSpace(os.Getenv("IM_WORKBENCH_REDIS_DB")); value != "" {
|
||||
if parsed, err := strconv.Atoi(value); err == nil {
|
||||
cfg.Redis.DB = parsed
|
||||
}
|
||||
}
|
||||
setBool(&cfg.Auth.AllowDevHeaderAuth, "IM_WORKBENCH_ALLOW_DEV_HEADER_AUTH")
|
||||
setBool(&cfg.Auth.RequiredTenantCode, "IM_WORKBENCH_REQUIRED_TENANT_CODE")
|
||||
setBool(&cfg.Auth.RequiredAPIKey, "IM_WORKBENCH_REQUIRED_API_KEY")
|
||||
setBool(&cfg.Auth.SkipSessionInDev, "IM_WORKBENCH_SKIP_SESSION_IN_DEV")
|
||||
setBool(&cfg.Kafka.Enabled, "IM_WORKBENCH_KAFKA_ENABLED")
|
||||
}
|
||||
|
||||
func (c *Config) validate() error {
|
||||
@@ -245,47 +111,24 @@ func (c *Config) validate() error {
|
||||
if strings.TrimSpace(c.MySQL.DSN) == "" {
|
||||
return errors.New("mysql dsn is required")
|
||||
}
|
||||
mysqlDSN := strings.TrimPrefix(strings.TrimSpace(c.MySQL.DSN), "mysql://")
|
||||
parsedDSN, err := mysqlDriver.ParseDSN(mysqlDSN)
|
||||
parsed, err := mysqlDriver.ParseDSN(strings.TrimPrefix(strings.TrimSpace(c.MySQL.DSN), "mysql://"))
|
||||
if err != nil {
|
||||
return fmt.Errorf("mysql dsn is invalid: %w", err)
|
||||
}
|
||||
if strings.EqualFold(strings.TrimSpace(parsedDSN.DBName), "tinode") {
|
||||
return errors.New("mysql database tinode is deprecated; use workbench")
|
||||
if !strings.EqualFold(strings.TrimSpace(parsed.DBName), "olons_admin") {
|
||||
return errors.New("mysql database must be olons_admin")
|
||||
}
|
||||
coreMySQLDSN := strings.TrimPrefix(strings.TrimSpace(c.CoreMySQL.DSN), "mysql://")
|
||||
if _, err := mysqlDriver.ParseDSN(coreMySQLDSN); err != nil {
|
||||
return fmt.Errorf("core mysql dsn is invalid: %w", err)
|
||||
}
|
||||
if strings.TrimSpace(c.Redis.Addr) == "" && !c.Auth.SkipSessionInDev {
|
||||
if strings.TrimSpace(c.Redis.Addr) == "" {
|
||||
return errors.New("redis addr is required")
|
||||
}
|
||||
if strings.TrimSpace(c.Auth.SessionKeyPattern) == "" {
|
||||
return errors.New("session key pattern is required")
|
||||
}
|
||||
if !strings.Contains(c.Auth.SessionKeyPattern, "%s") {
|
||||
return fmt.Errorf("session key pattern must include %%s")
|
||||
return errors.New("session key pattern must include %s")
|
||||
}
|
||||
if strings.TrimSpace(c.AI.Provider) == "" {
|
||||
c.AI.Provider = "deepseek"
|
||||
if c.Auth.SessionTTLSeconds <= 0 {
|
||||
c.Auth.SessionTTLSeconds = 14 * 24 * 60 * 60
|
||||
}
|
||||
if strings.TrimSpace(c.AI.DeepSeek.BaseURL) == "" {
|
||||
c.AI.DeepSeek.BaseURL = "https://api.deepseek.com"
|
||||
}
|
||||
if c.AI.DeepSeek.TimeoutMS <= 0 {
|
||||
c.AI.DeepSeek.TimeoutMS = 60000
|
||||
}
|
||||
if strings.TrimSpace(c.AI.DefaultModel) == "" {
|
||||
c.AI.DefaultModel = "deepseek-flash"
|
||||
}
|
||||
if strings.TrimSpace(c.AI.ComplexModel) == "" {
|
||||
c.AI.ComplexModel = c.AI.DefaultModel
|
||||
}
|
||||
if c.AI.Temperature == 0 {
|
||||
c.AI.Temperature = 0.2
|
||||
}
|
||||
if c.AI.MaxOutputTokens <= 0 {
|
||||
c.AI.MaxOutputTokens = 4096
|
||||
if c.Auth.DefaultTenantID < 0 {
|
||||
return errors.New("default tenant id must not be negative")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -304,6 +147,14 @@ func setInt(target *int, key string) {
|
||||
}
|
||||
}
|
||||
|
||||
func setInt64(target *int64, key string) {
|
||||
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
||||
if parsed, err := strconv.ParseInt(value, 10, 64); err == nil {
|
||||
*target = parsed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func setBool(target *bool, key string) {
|
||||
switch strings.ToLower(strings.TrimSpace(os.Getenv(key))) {
|
||||
case "1", "true", "yes", "on":
|
||||
@@ -313,14 +164,6 @@ func setBool(target *bool, key string) {
|
||||
}
|
||||
}
|
||||
|
||||
func setFloat(target *float64, key string) {
|
||||
if value := strings.TrimSpace(os.Getenv(key)); value != "" {
|
||||
if parsed, err := strconv.ParseFloat(value, 64); err == nil {
|
||||
*target = parsed
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func splitCSV(value string) []string {
|
||||
parts := strings.Split(value, ",")
|
||||
items := make([]string, 0, len(parts))
|
||||
|
||||
@@ -1,100 +1,21 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
import "testing"
|
||||
|
||||
func TestValidateRejectsDeprecatedTinodeDatabase(t *testing.T) {
|
||||
cfg := defaultConfig()
|
||||
cfg.MySQL.DSN = "root:root@tcp(127.0.0.1:3306)/tinode?parseTime=true"
|
||||
|
||||
err := cfg.validate()
|
||||
if err == nil || !strings.Contains(err.Error(), "tinode is deprecated") {
|
||||
t.Fatalf("validate() error = %v, want deprecated database error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestDefaultConfigUsesWorkbenchDatabase(t *testing.T) {
|
||||
func TestDefaultConfigUsesOlonsAdmin(t *testing.T) {
|
||||
cfg := defaultConfig()
|
||||
if err := cfg.validate(); err != nil {
|
||||
t.Fatalf("default config validation failed: %v", err)
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(cfg.MySQL.DSN, "/workbench?") {
|
||||
t.Fatalf("default mysql dsn = %q, want workbench database", cfg.MySQL.DSN)
|
||||
}
|
||||
if cfg.MySQL.MaxOpenConns != 15 || cfg.MySQL.MaxIdleConns != 5 {
|
||||
t.Fatalf("default mysql pool = %d/%d, want 15/5", cfg.MySQL.MaxOpenConns, cfg.MySQL.MaxIdleConns)
|
||||
if cfg.Auth.DefaultTenantID != 0 {
|
||||
t.Fatalf("DefaultTenantID = %d", cfg.Auth.DefaultTenantID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyEnvOverridesMySQLPool(t *testing.T) {
|
||||
t.Setenv("IM_WORKBENCH_MYSQL_MAX_OPEN_CONNS", "12")
|
||||
t.Setenv("IM_WORKBENCH_MYSQL_MAX_IDLE_CONNS", "4")
|
||||
func TestConfigRejectsOtherDatabase(t *testing.T) {
|
||||
cfg := defaultConfig()
|
||||
|
||||
applyEnv(cfg)
|
||||
|
||||
if cfg.MySQL.MaxOpenConns != 12 || cfg.MySQL.MaxIdleConns != 4 {
|
||||
t.Fatalf("mysql pool = %d/%d, want 12/4", cfg.MySQL.MaxOpenConns, cfg.MySQL.MaxIdleConns)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadFallsBackCoreMySQLToPrimaryDSN(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "workbench.json")
|
||||
if err := os.WriteFile(path, []byte(`{"mysql":{"dsn":"root:root@tcp(127.0.0.1:3306)/workbench?parseTime=true"}}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("IM_WORKBENCH_CONFIG", path)
|
||||
t.Setenv("IM_WORKBENCH_MYSQL_DSN", "")
|
||||
t.Setenv("IM_WORKBENCH_CORE_MYSQL_DSN", "")
|
||||
|
||||
cfg, err := Load()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if cfg.CoreMySQL.DSN != cfg.MySQL.DSN {
|
||||
t.Fatalf("core_mysql.dsn = %q, want primary mysql.dsn %q", cfg.CoreMySQL.DSN, cfg.MySQL.DSN)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadUsesCoreMySQLDSNFromEnvironment(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "workbench.json")
|
||||
if err := os.WriteFile(path, []byte(`{"mysql":{"dsn":"root:root@tcp(127.0.0.1:3306)/workbench?parseTime=true"}}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("IM_WORKBENCH_CONFIG", path)
|
||||
t.Setenv("IM_WORKBENCH_MYSQL_DSN", "")
|
||||
t.Setenv("IM_WORKBENCH_CORE_MYSQL_DSN", "root:root@tcp(127.0.0.1:3306)/meeting_im?parseTime=true")
|
||||
|
||||
cfg, err := Load()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(cfg.CoreMySQL.DSN, "/meeting_im?") {
|
||||
t.Fatalf("core_mysql.dsn = %q, want meeting_im", cfg.CoreMySQL.DSN)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadPreservesCoreMySQLDSNFromJSON(t *testing.T) {
|
||||
path := filepath.Join(t.TempDir(), "workbench.json")
|
||||
if err := os.WriteFile(path, []byte(`{"mysql":{"dsn":"root:root@tcp(127.0.0.1:3306)/workbench?parseTime=true"},"core_mysql":{"dsn":"root:root@tcp(127.0.0.1:3306)/meeting_im?parseTime=true"}}`), 0o600); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
t.Setenv("IM_WORKBENCH_CONFIG", path)
|
||||
t.Setenv("IM_WORKBENCH_MYSQL_DSN", "")
|
||||
t.Setenv("IM_WORKBENCH_CORE_MYSQL_DSN", "")
|
||||
|
||||
cfg, err := Load()
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !strings.Contains(cfg.MySQL.DSN, "/workbench?") {
|
||||
t.Fatalf("mysql.dsn = %q, want workbench", cfg.MySQL.DSN)
|
||||
}
|
||||
if !strings.Contains(cfg.CoreMySQL.DSN, "/meeting_im?") {
|
||||
t.Fatalf("core_mysql.dsn = %q, want meeting_im", cfg.CoreMySQL.DSN)
|
||||
cfg.MySQL.DSN = "root:root@tcp(127.0.0.1:3306)/other_database?parseTime=true"
|
||||
if err := cfg.validate(); err == nil {
|
||||
t.Fatal("expected non-olons_admin database to be rejected")
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user