feat(setting): Implemented the settings administrative menu
This commit is contained in:
258
lib/server/admin_settings_test.go
Normal file
258
lib/server/admin_settings_test.go
Normal file
@@ -0,0 +1,258 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
|
||||
"warpbox/lib/config"
|
||||
)
|
||||
|
||||
func TestAdminSettingsRequiresAuth(t *testing.T) {
|
||||
app, router := setupAdminSettingsTest(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "/admin/settings", nil)
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusSeeOther {
|
||||
t.Fatalf("expected redirect, got %d", response.Code)
|
||||
}
|
||||
if location := response.Header().Get("Location"); location != "/admin/login" {
|
||||
t.Fatalf("expected login redirect, got %q", location)
|
||||
}
|
||||
if app == nil {
|
||||
t.Fatal("expected app setup")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminSettingsPageRenders(t *testing.T) {
|
||||
app, router := setupAdminSettingsTest(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "/admin/settings", nil)
|
||||
request.AddCookie(authCookie(app))
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", response.Code)
|
||||
}
|
||||
body := response.Body.String()
|
||||
if !strings.Contains(body, "WarpBox Settings") {
|
||||
t.Fatalf("expected settings page title, got %s", body)
|
||||
}
|
||||
if !strings.Contains(body, "WARPBOX_API_ENABLED") {
|
||||
t.Fatalf("expected API env var in page body")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminSettingsExportIncludesCurrentValues(t *testing.T) {
|
||||
app, router := setupAdminSettingsTest(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodGet, "/admin/settings/export", nil)
|
||||
request.AddCookie(authCookie(app))
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", response.Code)
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Format string `json:"format"`
|
||||
Settings map[string]string `json:"settings"`
|
||||
}
|
||||
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
|
||||
t.Fatalf("json.Unmarshal returned error: %v", err)
|
||||
}
|
||||
if payload.Format != "warpbox.settings.export.v1" {
|
||||
t.Fatalf("unexpected export format: %q", payload.Format)
|
||||
}
|
||||
if payload.Settings[config.SettingAPIEnabled] != "false" {
|
||||
t.Fatalf("expected api_enabled to reflect environment false, got %q", payload.Settings[config.SettingAPIEnabled])
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminSettingsSavePersistsEditableOverrides(t *testing.T) {
|
||||
app, router := setupAdminSettingsTest(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/admin/settings/save", strings.NewReader(`{"values":{"api_enabled":"true","box_poll_interval_ms":"6000"}}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.AddCookie(authCookie(app))
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", response.Code, response.Body.String())
|
||||
}
|
||||
if !app.config.APIEnabled {
|
||||
t.Fatal("expected APIEnabled override to be applied")
|
||||
}
|
||||
if app.config.BoxPollIntervalMS != 6000 {
|
||||
t.Fatalf("expected poll interval override, got %d", app.config.BoxPollIntervalMS)
|
||||
}
|
||||
|
||||
overrides, err := config.ReadAdminSettingsOverrides(app.settingsOverridesPath)
|
||||
if err != nil {
|
||||
t.Fatalf("ReadAdminSettingsOverrides returned error: %v", err)
|
||||
}
|
||||
if overrides[config.SettingAPIEnabled] != "true" {
|
||||
t.Fatalf("expected persisted API override, got %#v", overrides)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminSettingsSaveRejectsLockedSetting(t *testing.T) {
|
||||
app, router := setupAdminSettingsTest(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/admin/settings/save", strings.NewReader(`{"values":{"data_dir":"./other"}}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.AddCookie(authCookie(app))
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusBadRequest {
|
||||
t.Fatalf("expected 400, got %d", response.Code)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminSettingsImportSkipsLockedAndUnknownKeys(t *testing.T) {
|
||||
app, router := setupAdminSettingsTest(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/admin/settings/import", strings.NewReader(`{"settings":{"api_enabled":"true","data_dir":"./other","bogus":"x"}}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.AddCookie(authCookie(app))
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", response.Code, response.Body.String())
|
||||
}
|
||||
if !app.config.APIEnabled {
|
||||
t.Fatal("expected editable import value to apply")
|
||||
}
|
||||
|
||||
var payload struct {
|
||||
Warnings []string `json:"warnings"`
|
||||
}
|
||||
if err := json.Unmarshal(response.Body.Bytes(), &payload); err != nil {
|
||||
t.Fatalf("json.Unmarshal returned error: %v", err)
|
||||
}
|
||||
if len(payload.Warnings) != 2 {
|
||||
t.Fatalf("expected 2 warnings, got %#v", payload.Warnings)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAdminSettingsResetUsesBuiltInDefaults(t *testing.T) {
|
||||
app, router := setupAdminSettingsTest(t)
|
||||
|
||||
request := httptest.NewRequest(http.MethodPost, "/admin/settings/reset", strings.NewReader(`{}`))
|
||||
request.Header.Set("Content-Type", "application/json")
|
||||
request.AddCookie(authCookie(app))
|
||||
response := httptest.NewRecorder()
|
||||
router.ServeHTTP(response, request)
|
||||
|
||||
if response.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d: %s", response.Code, response.Body.String())
|
||||
}
|
||||
if !app.config.APIEnabled {
|
||||
t.Fatal("expected reset to built-in defaults to restore APIEnabled=true")
|
||||
}
|
||||
}
|
||||
|
||||
func setupAdminSettingsTest(t *testing.T) (*App, *gin.Engine) {
|
||||
t.Helper()
|
||||
gin.SetMode(gin.TestMode)
|
||||
cwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
t.Fatalf("Getwd returned error: %v", err)
|
||||
}
|
||||
root := filepath.Clean(filepath.Join(cwd, "..", ".."))
|
||||
if err := os.Chdir(root); err != nil {
|
||||
t.Fatalf("Chdir returned error: %v", err)
|
||||
}
|
||||
t.Cleanup(func() {
|
||||
_ = os.Chdir(cwd)
|
||||
})
|
||||
clearAdminSettingsEnv(t)
|
||||
t.Setenv("WARPBOX_DATA_DIR", t.TempDir())
|
||||
t.Setenv("WARPBOX_ADMIN_PASSWORD", "secret")
|
||||
t.Setenv("WARPBOX_ADMIN_ENABLED", "true")
|
||||
t.Setenv("WARPBOX_API_ENABLED", "false")
|
||||
|
||||
cfg, err := config.Load()
|
||||
if err != nil {
|
||||
t.Fatalf("Load returned error: %v", err)
|
||||
}
|
||||
if err := cfg.EnsureDirectories(); err != nil {
|
||||
t.Fatalf("EnsureDirectories returned error: %v", err)
|
||||
}
|
||||
|
||||
app := &App{
|
||||
config: cfg,
|
||||
settingsOverridesPath: filepath.Join(cfg.DBDir, config.AdminSettingsOverrideFilename),
|
||||
}
|
||||
|
||||
htmlTemplates, err := loadHTMLTemplates()
|
||||
if err != nil {
|
||||
t.Fatalf("loadHTMLTemplates returned error: %v", err)
|
||||
}
|
||||
|
||||
router := gin.New()
|
||||
router.SetHTMLTemplate(htmlTemplates)
|
||||
admin := router.Group("/admin")
|
||||
admin.GET("/login", app.handleAdminLogin)
|
||||
protected := router.Group("/admin", app.adminAuthMiddleware)
|
||||
protected.GET("/settings", app.handleAdminSettings)
|
||||
protected.GET("/settings/export", app.handleAdminSettingsExport)
|
||||
protected.POST("/settings/save", app.handleAdminSettingsSave)
|
||||
protected.POST("/settings/import", app.handleAdminSettingsImport)
|
||||
protected.POST("/settings/reset", app.handleAdminSettingsReset)
|
||||
return app, router
|
||||
}
|
||||
|
||||
func authCookie(app *App) *http.Cookie {
|
||||
return &http.Cookie{Name: adminSessionCookie, Value: app.adminSessionToken()}
|
||||
}
|
||||
|
||||
func clearAdminSettingsEnv(t *testing.T) {
|
||||
t.Helper()
|
||||
for _, name := range []string{
|
||||
"WARPBOX_DATA_DIR",
|
||||
"WARPBOX_ADMIN_PASSWORD",
|
||||
"WARPBOX_ADMIN_USERNAME",
|
||||
"WARPBOX_ADMIN_EMAIL",
|
||||
"WARPBOX_ADMIN_ENABLED",
|
||||
"WARPBOX_ALLOW_ADMIN_SETTINGS_OVERRIDE",
|
||||
"WARPBOX_ADMIN_COOKIE_SECURE",
|
||||
"WARPBOX_GUEST_UPLOADS_ENABLED",
|
||||
"WARPBOX_API_ENABLED",
|
||||
"WARPBOX_ZIP_DOWNLOADS_ENABLED",
|
||||
"WARPBOX_ONE_TIME_DOWNLOADS_ENABLED",
|
||||
"WARPBOX_ONE_TIME_DOWNLOAD_EXPIRY_SECONDS",
|
||||
"WARPBOX_ONE_TIME_DOWNLOAD_RETRY_ON_FAILURE",
|
||||
"WARPBOX_RENEW_ON_ACCESS_ENABLED",
|
||||
"WARPBOX_RENEW_ON_DOWNLOAD_ENABLED",
|
||||
"WARPBOX_DEFAULT_GUEST_EXPIRY_SECONDS",
|
||||
"WARPBOX_MAX_GUEST_EXPIRY_SECONDS",
|
||||
"WARPBOX_GLOBAL_MAX_FILE_SIZE_MB",
|
||||
"WARPBOX_GLOBAL_MAX_FILE_SIZE_BYTES",
|
||||
"WARPBOX_GLOBAL_MAX_BOX_SIZE_MB",
|
||||
"WARPBOX_GLOBAL_MAX_BOX_SIZE_BYTES",
|
||||
"WARPBOX_DEFAULT_USER_MAX_FILE_SIZE_MB",
|
||||
"WARPBOX_DEFAULT_USER_MAX_FILE_SIZE_BYTES",
|
||||
"WARPBOX_DEFAULT_USER_MAX_BOX_SIZE_MB",
|
||||
"WARPBOX_DEFAULT_USER_MAX_BOX_SIZE_BYTES",
|
||||
"WARPBOX_SESSION_TTL_SECONDS",
|
||||
"WARPBOX_BOX_POLL_INTERVAL_MS",
|
||||
"WARPBOX_THUMBNAIL_BATCH_SIZE",
|
||||
"WARPBOX_THUMBNAIL_INTERVAL_SECONDS",
|
||||
} {
|
||||
t.Setenv(name, "")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user