feat: ServerMux to gorilla/mux, time route
This commit is contained in:
43
internal/apiserver/handlers/time.go
Normal file
43
internal/apiserver/handlers/time.go
Normal file
@@ -0,0 +1,43 @@
|
||||
// It's just test file. I'll remove it later
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"git.nwaifu.su/sergey/MyGoServer/internal/apiserver/models"
|
||||
)
|
||||
|
||||
// TimeHandler handles the time endpoint
|
||||
type TimeHandler struct{}
|
||||
|
||||
func NewTimeHandler() *TimeHandler {
|
||||
return &TimeHandler{}
|
||||
}
|
||||
|
||||
// ServeHTTP implements http.Handler
|
||||
func (h *TimeHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
h.handleTime(w, r)
|
||||
}
|
||||
|
||||
// @Summary Get server time
|
||||
// @Description Возвращает текущее серверное время в формате ISO 8601
|
||||
// @Tags Time
|
||||
// @Success 200 {object} models.Response "Текущее время сервера"
|
||||
// @Router /time [get]
|
||||
func (h *TimeHandler) handleTime(w http.ResponseWriter, r *http.Request) {
|
||||
// Get current time in UTC
|
||||
currentTime := time.Now().UTC()
|
||||
|
||||
// Create response with time data
|
||||
response := models.NewSuccessResponse(map[string]interface{}{
|
||||
"server_time": currentTime.Format(time.RFC3339),
|
||||
"unix_timestamp": currentTime.Unix(),
|
||||
"timezone": "UTC",
|
||||
})
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusOK)
|
||||
json.NewEncoder(w).Encode(response)
|
||||
}
|
||||
149
internal/apiserver/handlers/time_test.go
Normal file
149
internal/apiserver/handlers/time_test.go
Normal file
@@ -0,0 +1,149 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.nwaifu.su/sergey/MyGoServer/internal/apiserver/models"
|
||||
)
|
||||
|
||||
func TestTimeHandler_ServeHTTP(t *testing.T) {
|
||||
// Create a new time handler
|
||||
handler := NewTimeHandler()
|
||||
|
||||
// Create a test request
|
||||
req := httptest.NewRequest("GET", "/time", nil)
|
||||
|
||||
// Create a test response recorder
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
// Call the handler
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// Check status code
|
||||
if status := rr.Code; status != http.StatusOK {
|
||||
t.Errorf("handler returned wrong status code: got %v want %v",
|
||||
status, http.StatusOK)
|
||||
}
|
||||
|
||||
// Check content type
|
||||
if ct := rr.Header().Get("Content-Type"); ct != "application/json" {
|
||||
t.Errorf("handler returned wrong content type: got %v want %v",
|
||||
ct, "application/json")
|
||||
}
|
||||
|
||||
// Check response body contains expected structure
|
||||
var response models.Response
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
||||
t.Errorf("handler returned invalid JSON: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// Check success field
|
||||
if !response.Success {
|
||||
t.Errorf("handler returned success=false, want success=true")
|
||||
}
|
||||
|
||||
// Check that data exists and is a map
|
||||
if response.Data == nil {
|
||||
t.Errorf("handler returned nil data")
|
||||
return
|
||||
}
|
||||
|
||||
// Type assert to map[string]interface{}
|
||||
dataMap, ok := response.Data.(map[string]interface{})
|
||||
if !ok {
|
||||
t.Errorf("handler returned data of unexpected type")
|
||||
return
|
||||
}
|
||||
|
||||
// Check required fields exist
|
||||
requiredFields := []string{"server_time", "unix_timestamp", "timezone"}
|
||||
for _, field := range requiredFields {
|
||||
if _, exists := dataMap[field]; !exists {
|
||||
t.Errorf("handler response missing required field: %s", field)
|
||||
}
|
||||
}
|
||||
|
||||
// Check timezone is UTC
|
||||
if timezone, ok := dataMap["timezone"].(string); ok {
|
||||
if timezone != "UTC" {
|
||||
t.Errorf("handler returned wrong timezone: got %v want %v",
|
||||
timezone, "UTC")
|
||||
}
|
||||
} else {
|
||||
t.Errorf("handler returned timezone of unexpected type")
|
||||
}
|
||||
|
||||
// Check server_time format (should be RFC3339)
|
||||
if serverTime, ok := dataMap["server_time"].(string); ok {
|
||||
if _, err := time.Parse(time.RFC3339, serverTime); err != nil {
|
||||
t.Errorf("handler returned invalid server_time format: got %v, error: %v",
|
||||
serverTime, err)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("handler returned server_time of unexpected type")
|
||||
}
|
||||
|
||||
// Check unix_timestamp is a number
|
||||
if unixTimestamp, ok := dataMap["unix_timestamp"].(float64); ok {
|
||||
// Verify it's a reasonable timestamp (should be close to current time)
|
||||
now := time.Now().UTC().Unix()
|
||||
// Allow 10 seconds difference for test execution time
|
||||
if diff := now - int64(unixTimestamp); diff > 10 || diff < -10 {
|
||||
t.Errorf("handler returned unreasonable unix_timestamp: got %v, current time: %v",
|
||||
int64(unixTimestamp), now)
|
||||
}
|
||||
} else {
|
||||
// Try as string and convert
|
||||
if unixTimestampStr, ok := dataMap["unix_timestamp"].(string); ok {
|
||||
if _, err := strconv.ParseInt(unixTimestampStr, 10, 64); err != nil {
|
||||
t.Errorf("handler returned unix_timestamp of unexpected format: %v", err)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("handler returned unix_timestamp of unexpected type")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTimeHandler_ResponseStructure(t *testing.T) {
|
||||
handler := NewTimeHandler()
|
||||
req := httptest.NewRequest("GET", "/time", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
// Test that response is valid JSON and contains all expected top-level fields
|
||||
var response models.Response
|
||||
if err := json.Unmarshal(rr.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("Response is not valid JSON: %v", err)
|
||||
}
|
||||
|
||||
// Test response structure
|
||||
if response.Success != true {
|
||||
t.Errorf("Expected success=true, got success=%v", response.Success)
|
||||
}
|
||||
|
||||
if response.Error != "" {
|
||||
t.Errorf("Expected no error, got error=%v", response.Error)
|
||||
}
|
||||
|
||||
if response.Data == nil {
|
||||
t.Errorf("Expected data to be present, got nil")
|
||||
}
|
||||
|
||||
// Test that response body contains JSON structure
|
||||
body := rr.Body.String()
|
||||
if !strings.Contains(body, `"success":true`) {
|
||||
t.Errorf("Response body missing success field: %s", body)
|
||||
}
|
||||
|
||||
if !strings.Contains(body, `"data"`) {
|
||||
t.Errorf("Response body missing data field: %s", body)
|
||||
}
|
||||
}
|
||||
@@ -10,13 +10,14 @@ import (
|
||||
|
||||
// setupRoutes configures all routes
|
||||
func (s *Server) setupRoutes() {
|
||||
// Add request ID middleware to all routes
|
||||
s.router.Handle("/", middleware.RequestIDMiddleware(
|
||||
middleware.LoggingMiddleware(
|
||||
handlers.NewHomeHandler(),
|
||||
),
|
||||
))
|
||||
// Apply global middleware to all routes
|
||||
s.router.Use(middleware.RequestIDMiddleware)
|
||||
s.router.Use(middleware.LoggingMiddleware)
|
||||
|
||||
// Swagger UI
|
||||
s.router.Handle("/swagger/", httpSwagger.WrapHandler)
|
||||
// Register routes
|
||||
s.router.Handle("/", handlers.NewHomeHandler()).Methods("GET")
|
||||
s.router.Handle("/time", handlers.NewTimeHandler()).Methods("GET")
|
||||
|
||||
// Swagger UI (no middleware needed)
|
||||
s.router.PathPrefix("/swagger/").Handler(httpSwagger.WrapHandler)
|
||||
}
|
||||
|
||||
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"git.nwaifu.su/sergey/MyGoServer/cmd/apiserver/config"
|
||||
"git.nwaifu.su/sergey/MyGoServer/internal/apiserver/logger"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
type contextKey struct {
|
||||
@@ -22,7 +23,7 @@ func saveConnInContext(ctx context.Context, c net.Conn) context.Context {
|
||||
// Server represents the HTTP server
|
||||
type Server struct {
|
||||
config *config.Config
|
||||
router *http.ServeMux
|
||||
router *mux.Router
|
||||
server *http.Server
|
||||
}
|
||||
|
||||
@@ -36,7 +37,7 @@ func NewServer(cfg *config.Config) *Server {
|
||||
logger.Initialize(cfg.Logging.Level, cfg.Logging.Format, cfg.Logging.Output)
|
||||
|
||||
// Create router
|
||||
s.router = http.NewServeMux()
|
||||
s.router = mux.NewRouter()
|
||||
s.setupRoutes()
|
||||
|
||||
// Create HTTP server
|
||||
@@ -55,7 +56,7 @@ func (s *Server) Start() error {
|
||||
}
|
||||
|
||||
// GetRouter returns the HTTP router
|
||||
func (s *Server) GetRouter() *http.ServeMux {
|
||||
func (s *Server) GetRouter() *mux.Router {
|
||||
return s.router
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user