package handler import ( "baron-sso-backend/internal/domain" "baron-sso-backend/internal/service" "encoding/json" "net/http" "net/http/httptest" "testing" "time" "github.com/gofiber/fiber/v2" "github.com/stretchr/testify/assert" ) // --- Helper --- func newLinkedRpTestApp(h *AuthHandler) *fiber.App { app := fiber.New() app.Get("/api/v1/user/rp/linked", h.ListLinkedRps) return app } // --- Tests --- func TestListLinkedRps_PriorityAndAggregation(t *testing.T) { transport := roundTripFunc(func(r *http.Request) (*http.Response, error) { switch r.URL.Host { case "kratos.test": if r.URL.Path == "/sessions/whoami" { if r.Header.Get("X-Session-Token") == "" && r.Header.Get("Cookie") == "" { return httpResponse(r, http.StatusUnauthorized, "unauthorized"), nil } return httpJSONAny(r, http.StatusOK, map[string]interface{}{ "identity": map[string]interface{}{ "id": "user-123", "traits": map[string]interface{}{ "email": "user@test.com", }, }, }), nil } case "hydra.test": if r.URL.Path == "/oauth2/auth/sessions/consent" { return httpJSONAny(r, http.StatusOK, []map[string]interface{}{ { "client": map[string]interface{}{ "client_id": "client-active", "client_name": "Active App", }, "granted_scope": []string{"openid"}, "handled_at": time.Now().Format(time.RFC3339), }, }), nil } if r.URL.Path == "/admin/clients/client-audit" { return httpJSONAny(r, http.StatusOK, map[string]interface{}{ "client_id": "client-audit", "client_name": "Audit App", }), nil } if r.URL.Path == "/admin/clients/client-consent" { return httpJSONAny(r, http.StatusOK, map[string]interface{}{ "client_id": "client-consent", "client_name": "Consent App", }), nil } } return httpResponse(r, http.StatusNotFound, "not found"), nil }) client := &http.Client{Transport: transport} origDefault := http.DefaultClient http.DefaultClient = client defer func() { http.DefaultClient = origDefault }() auditRepo := &mockAuditRepo{ logs: []domain.AuditLog{ { UserID: "user-123", EventType: "consent.granted", Timestamp: time.Now().Add(-10 * time.Hour), Details: `{"client_id":"client-audit", "scopes":["audit_scope"]}`, }, }, } consentRepo := &mockConsentRepo{ consents: []domain.ClientConsent{ { Subject: "user-123", ClientID: "client-consent", GrantedScopes: []string{"consent_scope"}, UpdatedAt: time.Now().Add(-2 * time.Hour), }, }, } h := &AuthHandler{ Hydra: &service.HydraAdminService{ AdminURL: "http://hydra.test", HTTPClient: client, }, AuditRepo: auditRepo, ConsentRepo: consentRepo, KratosAdmin: &service.KratosAdminService{}, } t.Setenv("KRATOS_PUBLIC_URL", "http://kratos.test") t.Setenv("KRATOS_ADMIN_URL", "http://kratos.test") app := newLinkedRpTestApp(h) req := httptest.NewRequest(http.MethodGet, "/api/v1/user/rp/linked", nil) req.Header.Set("Cookie", "ory_kratos_session=valid") resp, err := app.Test(req) assert.NoError(t, err) assert.Equal(t, http.StatusOK, resp.StatusCode) var res struct { Items []struct { ID string `json:"id"` Name string `json:"name"` Status string `json:"status"` Scopes []string `json:"scopes"` } `json:"items"` } json.NewDecoder(resp.Body).Decode(&res) assert.Equal(t, 3, len(res.Items)) statusMap := make(map[string]string) for _, item := range res.Items { statusMap[item.ID] = item.Status } assert.Equal(t, "active", statusMap["client-active"]) assert.Equal(t, "inactive", statusMap["client-consent"]) assert.Equal(t, "inactive", statusMap["client-audit"]) }