package api import ( "bytes" "encoding/json" "net/http/httptest" "testing" "time" "github.com/golang-jwt/jwt/v5" "github.com/pulse-lets-go/internal/config" "github.com/pulse-lets-go/internal/engine" ) func testCfg() *config.Config { return &config.Config{ StaleThresholdSec: 20, Log: config.LogConfig{Level: "info", Format: "text"}, RateLimit: config.RateLimitConfig{Enabled: false}, BalanceMode: "weighted_random", Scoring: config.ScoringConfig{ IdleCPUMin: 5.0, CallFailureRateLethal: 15.0, Weights: config.ScoringWeights{CallScore: 0.40, LoadScore: 0.30, IdleScore: 0.20, FailScore: 0.10}, }, } } func createTestJWT(secret, username, role string) string { claims := jwt.MapClaims{ "user_id": "user-test", "username": username, "role": role, "exp": time.Now().Add(1 * time.Hour).Unix(), } token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) s, _ := token.SignedString([]byte(secret)) return s } func TestRouteModeEndpoint_NoAuth(t *testing.T) { cfg := testCfg() eng := engine.NewEngine(cfg) a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg) handler := a.Handler() body, _ := json.Marshal(map[string]string{"mode": "best"}) req := httptest.NewRequest("PUT", "/api/route/mode", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") rr := httptest.NewRecorder() handler.ServeHTTP(rr, req) if rr.Code == 404 { t.Fatalf("PUT /api/route/mode вернул 404 — маршрут не найден! body=%s", rr.Body.String()) } if rr.Code == 401 { t.Log("OK: без токена получаем 401 (роутинг работает)") } else { t.Logf("Код ответа: %d, body: %s", rr.Code, rr.Body.String()) } } func TestRouteModeEndpoint_ViewerForbidden(t *testing.T) { cfg := testCfg() eng := engine.NewEngine(cfg) a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg) handler := a.Handler() token := createTestJWT("test-secret", "pbx-viewer", "viewer") body, _ := json.Marshal(map[string]string{"mode": "weighted_random"}) req := httptest.NewRequest("PUT", "/api/route/mode", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) rr := httptest.NewRecorder() handler.ServeHTTP(rr, req) if rr.Code == 404 { t.Fatalf("PUT /api/route/mode вернул 404 (viewer) — маршрут не найден!") } if rr.Code != 403 { t.Errorf("viewer должен получить 403, получен %d", rr.Code) } t.Logf("viewer: код=%d, body=%s", rr.Code, rr.Body.String()) } func TestRouteModeEndpoint_AdminSuccess(t *testing.T) { cfg := testCfg() eng := engine.NewEngine(cfg) a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg) handler := a.Handler() token := createTestJWT("test-secret", "admin", "admin") body, _ := json.Marshal(map[string]string{"mode": "best"}) req := httptest.NewRequest("PUT", "/api/route/mode", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) rr := httptest.NewRecorder() handler.ServeHTTP(rr, req) if rr.Code == 404 { t.Fatalf("PUT /api/route/mode вернул 404 (admin) — маршрут не найден!") } if rr.Code != 200 { t.Fatalf("admin должен получить 200, получен %d body=%s", rr.Code, rr.Body.String()) } var resp map[string]string json.Unmarshal(rr.Body.Bytes(), &resp) if resp["mode"] != "best" { t.Errorf("ожидался mode=best, получен %s", resp["mode"]) } if eng.GetBalancingMode() != "best" { t.Errorf("engine mode должен быть 'best', получен %s", eng.GetBalancingMode()) } t.Logf("admin: код=%d, body=%s, engine_mode=%s", rr.Code, rr.Body.String(), eng.GetBalancingMode()) } func TestRouteModeEndpoint_InvalidMode(t *testing.T) { cfg := testCfg() eng := engine.NewEngine(cfg) a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg) handler := a.Handler() token := createTestJWT("test-secret", "admin", "admin") body, _ := json.Marshal(map[string]string{"mode": "invalid"}) req := httptest.NewRequest("PUT", "/api/route/mode", bytes.NewReader(body)) req.Header.Set("Content-Type", "application/json") req.Header.Set("Authorization", "Bearer "+token) rr := httptest.NewRecorder() handler.ServeHTTP(rr, req) if rr.Code != 400 { t.Errorf("невалидный mode должен дать 400, получен %d", rr.Code) } } func TestRouteEndpoint_UsesPickNode(t *testing.T) { cfg := testCfg() eng := engine.NewEngine(cfg) a := NewAPI(eng, nil, "test-secret", "test-api-key", func() bool { return true }, cfg) a.balanceGateways["test-node"] = "sip:test:5060" a.fallbackGateway = "sip:fallback:5060" handler := a.Handler() req := httptest.NewRequest("GET", "/api/route", nil) rr := httptest.NewRecorder() handler.ServeHTTP(rr, req) if rr.Code != 503 { t.Errorf("GET /api/route без нод должен дать 503, получен %d", rr.Code) } }