package handler import ( "baron-sso-backend/internal/domain" "bytes" "encoding/json" "net/http" "net/http/httptest" "testing" "github.com/gofiber/fiber/v2" "github.com/stretchr/testify/require" ) func TestUpdateMe_InvalidatesProfileCacheForTokenSession(t *testing.T) { token := "token-abc" identityID := "user-1" traits := map[string]interface{}{ "email": "qa@example.com", "name": "QA User", "phone_number": "+821012345678", "department": "Old Dept", "affiliationType": "employee", "companyCode": "", "role": domain.RoleUser, } transport := roundTripFunc(func(r *http.Request) (*http.Response, error) { switch { case r.URL.Host == "kratos.test" && r.URL.Path == "/sessions/whoami" && r.Method == http.MethodGet: if r.Header.Get("X-Session-Token") != token { return httpResponse(r, http.StatusUnauthorized, `{"error":"invalid token"}`), nil } return httpJSONAny(r, http.StatusOK, map[string]interface{}{ "identity": map[string]interface{}{ "id": identityID, "traits": traits, }, }), nil case r.URL.Host == "kratos.test" && r.URL.Path == "/admin/identities/"+identityID && r.Method == http.MethodPut: var payload struct { Traits map[string]interface{} `json:"traits"` } if err := json.NewDecoder(r.Body).Decode(&payload); err != nil { return httpResponse(r, http.StatusBadRequest, `{"error":"invalid body"}`), nil } for k, v := range payload.Traits { traits[k] = v } return httpResponse(r, http.StatusOK, `{"ok":true}`), nil } return httpResponse(r, http.StatusNotFound, "not found"), nil }) setDefaultHTTPClientForTest(t, transport) t.Setenv("KRATOS_PUBLIC_URL", "http://kratos.test") t.Setenv("KRATOS_ADMIN_URL", "http://kratos.test") redis := &mockRedisRepo{data: make(map[string]string)} h := &AuthHandler{ RedisService: redis, } app := fiber.New() app.Get("/api/v1/user/me", h.GetMe) app.Put("/api/v1/user/me", h.UpdateMe) // 1) 첫 조회로 Old Dept가 캐시에 저장됨 getReq1 := httptest.NewRequest(http.MethodGet, "/api/v1/user/me", nil) getReq1.Header.Set("Authorization", "Bearer "+token) getResp1, err := app.Test(getReq1, -1) require.NoError(t, err) require.Equal(t, http.StatusOK, getResp1.StatusCode) var profile1 map[string]interface{} require.NoError(t, json.NewDecoder(getResp1.Body).Decode(&profile1)) require.Equal(t, "Old Dept", profile1["department"]) // 2) 소속을 New Dept로 변경 updateBody, _ := json.Marshal(map[string]string{ "name": "QA User", "phone": "01012345678", "department": "New Dept", }) updateReq := httptest.NewRequest( http.MethodPut, "/api/v1/user/me", bytes.NewReader(updateBody), ) updateReq.Header.Set("Content-Type", "application/json") updateReq.Header.Set("Authorization", "Bearer "+token) updateResp, err := app.Test(updateReq, -1) require.NoError(t, err) require.Equal(t, http.StatusOK, updateResp.StatusCode) require.Equal(t, "New Dept", traits["department"]) // 3) 새로고침 재조회 시 New Dept가 보여야 함(캐시 무효화 회귀 방지) getReq2 := httptest.NewRequest(http.MethodGet, "/api/v1/user/me", nil) getReq2.Header.Set("Authorization", "Bearer "+token) getResp2, err := app.Test(getReq2, -1) require.NoError(t, err) require.Equal(t, http.StatusOK, getResp2.StatusCode) var profile2 map[string]interface{} require.NoError(t, json.NewDecoder(getResp2.Body).Decode(&profile2)) require.Equal(t, "New Dept", profile2["department"]) }