forked from baron/baron-sso
Ory Hydra Admin API 통신 로직 검증
This commit is contained in:
401
backend/internal/service/hydra_admin_service_test.go
Normal file
401
backend/internal/service/hydra_admin_service_test.go
Normal file
@@ -0,0 +1,401 @@
|
||||
/*
|
||||
이 테스트 파일은 HydraAdminService의 기능을 검증하기 위한 유닛 테스트입니다.
|
||||
Hydra Admin API와의 통신을 시뮬레이션하기 위해 httptest 패키지를 사용하여
|
||||
실제 네트워크 호출 없이 HTTP 핸들러를 모킹(Mocking)하는 방식으로 작성되었습니다.
|
||||
|
||||
주요 테스트 항목:
|
||||
1. 클라이언트 관리: List, Get, Create, Update, Patch, Delete (성공 및 NotFound 케이스)
|
||||
2. OIDC 인증/동의 흐름: GetConsentRequest, AcceptConsentRequest, RejectConsentRequest, GetLoginRequest, AcceptLoginRequest, RejectLoginRequest
|
||||
3. 세션 관리: ListConsentSessions, RevokeConsentSessions
|
||||
4. 존재하지 않는 리소스 접근 시 에러 처리
|
||||
*/
|
||||
|
||||
package service
|
||||
|
||||
import (
|
||||
"baron-sso-backend/internal/domain"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// clientForHandler는 주어진 핸들러로 요청을 보내는 http.Client를 반환합니다.
|
||||
// ory_service_test.go에 정의된 것과 동일한 로직입니다.
|
||||
func mockHydraClient(h http.Handler) *http.Client {
|
||||
return &http.Client{
|
||||
Transport: roundTripperFunc(func(req *http.Request) (*http.Response, error) {
|
||||
var bodyBytes []byte
|
||||
if req.Body != nil {
|
||||
bodyBytes, _ = io.ReadAll(req.Body)
|
||||
}
|
||||
r := httptest.NewRequest(req.Method, req.URL.String(), bytes.NewReader(bodyBytes))
|
||||
r.Header = req.Header.Clone()
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
h.ServeHTTP(w, r)
|
||||
return w.Result(), nil
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_ListClients(t *testing.T) {
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodGet || !strings.Contains(r.URL.Path, "/clients") {
|
||||
t.Errorf("unexpected request: %s %s", r.Method, r.URL.String())
|
||||
return
|
||||
}
|
||||
|
||||
clients := []domain.HydraClient{
|
||||
{ClientID: "client-1", ClientName: "Client One"},
|
||||
{ClientID: "client-2", ClientName: "Client Two"},
|
||||
}
|
||||
json.NewEncoder(w).Encode(clients)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
clients, err := svc.ListClients(context.Background(), 10, 0)
|
||||
if err != nil {
|
||||
t.Fatalf("ListClients failed: %v", err)
|
||||
}
|
||||
|
||||
if len(clients) != 2 {
|
||||
t.Errorf("expected 2 clients, got %d", len(clients))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_GetClient_Success(t *testing.T) {
|
||||
targetID := "test-client"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
client := domain.HydraClient{
|
||||
ClientID: targetID,
|
||||
ClientName: "Test Client",
|
||||
}
|
||||
json.NewEncoder(w).Encode(client)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
client, err := svc.GetClient(context.Background(), targetID)
|
||||
if err != nil {
|
||||
t.Fatalf("GetClient failed: %v", err)
|
||||
}
|
||||
|
||||
if client.ClientID != targetID {
|
||||
t.Errorf("expected %s, got %s", targetID, client.ClientID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_GetClient_NotFound(t *testing.T) {
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusNotFound)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
_, err := svc.GetClient(context.Background(), "non-existent")
|
||||
if err != ErrHydraNotFound {
|
||||
t.Errorf("expected ErrHydraNotFound, got %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_PatchClientStatus(t *testing.T) {
|
||||
targetID := "test-client"
|
||||
status := "inactive"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPatch {
|
||||
t.Errorf("expected PATCH, got %s", r.Method)
|
||||
}
|
||||
var patch []map[string]interface{}
|
||||
json.NewDecoder(r.Body).Decode(&patch)
|
||||
if patch[0]["value"] != status {
|
||||
t.Errorf("expected status %s, got %v", status, patch[0]["value"])
|
||||
}
|
||||
|
||||
client := domain.HydraClient{ClientID: targetID}
|
||||
json.NewEncoder(w).Encode(client)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
_, err := svc.PatchClientStatus(context.Background(), targetID, status)
|
||||
if err != nil {
|
||||
t.Fatalf("PatchClientStatus failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_CreateClient(t *testing.T) {
|
||||
newClient := domain.HydraClient{ClientID: "new-client"}
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(newClient)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
created, err := svc.CreateClient(context.Background(), newClient)
|
||||
if err != nil {
|
||||
t.Fatalf("CreateClient failed: %v", err)
|
||||
}
|
||||
if created.ClientID != newClient.ClientID {
|
||||
t.Errorf("expected %s, got %s", newClient.ClientID, created.ClientID)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_UpdateClient(t *testing.T) {
|
||||
targetID := "test-client"
|
||||
updatedClient := domain.HydraClient{ClientID: targetID, ClientName: "Updated"}
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
t.Errorf("expected PUT, got %s", r.Method)
|
||||
}
|
||||
json.NewEncoder(w).Encode(updatedClient)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
res, err := svc.UpdateClient(context.Background(), targetID, updatedClient)
|
||||
if err != nil {
|
||||
t.Fatalf("UpdateClient failed: %v", err)
|
||||
}
|
||||
if res.ClientName != "Updated" {
|
||||
t.Errorf("expected Updated, got %s", res.ClientName)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_DeleteClient(t *testing.T) {
|
||||
targetID := "test-client"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
t.Errorf("expected DELETE, got %s", r.Method)
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
err := svc.DeleteClient(context.Background(), targetID)
|
||||
if err != nil {
|
||||
t.Fatalf("DeleteClient failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_ListConsentSessions(t *testing.T) {
|
||||
subject := "user-123"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("subject") != subject {
|
||||
t.Errorf("expected subject %s, got %s", subject, r.URL.Query().Get("subject"))
|
||||
}
|
||||
sessions := []domain.HydraConsentSession{{Subject: subject}}
|
||||
json.NewEncoder(w).Encode(sessions)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
res, err := svc.ListConsentSessions(context.Background(), subject, "")
|
||||
if err != nil {
|
||||
t.Fatalf("ListConsentSessions failed: %v", err)
|
||||
}
|
||||
if len(res) != 1 {
|
||||
t.Errorf("expected 1 session, got %d", len(res))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_RevokeConsentSessions(t *testing.T) {
|
||||
subject := "user-123"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodDelete {
|
||||
t.Errorf("expected DELETE, got %s", r.Method)
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
err := svc.RevokeConsentSessions(context.Background(), subject, "client-1")
|
||||
if err != nil {
|
||||
t.Fatalf("RevokeConsentSessions failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_GetConsentRequest(t *testing.T) {
|
||||
challenge := "consent-challenge"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.URL.Query().Get("consent_challenge") != challenge {
|
||||
t.Errorf("expected challenge %s, got %s", challenge, r.URL.Query().Get("consent_challenge"))
|
||||
}
|
||||
resp := domain.HydraConsentRequest{Challenge: challenge}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
req, err := svc.GetConsentRequest(context.Background(), challenge)
|
||||
if err != nil {
|
||||
t.Fatalf("GetConsentRequest failed: %v", err)
|
||||
}
|
||||
if req.Challenge != challenge {
|
||||
t.Errorf("expected %s, got %s", challenge, req.Challenge)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_RejectConsentRequest(t *testing.T) {
|
||||
challenge := "consent-challenge"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
t.Errorf("expected PUT, got %s", r.Method)
|
||||
}
|
||||
resp := struct {
|
||||
RedirectTo string `json:"redirect_to"`
|
||||
}{
|
||||
RedirectTo: "http://redirect",
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
resp, err := svc.RejectConsentRequest(context.Background(), challenge)
|
||||
if err != nil {
|
||||
t.Fatalf("RejectConsentRequest failed: %v", err)
|
||||
}
|
||||
if resp.RedirectTo != "http://redirect" {
|
||||
t.Errorf("expected http://redirect, got %s", resp.RedirectTo)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_RejectLoginRequest(t *testing.T) {
|
||||
challenge := "login-challenge"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := struct {
|
||||
RedirectTo string `json:"redirect_to"`
|
||||
}{
|
||||
RedirectTo: "http://redirect",
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
resp, err := svc.RejectLoginRequest(context.Background(), challenge, "error", "desc")
|
||||
if err != nil {
|
||||
t.Fatalf("RejectLoginRequest failed: %v", err)
|
||||
}
|
||||
if resp.RedirectTo != "http://redirect" {
|
||||
t.Errorf("expected http://redirect, got %s", resp.RedirectTo)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_GetLoginRequest(t *testing.T) {
|
||||
challenge := "login-challenge"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := domain.HydraLoginRequest{Challenge: challenge}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
req, err := svc.GetLoginRequest(context.Background(), challenge)
|
||||
if err != nil {
|
||||
t.Fatalf("GetLoginRequest failed: %v", err)
|
||||
}
|
||||
if req.Challenge != challenge {
|
||||
t.Errorf("expected %s, got %s", challenge, req.Challenge)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_AcceptConsentRequest(t *testing.T) {
|
||||
challenge := "consent-challenge"
|
||||
grantInfo := &domain.HydraConsentRequest{RequestedScope: []string{"openid"}}
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := struct {
|
||||
RedirectTo string `json:"redirect_to"`
|
||||
}{
|
||||
RedirectTo: "http://callback",
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
resp, err := svc.AcceptConsentRequest(context.Background(), challenge, grantInfo, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("AcceptConsentRequest failed: %v", err)
|
||||
}
|
||||
if resp.RedirectTo != "http://callback" {
|
||||
t.Errorf("expected http://callback, got %s", resp.RedirectTo)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHydraAdminService_AcceptLoginRequest(t *testing.T) {
|
||||
challenge := "login-challenge"
|
||||
subject := "user@example.com"
|
||||
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
resp := struct {
|
||||
RedirectTo string `json:"redirect_to"`
|
||||
}{
|
||||
RedirectTo: "http://callback",
|
||||
}
|
||||
json.NewEncoder(w).Encode(resp)
|
||||
})
|
||||
|
||||
svc := &HydraAdminService{
|
||||
AdminURL: "http://hydra:4445",
|
||||
HTTPClient: mockHydraClient(handler),
|
||||
}
|
||||
|
||||
resp, err := svc.AcceptLoginRequest(context.Background(), challenge, subject)
|
||||
if err != nil {
|
||||
t.Fatalf("AcceptLoginRequest failed: %v", err)
|
||||
}
|
||||
if resp.RedirectTo != "http://callback" {
|
||||
t.Errorf("expected http://callback, got %s", resp.RedirectTo)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user