middleware-manager/api/handlers/plugin_handler_test.go
hhftechnologies caabae1ab1 Upgrade Go/toolchain and deps; add rollback helper
Bump Go versions in CI, Dockerfile and go.mod/toolchain to 1.25.x and update several module dependencies. Introduce rollbackTransaction(tx, context) and replace direct tx.Rollback() calls (including panic recovery) across handlers to centralize rollback logging/handling. Replace direct json.Unmarshal/Scan calls in tests with mustUnmarshalResponse/mustScan helpers. Add TrustedProxies to ServerConfig and configure Gin with SetTrustedProxies. Misc: adjust final Docker image (Alpine version and healthcheck command) and minor logging/formatting cleanups.
2026-04-16 03:38:12 +05:30

200 lines
6 KiB
Go

package handlers
import (
"bytes"
"encoding/json"
"net/http"
"os"
"path/filepath"
"testing"
"github.com/gin-gonic/gin"
"github.com/hhftechnology/middleware-manager/internal/testutil"
)
// TestNewPluginHandler tests plugin handler creation
func TestNewPluginHandler(t *testing.T) {
db := testutil.NewTempDB(t)
cm := testutil.NewTestConfigManager(t)
configPath := filepath.Join(t.TempDir(), "traefik.yml")
handler := NewPluginHandler(db.DB, configPath, cm)
if handler == nil {
t.Fatal("NewPluginHandler() returned nil")
}
if handler.DB == nil {
t.Error("handler.DB is nil")
}
if handler.TraefikStaticConfigPath != configPath {
t.Errorf("TraefikStaticConfigPath = %q, want %q", handler.TraefikStaticConfigPath, configPath)
}
}
// TestPluginHandler_RefreshPluginFetcher tests fetcher refresh
func TestPluginHandler_RefreshPluginFetcher(t *testing.T) {
db := testutil.NewTempDB(t)
cm := testutil.NewTestConfigManager(t)
configPath := filepath.Join(t.TempDir(), "traefik.yml")
handler := NewPluginHandler(db.DB, configPath, cm)
err := handler.RefreshPluginFetcher()
if err != nil {
t.Errorf("RefreshPluginFetcher() error = %v", err)
}
}
// TestPluginHandler_RefreshPluginFetcher_NoConfigManager tests refresh without config manager
func TestPluginHandler_RefreshPluginFetcher_NoConfigManager(t *testing.T) {
db := testutil.NewTempDB(t)
configPath := filepath.Join(t.TempDir(), "traefik.yml")
handler := NewPluginHandler(db.DB, configPath, nil)
err := handler.RefreshPluginFetcher()
if err == nil {
t.Error("RefreshPluginFetcher() should error without config manager")
}
}
// TestPluginHandler_GetPlugins tests getting plugins
func TestPluginHandler_GetPlugins(t *testing.T) {
db := testutil.NewTempDB(t)
cm := testutil.NewTestConfigManager(t)
configPath := filepath.Join(t.TempDir(), "traefik.yml")
handler := NewPluginHandler(db.DB, configPath, cm)
c, rec := testutil.NewContext(t, http.MethodGet, "/api/plugins", nil)
handler.GetPlugins(c)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String())
}
var plugins []map[string]interface{}
if err := json.Unmarshal(rec.Body.Bytes(), &plugins); err != nil {
t.Fatalf("failed to parse response: %v", err)
}
// Should return an array (possibly empty)
if plugins == nil {
t.Error("expected array, got nil")
}
}
// TestPluginHandler_GetPlugins_WithLocalConfig tests getting plugins with local config
func TestPluginHandler_GetPlugins_WithLocalConfig(t *testing.T) {
db := testutil.NewTempDB(t)
cm := testutil.NewTestConfigManager(t)
configDir := t.TempDir()
configPath := filepath.Join(configDir, "traefik.yml")
// Create a test traefik config with plugins
traefikConfig := `
experimental:
plugins:
mtls-whitelist:
moduleName: github.com/example/mtls-whitelist
version: v1.0.0
`
if err := os.WriteFile(configPath, []byte(traefikConfig), 0644); err != nil {
t.Fatalf("failed to write test config: %v", err)
}
handler := NewPluginHandler(db.DB, configPath, cm)
c, rec := testutil.NewContext(t, http.MethodGet, "/api/plugins", nil)
handler.GetPlugins(c)
if rec.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", rec.Code, rec.Body.String())
}
var plugins []map[string]interface{}
mustUnmarshalResponse(t, rec.Body.Bytes(), &plugins)
// Should find the plugin from local config
found := false
for _, p := range plugins {
if p["name"] == "mtls-whitelist" {
found = true
break
}
}
if !found && len(plugins) > 0 {
// Plugin should be in the list
t.Log("Plugin not found in response, but this may be expected if Traefik API is not available")
}
}
// TestPluginHandler_InstallPlugin_InvalidJSON tests invalid install request
func TestPluginHandler_InstallPlugin_InvalidJSON(t *testing.T) {
db := testutil.NewTempDB(t)
cm := testutil.NewTestConfigManager(t)
configPath := filepath.Join(t.TempDir(), "traefik.yml")
handler := NewPluginHandler(db.DB, configPath, cm)
body := bytes.NewBufferString(`{invalid}`)
c, rec := testutil.NewContext(t, http.MethodPost, "/api/plugins/install", body)
handler.InstallPlugin(c)
if rec.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", rec.Code)
}
}
// TestPluginHandler_InstallPlugin_MissingFields tests install with missing fields
func TestPluginHandler_InstallPlugin_MissingFields(t *testing.T) {
db := testutil.NewTempDB(t)
cm := testutil.NewTestConfigManager(t)
configPath := filepath.Join(t.TempDir(), "traefik.yml")
handler := NewPluginHandler(db.DB, configPath, cm)
// Missing required fields
body := bytes.NewBufferString(`{"name": "test"}`)
c, rec := testutil.NewContext(t, http.MethodPost, "/api/plugins/install", body)
handler.InstallPlugin(c)
if rec.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", rec.Code)
}
}
// TestPluginHandler_RemovePlugin_InvalidJSON ensures remove plugin validates body
func TestPluginHandler_RemovePlugin_InvalidJSON(t *testing.T) {
db := testutil.NewTempDB(t)
cm := testutil.NewTestConfigManager(t)
configPath := filepath.Join(t.TempDir(), "traefik.yml")
handler := NewPluginHandler(db.DB, configPath, cm)
c, rec := testutil.NewContext(t, http.MethodDelete, "/api/plugins/remove", bytes.NewBufferString(`{invalid}`))
handler.RemovePlugin(c)
if rec.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", rec.Code)
}
}
// TestPluginHandler_GetPluginUsage tests getting plugin usage info
func TestPluginHandler_GetPluginUsage(t *testing.T) {
db := testutil.NewTempDB(t)
cm := testutil.NewTestConfigManager(t)
configPath := filepath.Join(t.TempDir(), "traefik.yml")
handler := NewPluginHandler(db.DB, configPath, cm)
c, rec := testutil.NewContext(t, http.MethodGet, "/api/plugins/test-plugin/usage", nil)
c.Params = gin.Params{{Key: "name", Value: "test-plugin"}}
handler.GetPluginUsage(c)
// Depending on fetcher configuration, this may be 404 or 500, but should not panic
if rec.Code != http.StatusOK && rec.Code != http.StatusNotFound && rec.Code != http.StatusInternalServerError {
t.Fatalf("unexpected status code %d", rec.Code)
}
}