1
0
forked from baron/baron-sso
Files
baron-sso/backend/internal/service/hydra_admin_service_test.go

401 lines
11 KiB
Go

/*
이 테스트 파일은 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)
}
}