mirror of
https://github.com/pocket-id/pocket-id.git
synced 2025-12-11 15:53:00 +03:00
474 lines
14 KiB
Go
474 lines
14 KiB
Go
package service
|
|
|
|
import (
|
|
"sync/atomic"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/pocket-id/pocket-id/backend/internal/common"
|
|
"github.com/pocket-id/pocket-id/backend/internal/dto"
|
|
"github.com/pocket-id/pocket-id/backend/internal/model"
|
|
testutils "github.com/pocket-id/pocket-id/backend/internal/utils/testing"
|
|
)
|
|
|
|
// NewTestAppConfigService is a function used by tests to create AppConfigService objects with pre-defined configuration values
|
|
func NewTestAppConfigService(config *model.AppConfig) *AppConfigService {
|
|
service := &AppConfigService{
|
|
dbConfig: atomic.Pointer[model.AppConfig]{},
|
|
}
|
|
service.dbConfig.Store(config)
|
|
|
|
return service
|
|
}
|
|
|
|
func TestLoadDbConfig(t *testing.T) {
|
|
t.Run("empty config table", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
|
|
// Load the config
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Config should be equal to default config
|
|
require.Equal(t, service.GetDbConfig(), service.getDefaultDbConfig())
|
|
})
|
|
|
|
t.Run("loads value from config table", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Populate the config table with some initial values
|
|
err := db.
|
|
Create([]model.AppConfigVariable{
|
|
// Overrides default value
|
|
{Key: "appName", Value: "Test App"},
|
|
{Key: "sessionDuration", Value: "5"},
|
|
// Does not have a default value
|
|
{Key: "smtpHost", Value: "example"},
|
|
}).
|
|
Error
|
|
require.NoError(t, err)
|
|
|
|
// Load the config
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err = service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Values should match expected ones
|
|
expect := service.getDefaultDbConfig()
|
|
expect.AppName.Value = "Test App"
|
|
expect.SessionDuration.Value = "5"
|
|
expect.SmtpHost.Value = "example"
|
|
require.Equal(t, service.GetDbConfig(), expect)
|
|
})
|
|
|
|
t.Run("ignores unknown config keys", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Add an entry with a key that doesn't exist in the config struct
|
|
err := db.Create([]model.AppConfigVariable{
|
|
{Key: "__nonExistentKey", Value: "some value"},
|
|
{Key: "appName", Value: "TestApp"}, // This one should still be loaded
|
|
}).Error
|
|
require.NoError(t, err)
|
|
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
// This should not fail, just ignore the unknown key
|
|
err = service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
config := service.GetDbConfig()
|
|
require.Equal(t, "TestApp", config.AppName.Value)
|
|
})
|
|
|
|
t.Run("loading config multiple times", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Initial state
|
|
err := db.Create([]model.AppConfigVariable{
|
|
{Key: "appName", Value: "InitialApp"},
|
|
}).Error
|
|
require.NoError(t, err)
|
|
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err = service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
require.Equal(t, "InitialApp", service.GetDbConfig().AppName.Value)
|
|
|
|
// Update the database value
|
|
err = db.Model(&model.AppConfigVariable{}).
|
|
Where("key = ?", "appName").
|
|
Update("value", "UpdatedApp").Error
|
|
require.NoError(t, err)
|
|
|
|
// Load the config again, it should reflect the updated value
|
|
err = service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
require.Equal(t, "UpdatedApp", service.GetDbConfig().AppName.Value)
|
|
})
|
|
|
|
t.Run("loads config from env when UiConfigDisabled is true", func(t *testing.T) {
|
|
// Save the original state and restore it after the test
|
|
originalUiConfigDisabled := common.EnvConfig.UiConfigDisabled
|
|
defer func() {
|
|
common.EnvConfig.UiConfigDisabled = originalUiConfigDisabled
|
|
}()
|
|
|
|
// Set environment variables for testing
|
|
t.Setenv("APP_NAME", "EnvTest App")
|
|
t.Setenv("SESSION_DURATION", "45")
|
|
|
|
// Enable UiConfigDisabled to load from env
|
|
common.EnvConfig.UiConfigDisabled = true
|
|
|
|
// Create database with config that should be ignored
|
|
db := testutils.NewDatabaseForTest(t)
|
|
err := db.Create([]model.AppConfigVariable{
|
|
{Key: "appName", Value: "DB App"},
|
|
{Key: "sessionDuration", Value: "120"},
|
|
}).Error
|
|
require.NoError(t, err)
|
|
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
|
|
// Load the config
|
|
err = service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Config should be loaded from env, not DB
|
|
config := service.GetDbConfig()
|
|
require.Equal(t, "EnvTest App", config.AppName.Value, "Should load appName from env")
|
|
require.Equal(t, "45", config.SessionDuration.Value, "Should load sessionDuration from env")
|
|
})
|
|
|
|
t.Run("ignores env vars when UiConfigDisabled is false", func(t *testing.T) {
|
|
// Save the original state and restore it after the test
|
|
originalUiConfigDisabled := common.EnvConfig.UiConfigDisabled
|
|
defer func() {
|
|
common.EnvConfig.UiConfigDisabled = originalUiConfigDisabled
|
|
}()
|
|
|
|
// Set environment variables that should be ignored
|
|
t.Setenv("APP_NAME", "EnvTest App")
|
|
t.Setenv("SESSION_DURATION", "45")
|
|
|
|
// Make sure UiConfigDisabled is false to load from DB
|
|
common.EnvConfig.UiConfigDisabled = false
|
|
|
|
// Create database with config values that should take precedence
|
|
db := testutils.NewDatabaseForTest(t)
|
|
err := db.Create([]model.AppConfigVariable{
|
|
{Key: "appName", Value: "DB App"},
|
|
{Key: "sessionDuration", Value: "120"},
|
|
}).Error
|
|
require.NoError(t, err)
|
|
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
|
|
// Load the config
|
|
err = service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Config should be loaded from DB, not env
|
|
config := service.GetDbConfig()
|
|
require.Equal(t, "DB App", config.AppName.Value, "Should load appName from DB, not env")
|
|
require.Equal(t, "120", config.SessionDuration.Value, "Should load sessionDuration from DB, not env")
|
|
})
|
|
}
|
|
|
|
func TestUpdateAppConfigValues(t *testing.T) {
|
|
t.Run("update single value", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Create a service with default config
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Update a single config value
|
|
err = service.UpdateAppConfigValues(t.Context(), "appName", "Test App")
|
|
require.NoError(t, err)
|
|
|
|
// Verify in-memory config was updated
|
|
config := service.GetDbConfig()
|
|
require.Equal(t, "Test App", config.AppName.Value)
|
|
|
|
// Verify database was updated
|
|
var dbValue model.AppConfigVariable
|
|
err = db.Where("key = ?", "appName").First(&dbValue).Error
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Test App", dbValue.Value)
|
|
})
|
|
|
|
t.Run("update multiple values", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Create a service with default config
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Update multiple config values
|
|
err = service.UpdateAppConfigValues(
|
|
t.Context(),
|
|
"appName", "Test App",
|
|
"sessionDuration", "30",
|
|
"smtpHost", "mail.example.com",
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Verify in-memory config was updated
|
|
config := service.GetDbConfig()
|
|
require.Equal(t, "Test App", config.AppName.Value)
|
|
require.Equal(t, "30", config.SessionDuration.Value)
|
|
require.Equal(t, "mail.example.com", config.SmtpHost.Value)
|
|
|
|
// Verify database was updated
|
|
var count int64
|
|
db.Model(&model.AppConfigVariable{}).Count(&count)
|
|
require.Equal(t, int64(3), count)
|
|
|
|
var appName, sessionDuration, smtpHost model.AppConfigVariable
|
|
err = db.Where("key = ?", "appName").First(&appName).Error
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Test App", appName.Value)
|
|
|
|
err = db.Where("key = ?", "sessionDuration").First(&sessionDuration).Error
|
|
require.NoError(t, err)
|
|
require.Equal(t, "30", sessionDuration.Value)
|
|
|
|
err = db.Where("key = ?", "smtpHost").First(&smtpHost).Error
|
|
require.NoError(t, err)
|
|
require.Equal(t, "mail.example.com", smtpHost.Value)
|
|
})
|
|
|
|
t.Run("empty value resets to default", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Create a service with default config
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// First change the value
|
|
err = service.UpdateAppConfigValues(t.Context(), "sessionDuration", "30")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "30", service.GetDbConfig().SessionDuration.Value)
|
|
|
|
// Now set it to empty which should use default value
|
|
err = service.UpdateAppConfigValues(t.Context(), "sessionDuration", "")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "60", service.GetDbConfig().SessionDuration.Value) // Default value from getDefaultDbConfig
|
|
})
|
|
|
|
t.Run("error with odd number of arguments", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Create a service with default config
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Try to update with odd number of arguments
|
|
err = service.UpdateAppConfigValues(t.Context(), "appName", "Test App", "sessionDuration")
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid number of arguments")
|
|
})
|
|
|
|
t.Run("error with invalid key", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Create a service with default config
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Try to update with invalid key
|
|
err = service.UpdateAppConfigValues(t.Context(), "nonExistentKey", "some value")
|
|
require.Error(t, err)
|
|
require.Contains(t, err.Error(), "invalid configuration key")
|
|
})
|
|
}
|
|
|
|
func TestUpdateAppConfig(t *testing.T) {
|
|
t.Run("updates configuration values from DTO", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Create a service with default config
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Create update DTO
|
|
input := dto.AppConfigUpdateDto{
|
|
AppName: "Updated App Name",
|
|
SessionDuration: "120",
|
|
SmtpHost: "smtp.example.com",
|
|
SmtpPort: "587",
|
|
}
|
|
|
|
// Update config
|
|
updatedVars, err := service.UpdateAppConfig(t.Context(), input)
|
|
require.NoError(t, err)
|
|
|
|
// Verify returned updated variables
|
|
require.NotEmpty(t, updatedVars)
|
|
|
|
var foundAppName, foundSessionDuration, foundSmtpHost, foundSmtpPort bool
|
|
for _, v := range updatedVars {
|
|
switch v.Key {
|
|
case "appName":
|
|
require.Equal(t, "Updated App Name", v.Value)
|
|
foundAppName = true
|
|
case "sessionDuration":
|
|
require.Equal(t, "120", v.Value)
|
|
foundSessionDuration = true
|
|
case "smtpHost":
|
|
require.Equal(t, "smtp.example.com", v.Value)
|
|
foundSmtpHost = true
|
|
case "smtpPort":
|
|
require.Equal(t, "587", v.Value)
|
|
foundSmtpPort = true
|
|
}
|
|
}
|
|
require.True(t, foundAppName)
|
|
require.True(t, foundSessionDuration)
|
|
require.True(t, foundSmtpHost)
|
|
require.True(t, foundSmtpPort)
|
|
|
|
// Verify in-memory config was updated
|
|
config := service.GetDbConfig()
|
|
require.Equal(t, "Updated App Name", config.AppName.Value)
|
|
require.Equal(t, "120", config.SessionDuration.Value)
|
|
require.Equal(t, "smtp.example.com", config.SmtpHost.Value)
|
|
require.Equal(t, "587", config.SmtpPort.Value)
|
|
|
|
// Verify database was updated
|
|
var appName, sessionDuration, smtpHost, smtpPort model.AppConfigVariable
|
|
err = db.Where("key = ?", "appName").First(&appName).Error
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Updated App Name", appName.Value)
|
|
|
|
err = db.Where("key = ?", "sessionDuration").First(&sessionDuration).Error
|
|
require.NoError(t, err)
|
|
require.Equal(t, "120", sessionDuration.Value)
|
|
|
|
err = db.Where("key = ?", "smtpHost").First(&smtpHost).Error
|
|
require.NoError(t, err)
|
|
require.Equal(t, "smtp.example.com", smtpHost.Value)
|
|
|
|
err = db.Where("key = ?", "smtpPort").First(&smtpPort).Error
|
|
require.NoError(t, err)
|
|
require.Equal(t, "587", smtpPort.Value)
|
|
})
|
|
|
|
t.Run("empty values reset to defaults", func(t *testing.T) {
|
|
db := testutils.NewDatabaseForTest(t)
|
|
|
|
// Create a service with default config and modify some values
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// First set some non-default values
|
|
err = service.UpdateAppConfigValues(t.Context(),
|
|
"appName", "Custom App",
|
|
"sessionDuration", "120",
|
|
)
|
|
require.NoError(t, err)
|
|
|
|
// Create update DTO with empty values to reset to defaults
|
|
input := dto.AppConfigUpdateDto{
|
|
AppName: "", // Should reset to default "Pocket ID"
|
|
SessionDuration: "", // Should reset to default "60"
|
|
}
|
|
|
|
// Update config
|
|
updatedVars, err := service.UpdateAppConfig(t.Context(), input)
|
|
require.NoError(t, err)
|
|
|
|
// Verify returned updated variables (they should be empty strings in DB)
|
|
var foundAppName, foundSessionDuration bool
|
|
for _, v := range updatedVars {
|
|
switch v.Key {
|
|
case "appName":
|
|
require.Equal(t, "Pocket ID", v.Value) // Returns the default value
|
|
foundAppName = true
|
|
case "sessionDuration":
|
|
require.Equal(t, "60", v.Value) // Returns the default value
|
|
foundSessionDuration = true
|
|
}
|
|
}
|
|
require.True(t, foundAppName)
|
|
require.True(t, foundSessionDuration)
|
|
|
|
// Verify in-memory config was reset to defaults
|
|
config := service.GetDbConfig()
|
|
require.Equal(t, "Pocket ID", config.AppName.Value) // Default value
|
|
require.Equal(t, "60", config.SessionDuration.Value) // Default value
|
|
|
|
// Verify database was updated with empty values
|
|
for _, key := range []string{"appName", "sessionDuration"} {
|
|
var loaded model.AppConfigVariable
|
|
err = db.Where("key = ?", key).First(&loaded).Error
|
|
require.NoErrorf(t, err, "Failed to load DB value for key '%s'", key)
|
|
require.Emptyf(t, loaded.Value, "Loaded value for key '%s' is not empty", key)
|
|
}
|
|
})
|
|
|
|
t.Run("cannot update when UiConfigDisabled is true", func(t *testing.T) {
|
|
// Save the original state and restore it after the test
|
|
originalUiConfigDisabled := common.EnvConfig.UiConfigDisabled
|
|
defer func() {
|
|
common.EnvConfig.UiConfigDisabled = originalUiConfigDisabled
|
|
}()
|
|
|
|
// Disable UI config
|
|
common.EnvConfig.UiConfigDisabled = true
|
|
|
|
db := testutils.NewDatabaseForTest(t)
|
|
service := &AppConfigService{
|
|
db: db,
|
|
}
|
|
err := service.LoadDbConfig(t.Context())
|
|
require.NoError(t, err)
|
|
|
|
// Try to update config
|
|
_, err = service.UpdateAppConfig(t.Context(), dto.AppConfigUpdateDto{
|
|
AppName: "Should Not Update",
|
|
})
|
|
|
|
// Should get a UiConfigDisabledError
|
|
require.Error(t, err)
|
|
var uiConfigDisabledErr *common.UiConfigDisabledError
|
|
require.ErrorAs(t, err, &uiConfigDisabledErr)
|
|
})
|
|
}
|