package server_test import ( "net/http" "testing" "gitea.meghsakha.com/platform/tenant-registry/internal/keycloak" ) type membershipsBody struct { UserID string `json:"user_id"` Memberships []struct { keycloak.Claims Source string `json:"source"` } `json:"memberships"` } func TestGetUserMemberships(t *testing.T) { eachStore(t, func(t *testing.T, h *testHarness) { // The KC attribute projection carries a stale plan/status and a // legacy tenant_id — the registry row must win (source: registry). h.kcMock.Claims["u-1"] = keycloak.Claims{ TenantID: "kc-legacy-id", TenantSlug: "acme", OrgRoles: []string{"IT_ADMIN"}, Plan: "stale-plan", TenantStatus: "stale", } resp, raw := h.do(http.MethodGet, "/v1/users/u-1/memberships", nil) if resp.StatusCode != http.StatusOK { t.Fatalf("status %d: %s", resp.StatusCode, raw) } body := decode[membershipsBody](t, raw) if body.UserID != "u-1" || len(body.Memberships) != 1 { t.Fatalf("unexpected body: %s", raw) } m := body.Memberships[0] if m.Source != "registry" { t.Errorf("want source registry, got %q", m.Source) } if m.TenantID != h.tenant.ID || m.TenantSlug != "acme" { t.Errorf("registry identity not authoritative: %+v", m.Claims) } if m.Plan != h.tenant.Plan || m.TenantStatus != h.tenant.Status { t.Errorf("registry lifecycle not authoritative: plan=%q status=%q", m.Plan, m.TenantStatus) } if len(m.OrgRoles) != 1 || m.OrgRoles[0] != "IT_ADMIN" { t.Errorf("org_roles must stay keycloak-owned: %v", m.OrgRoles) } }) } func TestGetUserMemberships_unknownTenantFallsBackToKeycloak(t *testing.T) { eachStore(t, func(t *testing.T, h *testHarness) { h.kcMock.Claims["u-2"] = keycloak.Claims{ TenantID: "ghost-001", TenantSlug: "ghost", OrgRoles: []string{"USER"}, Plan: "Scale", TenantStatus: "active", } resp, raw := h.do(http.MethodGet, "/v1/users/u-2/memberships", nil) if resp.StatusCode != http.StatusOK { t.Fatalf("status %d: %s", resp.StatusCode, raw) } m := decode[membershipsBody](t, raw).Memberships[0] if m.Source != "keycloak" || m.TenantSlug != "ghost" || m.Plan != "Scale" { t.Errorf("expected untouched keycloak projection, got %+v (source %q)", m.Claims, m.Source) } }) } func TestGetUserMemberships_unknownUser404(t *testing.T) { eachStore(t, func(t *testing.T, h *testHarness) { resp, _ := h.do(http.MethodGet, "/v1/users/nobody/memberships", nil) if resp.StatusCode != http.StatusNotFound { t.Fatalf("want 404, got %d", resp.StatusCode) } }) }