API / internal /api /handlers /management /generics_test.go
sshinmen's picture
Refactor auth handlers and executors
9b1c663
Raw
History Blame Contribute Delete
5.74 kB
package management
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"path/filepath"
"sort"
"strings"
"testing"
"github.com/gin-gonic/gin"
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
"github.com/stretchr/testify/assert"
)
// setupTestHandler creates a temporary config file and a Handler for testing.
// It returns the handler, the config object, the cleanup function, and an error if any.
func setupTestHandler(t *testing.T) (*Handler, *config.Config, func(), error) {
// Create a temporary file for config
tmpFile, err := os.CreateTemp("", "config_test_*.yaml")
if err != nil {
return nil, nil, nil, err
}
// Write initial valid YAML content (empty object)
if _, err := tmpFile.WriteString("{}\n"); err != nil {
tmpFile.Close()
os.Remove(tmpFile.Name())
return nil, nil, nil, err
}
tmpFile.Close()
cfg := &config.Config{
SDKConfig: config.SDKConfig{
Access: config.AccessConfig{
Providers: nil,
},
},
}
h := NewHandler(cfg, tmpFile.Name(), nil)
cleanup := func() {
os.Remove(tmpFile.Name())
}
return h, cfg, cleanup, nil
}
func TestPutAPIKeys(t *testing.T) {
gin.SetMode(gin.TestMode)
h, cfg, cleanup, err := setupTestHandler(t)
assert.NoError(t, err)
defer cleanup()
// Initial state
cfg.APIKeys = []string{"initial-key"}
// Test case: Put a new list of keys
newKeys := []string{"key1", "key2", "key3"}
body, _ := json.Marshal(newKeys)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodPut, "/api-keys", bytes.NewBuffer(body))
h.PutAPIKeys(c)
assert.Equal(t, http.StatusOK, w.Code)
// Verify memory update
assert.Equal(t, newKeys, cfg.APIKeys)
// Verify Access.Providers is cleared (as per PutAPIKeys logic)
assert.Nil(t, cfg.Access.Providers)
// Test case: Put using object wrapper { "items": [...] }
wrapperBody := `{"items": ["wrapped-key-1", "wrapped-key-2"]}`
w = httptest.NewRecorder()
c, _ = gin.CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodPut, "/api-keys", bytes.NewBufferString(wrapperBody))
h.PutAPIKeys(c)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, []string{"wrapped-key-1", "wrapped-key-2"}, cfg.APIKeys)
}
func TestDeleteAPIKeys(t *testing.T) {
gin.SetMode(gin.TestMode)
h, cfg, cleanup, err := setupTestHandler(t)
assert.NoError(t, err)
defer cleanup()
// Initial state
cfg.APIKeys = []string{"key1", "key2", "key3", "key4"}
// Test case: Delete by value (query param "value")
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodDelete, "/api-keys?value=key2", nil)
h.DeleteAPIKeys(c)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, []string{"key1", "key3", "key4"}, cfg.APIKeys)
// Test case: Delete by index
w = httptest.NewRecorder()
c, _ = gin.CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodDelete, "/api-keys?index=1", nil) // Deleting index 1 (key3 now, since key2 is gone)
h.DeleteAPIKeys(c)
assert.Equal(t, http.StatusOK, w.Code)
assert.Equal(t, []string{"key1", "key4"}, cfg.APIKeys)
// Test case: Delete non-existent value
w = httptest.NewRecorder()
c, _ = gin.CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodDelete, "/api-keys?value=non-existent", nil)
h.DeleteAPIKeys(c)
assert.Equal(t, http.StatusNotFound, w.Code)
assert.Equal(t, []string{"key1", "key4"}, cfg.APIKeys)
// Test case: Delete invalid index
w = httptest.NewRecorder()
c, _ = gin.CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodDelete, "/api-keys?index=99", nil)
h.DeleteAPIKeys(c)
// GenericDeleteList returns 400 if index is out of bounds but parsed correctly?
// The code says: if _, err := fmt.Sscanf(idxStr, "%d", &idx); err == nil && idx >= 0 && idx < len(list)
// If that condition fails, it falls through to c.JSON(400, "missing ... or index")
assert.Equal(t, http.StatusBadRequest, w.Code)
assert.Equal(t, []string{"key1", "key4"}, cfg.APIKeys)
}
func TestGenericUpdateField(t *testing.T) {
gin.SetMode(gin.TestMode)
h, cfg, cleanup, err := setupTestHandler(t)
assert.NoError(t, err)
defer cleanup()
// Test updating a bool field (Debug)
cfg.Debug = false
body := `{"value": true}`
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
c.Request, _ = http.NewRequest(http.MethodPut, "/debug", bytes.NewBufferString(body))
// Manually call genericUpdateField with a setter that updates cfg.Debug
genericUpdateField(h, c, func(v bool) {
cfg.Debug = v
})
assert.Equal(t, http.StatusOK, w.Code)
assert.True(t, cfg.Debug)
// Verify persistence (load from file)
loaded, err := config.LoadConfig(h.configFilePath)
assert.NoError(t, err)
assert.True(t, loaded.Debug)
}
func TestGenericListFiles(t *testing.T) {
gin.SetMode(gin.TestMode)
// Create temp dir
tmpDir, err := os.MkdirTemp("", "test_files")
assert.NoError(t, err)
defer os.RemoveAll(tmpDir)
// Create some files
_ = os.WriteFile(filepath.Join(tmpDir, "file1.txt"), []byte("content"), 0644)
_ = os.WriteFile(filepath.Join(tmpDir, "file2.json"), []byte("content"), 0644)
_ = os.WriteFile(filepath.Join(tmpDir, "file3.txt"), []byte("content"), 0644)
w := httptest.NewRecorder()
c, _ := gin.CreateTestContext(w)
genericListFiles(c, tmpDir, func(e os.DirEntry) (string, bool) {
if strings.HasSuffix(e.Name(), ".txt") {
return e.Name(), true
}
return "", false
}, func(files []string) {
// Sort
sort.Strings(files)
})
assert.Equal(t, http.StatusOK, w.Code)
var resp struct {
Files []string `json:"files"`
}
err = json.Unmarshal(w.Body.Bytes(), &resp)
assert.NoError(t, err)
assert.Equal(t, []string{"file1.txt", "file3.txt"}, resp.Files)
}