Files
KanzlAI-mGMT/backend/internal/auth/tenant_resolver_test.go
m 34dcbb74fe fix: resolve merge conflicts from role-based permissions + audit trail branches
Combines auth context keys (user role, IP, user-agent), tenant resolver
(GetUserRole-based access verification), middleware (deferred tenant
resolution + request info capture), and router (audit log + notifications
+ assignments).
2026-03-30 11:24:43 +02:00

154 lines
4.0 KiB
Go

package auth
import (
"context"
"net/http"
"net/http/httptest"
"testing"
"github.com/google/uuid"
)
type mockTenantLookup struct {
tenantID *uuid.UUID
role string
roleSet bool // true means role was explicitly set (even if empty)
err error
}
func (m *mockTenantLookup) FirstTenantForUser(ctx context.Context, userID uuid.UUID) (*uuid.UUID, error) {
return m.tenantID, m.err
}
func (m *mockTenantLookup) GetUserRole(ctx context.Context, userID, tenantID uuid.UUID) (string, error) {
if m.roleSet {
return m.role, m.err
}
return "associate", m.err
}
func TestTenantResolver_FromHeader(t *testing.T) {
tenantID := uuid.New()
tr := NewTenantResolver(&mockTenantLookup{role: "partner"})
var gotTenantID uuid.UUID
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id, ok := TenantFromContext(r.Context())
if !ok {
t.Fatal("tenant ID not in context")
}
gotTenantID = id
w.WriteHeader(http.StatusOK)
})
r := httptest.NewRequest("GET", "/api/cases", nil)
r.Header.Set("X-Tenant-ID", tenantID.String())
r = r.WithContext(ContextWithUserID(r.Context(), uuid.New()))
w := httptest.NewRecorder()
tr.Resolve(next).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
if gotTenantID != tenantID {
t.Errorf("expected tenant %s, got %s", tenantID, gotTenantID)
}
}
func TestTenantResolver_FromHeader_NoAccess(t *testing.T) {
tenantID := uuid.New()
tr := NewTenantResolver(&mockTenantLookup{role: "", roleSet: true})
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatal("next should not be called")
})
r := httptest.NewRequest("GET", "/api/cases", nil)
r.Header.Set("X-Tenant-ID", tenantID.String())
r = r.WithContext(ContextWithUserID(r.Context(), uuid.New()))
w := httptest.NewRecorder()
tr.Resolve(next).ServeHTTP(w, r)
if w.Code != http.StatusForbidden {
t.Errorf("expected 403, got %d", w.Code)
}
}
func TestTenantResolver_DefaultsToFirst(t *testing.T) {
tenantID := uuid.New()
tr := NewTenantResolver(&mockTenantLookup{tenantID: &tenantID, role: "owner"})
var gotTenantID uuid.UUID
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
id, _ := TenantFromContext(r.Context())
gotTenantID = id
w.WriteHeader(http.StatusOK)
})
r := httptest.NewRequest("GET", "/api/cases", nil)
r = r.WithContext(ContextWithUserID(r.Context(), uuid.New()))
w := httptest.NewRecorder()
tr.Resolve(next).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d", w.Code)
}
if gotTenantID != tenantID {
t.Errorf("expected tenant %s, got %s", tenantID, gotTenantID)
}
}
func TestTenantResolver_NoUser(t *testing.T) {
tr := NewTenantResolver(&mockTenantLookup{})
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatal("next should not be called")
})
r := httptest.NewRequest("GET", "/api/cases", nil)
w := httptest.NewRecorder()
tr.Resolve(next).ServeHTTP(w, r)
if w.Code != http.StatusUnauthorized {
t.Errorf("expected 401, got %d", w.Code)
}
}
func TestTenantResolver_InvalidHeader(t *testing.T) {
tr := NewTenantResolver(&mockTenantLookup{})
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatal("next should not be called")
})
r := httptest.NewRequest("GET", "/api/cases", nil)
r.Header.Set("X-Tenant-ID", "not-a-uuid")
r = r.WithContext(ContextWithUserID(r.Context(), uuid.New()))
w := httptest.NewRecorder()
tr.Resolve(next).ServeHTTP(w, r)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestTenantResolver_NoTenantForUser(t *testing.T) {
tr := NewTenantResolver(&mockTenantLookup{tenantID: nil})
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
t.Fatal("next should not be called")
})
r := httptest.NewRequest("GET", "/api/cases", nil)
r = r.WithContext(ContextWithUserID(r.Context(), uuid.New()))
w := httptest.NewRecorder()
tr.Resolve(next).ServeHTTP(w, r)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}