feat: bootstrap reusable GoOmni framework
This commit is contained in:
132
app_test.go
Normal file
132
app_test.go
Normal file
@@ -0,0 +1,132 @@
|
||||
package omni
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func TestAppReturnsStandardResponse(t *testing.T) {
|
||||
app := New(WithMode(gin.TestMode))
|
||||
app.GET("/health", func(ctx *Context) error {
|
||||
return ctx.OK(map[string]string{"status": "ok"})
|
||||
})
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(http.MethodGet, "/health", nil)
|
||||
app.Engine().ServeHTTP(recorder, request)
|
||||
|
||||
if recorder.Code != http.StatusOK {
|
||||
t.Fatalf("expected status 200, got %d", recorder.Code)
|
||||
}
|
||||
var response Response
|
||||
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
if response.Code != "OK" || response.Message != "success" {
|
||||
t.Fatalf("unexpected response: %+v", response)
|
||||
}
|
||||
if recorder.Header().Get("X-Request-ID") == "" || response.RequestID == "" {
|
||||
t.Fatal("expected request ID in header and response")
|
||||
}
|
||||
if !strings.Contains(recorder.Body.String(), `"requestId"`) || strings.Contains(recorder.Body.String(), `"request_id"`) {
|
||||
t.Fatalf("expected lowerCamelCase response fields: %s", recorder.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppReturnsAPIError(t *testing.T) {
|
||||
app := New(WithMode(gin.TestMode))
|
||||
app.GET("/users/:id", func(ctx *Context) error {
|
||||
return NotFound("USER_NOT_FOUND", "user was not found")
|
||||
})
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(http.MethodGet, "/users/1", nil)
|
||||
app.Engine().ServeHTTP(recorder, request)
|
||||
|
||||
if recorder.Code != http.StatusNotFound {
|
||||
t.Fatalf("expected status 404, got %d", recorder.Code)
|
||||
}
|
||||
var response Response
|
||||
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
if response.Code != "USER_NOT_FOUND" || response.Message != "user was not found" {
|
||||
t.Fatalf("unexpected response: %+v", response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppRecoversFromPanic(t *testing.T) {
|
||||
app := New(WithMode(gin.TestMode))
|
||||
app.GET("/panic", func(ctx *Context) error {
|
||||
panic("boom")
|
||||
})
|
||||
|
||||
recorder := httptest.NewRecorder()
|
||||
request := httptest.NewRequest(http.MethodGet, "/panic", nil)
|
||||
app.Engine().ServeHTTP(recorder, request)
|
||||
|
||||
if recorder.Code != http.StatusInternalServerError {
|
||||
t.Fatalf("expected status 500, got %d", recorder.Code)
|
||||
}
|
||||
var response Response
|
||||
if err := json.Unmarshal(recorder.Body.Bytes(), &response); err != nil {
|
||||
t.Fatalf("decode response: %v", err)
|
||||
}
|
||||
if response.Code != "INTERNAL_ERROR" {
|
||||
t.Fatalf("unexpected response: %+v", response)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppRunContextShutsDownGracefully(t *testing.T) {
|
||||
app := New(WithMode(gin.TestMode), WithShutdownTimeout(time.Second))
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
done := make(chan error, 1)
|
||||
go func() { done <- app.RunContext(ctx, "127.0.0.1:0") }()
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
cancel()
|
||||
select {
|
||||
case err := <-done:
|
||||
if err != nil {
|
||||
t.Fatalf("graceful shutdown returned an error: %v", err)
|
||||
}
|
||||
case <-time.After(2 * time.Second):
|
||||
t.Fatal("server did not stop after context cancellation")
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppClosesResourcesInReverseOrder(t *testing.T) {
|
||||
app := New(WithMode(gin.TestMode))
|
||||
var closed []string
|
||||
app.OnShutdown(func() error {
|
||||
closed = append(closed, "database")
|
||||
return nil
|
||||
}).OnShutdown(func() error {
|
||||
closed = append(closed, "logger")
|
||||
return nil
|
||||
})
|
||||
|
||||
if err := app.Close(); err != nil {
|
||||
t.Fatalf("close app: %v", err)
|
||||
}
|
||||
if err := app.Close(); err != nil {
|
||||
t.Fatalf("close app twice: %v", err)
|
||||
}
|
||||
if got := strings.Join(closed, ","); got != "logger,database" {
|
||||
t.Fatalf("unexpected close order: %s", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAppUsesConfiguredAddressWhenNoOverrideIsProvided(t *testing.T) {
|
||||
settings := Config{Address: "127.0.0.1:18080", RequestIDHeader: "X-Request-ID", ShutdownTimeout: time.Second}
|
||||
app := New(WithConfig(settings))
|
||||
if got := app.NewServer("").Addr; got != settings.Address {
|
||||
t.Fatalf("expected configured address %q, got %q", settings.Address, got)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user