Nik Afiq 657b6aeb22 feat: implement initial application structure with health and hello endpoints
- Add bootstrap package to initialize application components including logger, tracer, and HTTP server.
- Create config package to load runtime settings from environment variables.
- Implement observability features including logging, metrics, and tracing.
- Add health check and hello use cases with corresponding HTTP handlers.
- Introduce middleware for request ID, access logging, metrics, and recovery.
- Set up HTTP router with defined routes for health and hello endpoints.
- Include tests for health and hello endpoints to ensure proper functionality.
- Add OpenTelemetry collector configuration for trace exporting.
2026-03-05 21:22:43 +09:00

80 lines
2.1 KiB
Go

package httptransport_test
import (
"encoding/json"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"go.opentelemetry.io/otel/trace/noop"
"switchbot-api/internal/app/observability"
"switchbot-api/internal/domain/health"
"switchbot-api/internal/domain/hello"
httptransport "switchbot-api/internal/transport/http"
"switchbot-api/internal/transport/http/handlers"
)
func TestHealthz(t *testing.T) {
router := newTestRouter()
req := httptest.NewRequest(http.MethodGet, "/healthz", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var body map[string]string
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
t.Fatalf("failed to unmarshal response: %v", err)
}
if body["status"] != "ok" {
t.Fatalf("expected status=ok, got %q", body["status"])
}
}
func TestHello(t *testing.T) {
router := newTestRouter()
req := httptest.NewRequest(http.MethodGet, "/hello", nil)
rec := httptest.NewRecorder()
router.ServeHTTP(rec, req)
if rec.Code != http.StatusOK {
t.Fatalf("expected status %d, got %d", http.StatusOK, rec.Code)
}
var body map[string]string
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
t.Fatalf("failed to unmarshal response: %v", err)
}
if body["message"] != "hello world" {
t.Fatalf("expected message=hello world, got %q", body["message"])
}
}
func newTestRouter() *gin.Engine {
gin.SetMode(gin.TestMode)
logger := slog.New(slog.NewJSONHandler(io.Discard, nil))
registry := observability.NewRegistry()
httpMetrics := observability.NewHTTPMetrics(registry)
healthHandler := handlers.NewHealthHandler(health.NewReadinessUsecase(nil))
helloHandler := handlers.NewHelloHandler(hello.NewService())
return httptransport.NewRouter(httptransport.Dependencies{
Logger: logger,
Tracer: noop.NewTracerProvider().Tracer("test"),
HTTPMetrics: httpMetrics,
PrometheusHandler: observability.NewPrometheusHandler(registry),
HealthHandler: healthHandler,
HelloHandler: helloHandler,
})
}