mirror of
https://github.com/hhftechnology/middleware-manager.git
synced 2026-07-11 17:08:24 +00:00
Add extensive unit tests across the codebase (api/errors, cache, many models tests, util/id_normalizer and others) to improve coverage and validate behavior. Replace ServiceWatcher.isRunning bool with atomic.Bool for safer concurrent access, update Start/Stop logic and related imports, and adjust service_watcher_test accordingly. Also update traefik_fetcher_test to use atomic counters for request counting and add minor import changes (sync/atomic).
184 lines
4.6 KiB
Go
184 lines
4.6 KiB
Go
package errors
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"github.com/gin-gonic/gin"
|
|
)
|
|
|
|
func init() {
|
|
gin.SetMode(gin.TestMode)
|
|
}
|
|
|
|
func newTestContext() (*gin.Context, *httptest.ResponseRecorder) {
|
|
w := httptest.NewRecorder()
|
|
c, _ := gin.CreateTestContext(w)
|
|
return c, w
|
|
}
|
|
|
|
func parseResponse(t *testing.T, w *httptest.ResponseRecorder) APIError {
|
|
t.Helper()
|
|
var resp APIError
|
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
|
t.Fatalf("failed to parse response: %v", err)
|
|
}
|
|
return resp
|
|
}
|
|
|
|
func TestHandleAPIError_WithError(t *testing.T) {
|
|
c, w := newTestContext()
|
|
HandleAPIError(c, http.StatusBadRequest, "bad input", errors.New("field missing"))
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusBadRequest)
|
|
}
|
|
|
|
resp := parseResponse(t, w)
|
|
if resp.Code != http.StatusBadRequest {
|
|
t.Errorf("resp.Code = %d, want %d", resp.Code, http.StatusBadRequest)
|
|
}
|
|
if resp.Message != "bad input" {
|
|
t.Errorf("resp.Message = %q, want %q", resp.Message, "bad input")
|
|
}
|
|
if resp.Details != "field missing" {
|
|
t.Errorf("resp.Details = %q, want %q", resp.Details, "field missing")
|
|
}
|
|
}
|
|
|
|
func TestHandleAPIError_WithoutError(t *testing.T) {
|
|
c, w := newTestContext()
|
|
HandleAPIError(c, http.StatusNotFound, "not found", nil)
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusNotFound)
|
|
}
|
|
|
|
resp := parseResponse(t, w)
|
|
if resp.Details != "" {
|
|
t.Errorf("resp.Details = %q, want empty", resp.Details)
|
|
}
|
|
}
|
|
|
|
func TestNotFound(t *testing.T) {
|
|
t.Run("with ID", func(t *testing.T) {
|
|
c, w := newTestContext()
|
|
NotFound(c, "Middleware", "abc123")
|
|
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusNotFound)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if resp.Message != "Middleware not found: abc123" {
|
|
t.Errorf("message = %q", resp.Message)
|
|
}
|
|
})
|
|
|
|
t.Run("without ID", func(t *testing.T) {
|
|
c, w := newTestContext()
|
|
NotFound(c, "Service", "")
|
|
|
|
resp := parseResponse(t, w)
|
|
if resp.Message != "Service not found" {
|
|
t.Errorf("message = %q", resp.Message)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestBadRequest(t *testing.T) {
|
|
c, w := newTestContext()
|
|
BadRequest(c, "invalid input", errors.New("parse error"))
|
|
|
|
if w.Code != http.StatusBadRequest {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusBadRequest)
|
|
}
|
|
}
|
|
|
|
func TestServerError(t *testing.T) {
|
|
c, w := newTestContext()
|
|
ServerError(c, "internal failure", errors.New("db down"))
|
|
|
|
if w.Code != http.StatusInternalServerError {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusInternalServerError)
|
|
}
|
|
}
|
|
|
|
func TestUnauthorized(t *testing.T) {
|
|
t.Run("default message", func(t *testing.T) {
|
|
c, w := newTestContext()
|
|
Unauthorized(c, "")
|
|
|
|
if w.Code != http.StatusUnauthorized {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusUnauthorized)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if resp.Message != "Unauthorized access" {
|
|
t.Errorf("message = %q, want %q", resp.Message, "Unauthorized access")
|
|
}
|
|
})
|
|
|
|
t.Run("custom message", func(t *testing.T) {
|
|
c, w := newTestContext()
|
|
Unauthorized(c, "token expired")
|
|
|
|
resp := parseResponse(t, w)
|
|
if resp.Message != "token expired" {
|
|
t.Errorf("message = %q, want %q", resp.Message, "token expired")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestForbidden(t *testing.T) {
|
|
t.Run("default message", func(t *testing.T) {
|
|
c, w := newTestContext()
|
|
Forbidden(c, "")
|
|
|
|
if w.Code != http.StatusForbidden {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusForbidden)
|
|
}
|
|
resp := parseResponse(t, w)
|
|
if resp.Message != "Access forbidden" {
|
|
t.Errorf("message = %q, want %q", resp.Message, "Access forbidden")
|
|
}
|
|
})
|
|
|
|
t.Run("custom message", func(t *testing.T) {
|
|
c, w := newTestContext()
|
|
Forbidden(c, "admin only")
|
|
|
|
resp := parseResponse(t, w)
|
|
if resp.Message != "admin only" {
|
|
t.Errorf("message = %q, want %q", resp.Message, "admin only")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestConflict(t *testing.T) {
|
|
c, w := newTestContext()
|
|
Conflict(c, "duplicate name", errors.New("unique constraint"))
|
|
|
|
if w.Code != http.StatusConflict {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusConflict)
|
|
}
|
|
}
|
|
|
|
func TestUnprocessableEntity(t *testing.T) {
|
|
c, w := newTestContext()
|
|
UnprocessableEntity(c, "validation failed", errors.New("invalid config"))
|
|
|
|
if w.Code != http.StatusUnprocessableEntity {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusUnprocessableEntity)
|
|
}
|
|
}
|
|
|
|
func TestServiceUnavailable(t *testing.T) {
|
|
c, w := newTestContext()
|
|
ServiceUnavailable(c, "traefik unreachable", errors.New("connection refused"))
|
|
|
|
if w.Code != http.StatusServiceUnavailable {
|
|
t.Errorf("status = %d, want %d", w.Code, http.StatusServiceUnavailable)
|
|
}
|
|
}
|