Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 55 additions & 28 deletions apps/server/internal/modules/auth/handler_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -376,42 +376,69 @@ func TestRefreshResponseStructure(t *testing.T) {
//
// Requirements: 0.7
func TestLogoutTokenRevocation(t *testing.T) {
tests := []struct {
name string
scenario string
}{
{
name: "logout with refresh token deletes token",
scenario: "refresh_token cookie present",
},
{
name: "logout without refresh token succeeds",
scenario: "no refresh_token cookie",
},
}
t.Run("logout without refresh token clears cookies", func(t *testing.T) {
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/v1/auth/logout", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
// This test documents that Logout:
// - Deletes refresh token from database if present
// - Clears access_token and refresh_token cookies (MaxAge=-1)
// - Always returns success (idempotent)
// - Returns HTTP 200 status
t.Logf("Scenario: %s", tt.scenario)
})
}
h := &Handler{} // No service needed when no refresh_token cookie exists

err := h.Logout(c)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if rec.Code != http.StatusOK {
t.Errorf("expected status %d, got %d", http.StatusOK, rec.Code)
}

cookies := rec.Result().Cookies()
accessCleared := false
refreshCleared := false
for _, cookie := range cookies {
if cookie.Name == "access_token" && cookie.MaxAge == -1 {
accessCleared = true
}
if cookie.Name == "refresh_token" && cookie.MaxAge == -1 {
refreshCleared = true
}
}

if !accessCleared {
t.Error("access_token cookie was not cleared")
}
if !refreshCleared {
t.Error("refresh_token cookie was not cleared")
}
})
}

// TestLogoutResponseStructure verifies response format
//
// Requirements: 0.7
func TestLogoutResponseStructure(t *testing.T) {
t.Run("response indicates success", func(t *testing.T) {
// This test documents that Logout returns:
// - success: true
// - data: {} (empty object)
// - HTTP 200 status
t.Log("Response follows GenericResponse structure")
e := echo.New()
req := httptest.NewRequest(http.MethodPost, "/v1/auth/logout", nil)
rec := httptest.NewRecorder()
c := e.NewContext(req, rec)

h := &Handler{} // No service needed when no refresh_token cookie exists

err := h.Logout(c)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}

if rec.Code != http.StatusOK {
t.Errorf("expected status %d, got %d", http.StatusOK, rec.Code)
}

body := rec.Body.String()
if !strings.Contains(body, "\"success\":true") {
t.Errorf("expected response to contain success:true, got %s", body)
}
})
}

Expand Down
Loading