feat(server): tenant-registry skeleton boots against dev stack
Minimal Go service so platform/portal has something to resolve in local
dev. Stdlib net/http with Go 1.22 enhanced ServeMux (method+path
patterns); no third-party deps yet.
Layout:
cmd/server/main.go entry point with graceful shutdown
internal/config/ env-driven config (APP_ENV, ADDR, KC issuer)
internal/server/ http handlers + request-logging middleware
internal/store/memory.go in-memory tenant store, seeded with acme
migrations/0001_init.up.sql schema for the M4.1 follow-up (unapplied)
Makefile dev/test/build/lint/docker targets
Dockerfile multi-stage distroless build
Endpoints (under :8080 in dev):
GET /healthz
GET /v1/tenants/by-slug/{slug} 200 acme | 404
GET /v1/tenants/{id} 200 by uuid | 404
JWT validation and the real Postgres-backed store land in the M4.1
follow-up PR — keeping this PR strictly to 'boots, replies, tests pass'.
Refs: M4.1 (skeleton)
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"gitea.meghsakha.com/platform/tenant-registry/internal/config"
|
||||
)
|
||||
|
||||
func newTestServer(t *testing.T) *httptest.Server {
|
||||
t.Helper()
|
||||
cfg := &config.Config{Env: "dev", Addr: ":0"}
|
||||
h := NewRouter(cfg, slog.New(slog.NewTextHandler(os.Stderr, nil)))
|
||||
return httptest.NewServer(h)
|
||||
}
|
||||
|
||||
func TestHealthz(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
resp, err := http.Get(srv.URL + "/healthz")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
t.Fatalf("got %d, want 200", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestTenantBySlug_acme(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
resp, err := http.Get(srv.URL + "/v1/tenants/by-slug/acme")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
t.Fatalf("got %d, want 200; body=%s", resp.StatusCode, body)
|
||||
}
|
||||
var payload map[string]any
|
||||
if err := json.NewDecoder(resp.Body).Decode(&payload); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if payload["slug"] != "acme" {
|
||||
t.Fatalf("expected slug=acme, got %v", payload["slug"])
|
||||
}
|
||||
if payload["status"] != "active" {
|
||||
t.Fatalf("expected status=active, got %v", payload["status"])
|
||||
}
|
||||
}
|
||||
|
||||
func TestTenantBySlug_unknown(t *testing.T) {
|
||||
srv := newTestServer(t)
|
||||
defer srv.Close()
|
||||
|
||||
resp, err := http.Get(srv.URL + "/v1/tenants/by-slug/nope")
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode != http.StatusNotFound {
|
||||
t.Fatalf("got %d, want 404", resp.StatusCode)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user