To gitea and beyond, let's go(-yco)
This commit is contained in:
201
internal/integration/edge_cases_integration_test.go
Normal file
201
internal/integration/edge_cases_integration_test.go
Normal file
@@ -0,0 +1,201 @@
|
||||
package integration
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"goyco/internal/middleware"
|
||||
"goyco/internal/testutils"
|
||||
)
|
||||
|
||||
func TestIntegration_EdgeCases(t *testing.T) {
|
||||
ctx := setupTestContext(t)
|
||||
|
||||
t.Run("Expired_Token_Handling", func(t *testing.T) {
|
||||
ctx.Suite.EmailSender.Reset()
|
||||
createAuthenticatedUser(t, ctx.AuthService, ctx.Suite.UserRepo, "expired_user", "expired@example.com")
|
||||
|
||||
expiredToken := "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2MDAwMDAwMDB9.expired"
|
||||
|
||||
req := httptest.NewRequest("GET", "/api/auth/me", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+expiredToken)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
ctx.Router.ServeHTTP(rec, req)
|
||||
|
||||
assertErrorResponse(t, rec, http.StatusUnauthorized)
|
||||
})
|
||||
|
||||
t.Run("Concurrent_Vote_Operations", func(t *testing.T) {
|
||||
ctx.Suite.EmailSender.Reset()
|
||||
user1 := createAuthenticatedUser(t, ctx.AuthService, ctx.Suite.UserRepo, "vote_user1", "vote1@example.com")
|
||||
|
||||
post := testutils.CreatePostWithRepo(t, ctx.Suite.PostRepo, user1.User.ID, "Concurrent Vote Post", "https://example.com/concurrent")
|
||||
|
||||
var wg sync.WaitGroup
|
||||
errors := make(chan error, 10)
|
||||
|
||||
for i := 0; i < 5; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
voteBody := map[string]string{"type": "up"}
|
||||
body, _ := json.Marshal(voteBody)
|
||||
req := httptest.NewRequest("POST", fmt.Sprintf("/api/posts/%d/vote", post.ID), bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+user1.Token)
|
||||
req = testutils.WithUserContext(req, middleware.UserIDKey, user1.User.ID)
|
||||
req = testutils.WithURLParams(req, map[string]string{"id": fmt.Sprintf("%d", post.ID)})
|
||||
rec := httptest.NewRecorder()
|
||||
ctx.Router.ServeHTTP(rec, req)
|
||||
if rec.Code != http.StatusOK {
|
||||
errors <- fmt.Errorf("unexpected status: %d", rec.Code)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
close(errors)
|
||||
|
||||
for err := range errors {
|
||||
t.Error(err)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Large_Payload_Handling", func(t *testing.T) {
|
||||
ctx.Suite.EmailSender.Reset()
|
||||
user := createAuthenticatedUser(t, ctx.AuthService, ctx.Suite.UserRepo, "large_user", "large@example.com")
|
||||
|
||||
largeContent := make([]byte, 10001)
|
||||
for i := range largeContent {
|
||||
largeContent[i] = 'a'
|
||||
}
|
||||
|
||||
postBody := map[string]string{
|
||||
"title": "Large Post",
|
||||
"url": "https://example.com/large",
|
||||
"content": string(largeContent),
|
||||
}
|
||||
body, _ := json.Marshal(postBody)
|
||||
req := httptest.NewRequest("POST", "/api/posts", bytes.NewBuffer(body))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+user.Token)
|
||||
req = testutils.WithUserContext(req, middleware.UserIDKey, user.User.ID)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
ctx.Router.ServeHTTP(rec, req)
|
||||
|
||||
assertErrorResponse(t, rec, http.StatusBadRequest)
|
||||
|
||||
smallContent := make([]byte, 1000)
|
||||
for i := range smallContent {
|
||||
smallContent[i] = 'a'
|
||||
}
|
||||
|
||||
postBody2 := map[string]string{
|
||||
"title": "Small Post",
|
||||
"url": "https://example.com/small",
|
||||
"content": string(smallContent),
|
||||
}
|
||||
body2, _ := json.Marshal(postBody2)
|
||||
req2 := httptest.NewRequest("POST", "/api/posts", bytes.NewBuffer(body2))
|
||||
req2.Header.Set("Content-Type", "application/json")
|
||||
req2.Header.Set("Authorization", "Bearer "+user.Token)
|
||||
req2 = testutils.WithUserContext(req2, middleware.UserIDKey, user.User.ID)
|
||||
rec2 := httptest.NewRecorder()
|
||||
|
||||
ctx.Router.ServeHTTP(rec2, req2)
|
||||
|
||||
assertStatus(t, rec2, http.StatusCreated)
|
||||
})
|
||||
|
||||
t.Run("Malformed_JSON_Payloads", func(t *testing.T) {
|
||||
ctx.Suite.EmailSender.Reset()
|
||||
user := createAuthenticatedUser(t, ctx.AuthService, ctx.Suite.UserRepo, "malformed_user", "malformed@example.com")
|
||||
|
||||
malformedPayloads := []string{
|
||||
`{"title": "test"`,
|
||||
`{"title": "test",}`,
|
||||
`{title: "test"}`,
|
||||
`{"title": 'test'}`,
|
||||
`{"title": "test" "url": ""}`,
|
||||
}
|
||||
|
||||
for _, payload := range malformedPayloads {
|
||||
req := httptest.NewRequest("POST", "/api/posts", bytes.NewBufferString(payload))
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("Authorization", "Bearer "+user.Token)
|
||||
req = testutils.WithUserContext(req, middleware.UserIDKey, user.User.ID)
|
||||
rec := httptest.NewRecorder()
|
||||
|
||||
ctx.Router.ServeHTTP(rec, req)
|
||||
|
||||
assertErrorResponse(t, rec, http.StatusBadRequest)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("Race_Condition_Vote_Removal", func(t *testing.T) {
|
||||
ctx.Suite.EmailSender.Reset()
|
||||
user := createAuthenticatedUser(t, ctx.AuthService, ctx.Suite.UserRepo, "race_user", "race@example.com")
|
||||
|
||||
post := testutils.CreatePostWithRepo(t, ctx.Suite.PostRepo, user.User.ID, "Race Post", "https://example.com/race")
|
||||
|
||||
voteBody := map[string]string{"type": "up"}
|
||||
body, _ := json.Marshal(voteBody)
|
||||
|
||||
voteReq := httptest.NewRequest("POST", fmt.Sprintf("/api/posts/%d/vote", post.ID), bytes.NewBuffer(body))
|
||||
voteReq.Header.Set("Content-Type", "application/json")
|
||||
voteReq.Header.Set("Authorization", "Bearer "+user.Token)
|
||||
voteReq = testutils.WithUserContext(voteReq, middleware.UserIDKey, user.User.ID)
|
||||
voteReq = testutils.WithURLParams(voteReq, map[string]string{"id": fmt.Sprintf("%d", post.ID)})
|
||||
voteRec := httptest.NewRecorder()
|
||||
ctx.Router.ServeHTTP(voteRec, voteReq)
|
||||
assertStatus(t, voteRec, http.StatusOK)
|
||||
|
||||
var wg sync.WaitGroup
|
||||
for i := 0; i < 3; i++ {
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
req := httptest.NewRequest("DELETE", fmt.Sprintf("/api/posts/%d/vote", post.ID), nil)
|
||||
req.Header.Set("Authorization", "Bearer "+user.Token)
|
||||
req = testutils.WithUserContext(req, middleware.UserIDKey, user.User.ID)
|
||||
req = testutils.WithURLParams(req, map[string]string{"id": fmt.Sprintf("%d", post.ID)})
|
||||
rec := httptest.NewRecorder()
|
||||
ctx.Router.ServeHTTP(rec, req)
|
||||
}()
|
||||
}
|
||||
wg.Wait()
|
||||
|
||||
getVotesReq := httptest.NewRequest("GET", fmt.Sprintf("/api/posts/%d/votes", post.ID), nil)
|
||||
getVotesReq.Header.Set("Authorization", "Bearer "+user.Token)
|
||||
getVotesReq = testutils.WithUserContext(getVotesReq, middleware.UserIDKey, user.User.ID)
|
||||
getVotesReq = testutils.WithURLParams(getVotesReq, map[string]string{"id": fmt.Sprintf("%d", post.ID)})
|
||||
getVotesRec := httptest.NewRecorder()
|
||||
ctx.Router.ServeHTTP(getVotesRec, getVotesReq)
|
||||
|
||||
votesResponse := assertJSONResponse(t, getVotesRec, http.StatusOK)
|
||||
if votesResponse != nil {
|
||||
if data, ok := votesResponse["data"].(map[string]any); ok {
|
||||
if votes, ok := data["votes"].([]any); ok {
|
||||
userVoteCount := 0
|
||||
for _, vote := range votes {
|
||||
if voteMap, ok := vote.(map[string]any); ok {
|
||||
if userID, ok := voteMap["user_id"].(float64); ok && uint(userID) == user.User.ID {
|
||||
userVoteCount++
|
||||
}
|
||||
}
|
||||
}
|
||||
if userVoteCount > 1 {
|
||||
t.Errorf("Expected at most 1 vote from user, got %d", userVoteCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user