Refactor backend code to remove duplicate functions and factorize service startup
parent
7505a4256e
commit
c03ae3f0f0
@ -0,0 +1,46 @@
|
||||
// Package envutil provides environment variable parsing helpers with fallback values.
|
||||
package envutil
|
||||
|
||||
import (
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
// String returns the environment variable value or fallback when empty.
|
||||
func String(key string, fallback string) string {
|
||||
if v := os.Getenv(key); v != "" {
|
||||
return v
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
// Int returns the parsed integer environment variable or fallback on parse error.
|
||||
func Int(key string, fallback int) int {
|
||||
v := os.Getenv(key)
|
||||
if v == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
i, err := strconv.Atoi(v)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return i
|
||||
}
|
||||
|
||||
// Duration returns the parsed duration environment variable or fallback on parse error.
|
||||
func Duration(key string, fallback time.Duration) time.Duration {
|
||||
v := os.Getenv(key)
|
||||
if v == "" {
|
||||
return fallback
|
||||
}
|
||||
|
||||
d, err := time.ParseDuration(v)
|
||||
if err != nil {
|
||||
return fallback
|
||||
}
|
||||
|
||||
return d
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package envutil
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestString(t *testing.T) {
|
||||
t.Setenv("APP_VALUE", "configured")
|
||||
require.Equal(t, "configured", String("APP_VALUE", "fallback"))
|
||||
}
|
||||
|
||||
func TestStringFallback(t *testing.T) {
|
||||
t.Setenv("APP_VALUE", "")
|
||||
require.Equal(t, "fallback", String("APP_VALUE", "fallback"))
|
||||
}
|
||||
|
||||
func TestInt(t *testing.T) {
|
||||
t.Setenv("APP_PORT", "8080")
|
||||
require.Equal(t, 8080, Int("APP_PORT", 3000))
|
||||
}
|
||||
|
||||
func TestIntFallbackInvalid(t *testing.T) {
|
||||
t.Setenv("APP_PORT", "oops")
|
||||
require.Equal(t, 3000, Int("APP_PORT", 3000))
|
||||
}
|
||||
|
||||
func TestDuration(t *testing.T) {
|
||||
t.Setenv("APP_TIMEOUT", "7s")
|
||||
require.Equal(t, 7*time.Second, Duration("APP_TIMEOUT", time.Second))
|
||||
}
|
||||
|
||||
func TestDurationFallbackInvalid(t *testing.T) {
|
||||
t.Setenv("APP_TIMEOUT", "oops")
|
||||
require.Equal(t, 2*time.Second, Duration("APP_TIMEOUT", 2*time.Second))
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
// Package serviceboot provides shared service bootstrap helpers.
|
||||
package serviceboot
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gofiber/fiber/v3"
|
||||
|
||||
"knowfoolery/backend/shared/infra/utils/envutil"
|
||||
)
|
||||
|
||||
// Config defines basic service runtime settings.
|
||||
type Config struct {
|
||||
AppName string
|
||||
ServiceSlug string
|
||||
PortEnv string
|
||||
DefaultPort int
|
||||
}
|
||||
|
||||
// NewFiberApp creates a Fiber app with shared defaults.
|
||||
func NewFiberApp(cfg Config) *fiber.App {
|
||||
return fiber.New(fiber.Config{
|
||||
AppName: cfg.AppName,
|
||||
})
|
||||
}
|
||||
|
||||
// RegisterHealth registers a standard health endpoint.
|
||||
func RegisterHealth(app *fiber.App, serviceSlug string) {
|
||||
app.Get("/health", func(c fiber.Ctx) error {
|
||||
return c.JSON(fiber.Map{
|
||||
"status": "healthy",
|
||||
"service": serviceSlug,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
// ListenAddress resolves the listen port from environment with fallback.
|
||||
func ListenAddress(portEnv string, defaultPort int) string {
|
||||
port := envutil.Int(portEnv, defaultPort)
|
||||
if port < 1 || port > 65535 {
|
||||
port = defaultPort
|
||||
}
|
||||
if port < 1 || port > 65535 {
|
||||
port = 8080
|
||||
}
|
||||
return fmt.Sprintf(":%d", port)
|
||||
}
|
||||
|
||||
// Run starts the Fiber app.
|
||||
func Run(app *fiber.App, addr string) error {
|
||||
return app.Listen(addr)
|
||||
}
|
||||
@ -0,0 +1,40 @@
|
||||
package serviceboot
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestRegisterHealth(t *testing.T) {
|
||||
app := NewFiberApp(Config{AppName: "test-service"})
|
||||
RegisterHealth(app, "svc")
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/health", nil)
|
||||
resp, err := app.Test(req)
|
||||
require.NoError(t, err)
|
||||
defer resp.Body.Close()
|
||||
|
||||
var body map[string]string
|
||||
require.NoError(t, json.NewDecoder(resp.Body).Decode(&body))
|
||||
require.Equal(t, "healthy", body["status"])
|
||||
require.Equal(t, "svc", body["service"])
|
||||
}
|
||||
|
||||
func TestListenAddressFromEnv(t *testing.T) {
|
||||
t.Setenv("SERVICE_PORT", "9090")
|
||||
require.Equal(t, ":9090", ListenAddress("SERVICE_PORT", 8080))
|
||||
}
|
||||
|
||||
func TestListenAddressFallback(t *testing.T) {
|
||||
t.Setenv("SERVICE_PORT", "bad")
|
||||
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))
|
||||
}
|
||||
|
||||
func TestListenAddressOutOfRangeFallback(t *testing.T) {
|
||||
t.Setenv("SERVICE_PORT", "70000")
|
||||
require.Equal(t, ":8080", ListenAddress("SERVICE_PORT", 8080))
|
||||
}
|
||||
Loading…
Reference in New Issue