A community based topic aggregation platform built on atproto
at main 202 lines 5.9 kB view raw
1package user 2 3import ( 4 "context" 5 "encoding/json" 6 "net/http" 7 "net/http/httptest" 8 "testing" 9 "time" 10 11 "Coves/internal/api/middleware" 12 "Coves/internal/core/users" 13 14 "github.com/stretchr/testify/assert" 15 "github.com/stretchr/testify/mock" 16) 17 18func TestHandleMe_Success(t *testing.T) { 19 mockService := new(MockUserService) 20 handler := NewMeHandler(mockService) 21 22 testDID := "did:plc:testme123" 23 profile := &users.ProfileViewDetailed{ 24 DID: testDID, 25 Handle: "alice.test", 26 CreatedAt: time.Date(2025, 1, 1, 0, 0, 0, 0, time.UTC), 27 DisplayName: "Alice", 28 Bio: "Hello world", 29 Avatar: "https://cdn.example.com/avatar.jpg", 30 Stats: &users.ProfileStats{ 31 PostCount: 10, 32 CommentCount: 5, 33 CommunityCount: 3, 34 Reputation: 42, 35 }, 36 } 37 mockService.On("GetProfile", mock.Anything, testDID).Return(profile, nil) 38 39 req := httptest.NewRequest(http.MethodGet, "/api/me", nil) 40 ctx := middleware.SetTestUserDID(req.Context(), testDID) 41 req = req.WithContext(ctx) 42 43 w := httptest.NewRecorder() 44 handler.HandleMe(w, req) 45 46 assert.Equal(t, http.StatusOK, w.Code) 47 assert.Equal(t, "application/json", w.Header().Get("Content-Type")) 48 49 // Unmarshal into raw map to verify wire-format JSON key names 50 var raw map[string]json.RawMessage 51 err := json.Unmarshal(w.Body.Bytes(), &raw) 52 assert.NoError(t, err) 53 54 // Bio field has json:"description" tag — verify the wire key is "description", not "bio" 55 assert.Contains(t, raw, "description", "Bio should serialize as 'description' per json tag") 56 assert.NotContains(t, raw, "bio", "'bio' key should not appear in JSON output") 57 58 // Also verify typed deserialization 59 var resp users.ProfileViewDetailed 60 err = json.Unmarshal(w.Body.Bytes(), &resp) 61 assert.NoError(t, err) 62 assert.Equal(t, testDID, resp.DID) 63 assert.Equal(t, "alice.test", resp.Handle) 64 assert.Equal(t, "Alice", resp.DisplayName) 65 assert.Equal(t, "Hello world", resp.Bio) 66 assert.Equal(t, 10, resp.Stats.PostCount) 67 68 mockService.AssertExpectations(t) 69} 70 71func TestHandleMe_NilStats(t *testing.T) { 72 mockService := new(MockUserService) 73 handler := NewMeHandler(mockService) 74 75 testDID := "did:plc:nostats" 76 profile := &users.ProfileViewDetailed{ 77 DID: testDID, 78 Handle: "bob.test", 79 CreatedAt: time.Date(2025, 6, 1, 0, 0, 0, 0, time.UTC), 80 Stats: nil, 81 } 82 mockService.On("GetProfile", mock.Anything, testDID).Return(profile, nil) 83 84 req := httptest.NewRequest(http.MethodGet, "/api/me", nil) 85 ctx := middleware.SetTestUserDID(req.Context(), testDID) 86 req = req.WithContext(ctx) 87 88 w := httptest.NewRecorder() 89 handler.HandleMe(w, req) 90 91 assert.Equal(t, http.StatusOK, w.Code) 92 93 var raw map[string]json.RawMessage 94 err := json.Unmarshal(w.Body.Bytes(), &raw) 95 assert.NoError(t, err) 96 97 // stats should be omitted when nil (omitempty tag) 98 assert.NotContains(t, raw, "stats", "nil Stats should be omitted from JSON") 99 100 mockService.AssertExpectations(t) 101} 102 103func TestHandleMe_Unauthenticated(t *testing.T) { 104 mockService := new(MockUserService) 105 handler := NewMeHandler(mockService) 106 107 req := httptest.NewRequest(http.MethodGet, "/api/me", nil) 108 109 w := httptest.NewRecorder() 110 handler.HandleMe(w, req) 111 112 assert.Equal(t, http.StatusUnauthorized, w.Code) 113 114 // Validate the full error JSON structure 115 var errResp map[string]string 116 err := json.Unmarshal(w.Body.Bytes(), &errResp) 117 assert.NoError(t, err) 118 assert.Equal(t, "AuthRequired", errResp["error"]) 119 assert.Equal(t, "Authentication required", errResp["message"]) 120 121 mockService.AssertNotCalled(t, "GetProfile", mock.Anything, mock.Anything) 122} 123 124func TestHandleMe_UserNotFound(t *testing.T) { 125 mockService := new(MockUserService) 126 handler := NewMeHandler(mockService) 127 128 testDID := "did:plc:nonexistent" 129 mockService.On("GetProfile", mock.Anything, testDID).Return(nil, users.ErrUserNotFound) 130 131 req := httptest.NewRequest(http.MethodGet, "/api/me", nil) 132 ctx := middleware.SetTestUserDID(req.Context(), testDID) 133 req = req.WithContext(ctx) 134 135 w := httptest.NewRecorder() 136 handler.HandleMe(w, req) 137 138 assert.Equal(t, http.StatusNotFound, w.Code) 139 assert.Contains(t, w.Body.String(), "AccountNotFound") 140 141 mockService.AssertExpectations(t) 142} 143 144func TestHandleMe_InternalError(t *testing.T) { 145 mockService := new(MockUserService) 146 handler := NewMeHandler(mockService) 147 148 testDID := "did:plc:erroruser" 149 mockService.On("GetProfile", mock.Anything, testDID).Return(nil, assert.AnError) 150 151 req := httptest.NewRequest(http.MethodGet, "/api/me", nil) 152 ctx := middleware.SetTestUserDID(req.Context(), testDID) 153 req = req.WithContext(ctx) 154 155 w := httptest.NewRecorder() 156 handler.HandleMe(w, req) 157 158 assert.Equal(t, http.StatusInternalServerError, w.Code) 159 assert.Contains(t, w.Body.String(), "InternalServerError") 160 161 mockService.AssertExpectations(t) 162} 163 164func TestHandleMe_Timeout(t *testing.T) { 165 mockService := new(MockUserService) 166 handler := NewMeHandler(mockService) 167 168 testDID := "did:plc:timeoutuser" 169 mockService.On("GetProfile", mock.Anything, testDID).Return(nil, context.DeadlineExceeded) 170 171 req := httptest.NewRequest(http.MethodGet, "/api/me", nil) 172 ctx := middleware.SetTestUserDID(req.Context(), testDID) 173 req = req.WithContext(ctx) 174 175 w := httptest.NewRecorder() 176 handler.HandleMe(w, req) 177 178 assert.Equal(t, http.StatusGatewayTimeout, w.Code) 179 assert.Contains(t, w.Body.String(), "Timeout") 180 181 mockService.AssertExpectations(t) 182} 183 184func TestHandleMe_ContextCanceled(t *testing.T) { 185 mockService := new(MockUserService) 186 handler := NewMeHandler(mockService) 187 188 testDID := "did:plc:canceluser" 189 mockService.On("GetProfile", mock.Anything, testDID).Return(nil, context.Canceled) 190 191 req := httptest.NewRequest(http.MethodGet, "/api/me", nil) 192 ctx := middleware.SetTestUserDID(req.Context(), testDID) 193 req = req.WithContext(ctx) 194 195 w := httptest.NewRecorder() 196 handler.HandleMe(w, req) 197 198 assert.Equal(t, http.StatusBadRequest, w.Code) 199 assert.Contains(t, w.Body.String(), "RequestCanceled") 200 201 mockService.AssertExpectations(t) 202}