To gitea and beyond, let's go(-yco)

This commit is contained in:
2025-11-10 19:12:09 +01:00
parent 8f6133392d
commit 71a031342b
245 changed files with 83994 additions and 0 deletions

View File

@@ -0,0 +1,246 @@
package e2e
import (
"bytes"
"fmt"
"net/http"
"sync"
"testing"
"time"
"goyco/internal/testutils"
)
func TestE2E_CompleteUserJourney(t *testing.T) {
ctx := setupTestContext(t)
t.Run("complete_user_journey", func(t *testing.T) {
_, authClient := ctx.createUserAndLogin(t, "testuser", "StrongPass123!")
createdPost := authClient.CreatePost(t, "Test Post", "https://example.com/test", "This is a test post content")
voteResp := authClient.VoteOnPost(t, createdPost.ID, "up")
if !voteResp.Success {
t.Errorf("Expected vote to be successful, got failure: %s", voteResp.Message)
}
postsResp := authClient.GetPosts(t)
assertPostInList(t, postsResp, createdPost)
searchResp := authClient.SearchPosts(t, "test")
assertPostInList(t, searchResp, createdPost)
authClient.Logout(t)
})
}
func TestE2E_ErrorHandlingWorkflows(t *testing.T) {
ctx := setupTestContext(t)
t.Run("unauthenticated_user_workflow", func(t *testing.T) {
request, err := http.NewRequest("POST", ctx.baseURL+"/api/posts", bytes.NewReader([]byte(`{"title":"Test","url":"https://example.com"}`)))
if err != nil {
t.Fatalf("Failed to create request: %v", err)
}
request.Header.Set("Content-Type", "application/json")
testutils.WithStandardHeaders(request)
resp, err := ctx.client.Do(request)
if err != nil {
t.Fatalf("Failed to make request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Expected 401 for unauthenticated post creation, got %d", resp.StatusCode)
}
request, err = testutils.NewRequestBuilder("GET", ctx.baseURL+"/api/auth/me").Build()
if err != nil {
t.Fatalf("Failed to create request: %v", err)
}
resp, err = ctx.client.Do(request)
if err != nil {
t.Fatalf("Failed to make request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusUnauthorized {
t.Errorf("Expected 401 for unauthenticated profile access, got %d", resp.StatusCode)
}
})
t.Run("invalid_registration_workflow", func(t *testing.T) {
invalidData := []struct {
name string
body []byte
}{
{
name: "empty_username",
body: []byte(`{"username":"","email":"test@example.com","password":"ValidPass123!"}`),
},
{
name: "invalid_email",
body: []byte(`{"username":"testuser","email":"invalid-email","password":"ValidPass123!"}`),
},
{
name: "weak_password",
body: []byte(`{"username":"testuser","email":"test@example.com","password":"123"}`),
},
{
name: "malformed_json",
body: []byte(`{"username": "test", "password": }`),
},
}
for _, test := range invalidData {
t.Run(test.name, func(t *testing.T) {
request, err := testutils.NewRequestBuilder("POST", ctx.baseURL+"/api/auth/register").
WithBody(bytes.NewReader(test.body)).
Build()
if err != nil {
t.Fatalf("Failed to create request: %v", err)
}
resp, err := ctx.client.Do(request)
if err != nil {
t.Fatalf("Failed to make request: %v", err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusOK || resp.StatusCode == http.StatusCreated {
t.Errorf("Expected invalid registration to fail, got success status %d", resp.StatusCode)
}
})
}
})
}
func TestE2E_ConcurrentUserWorkflows(t *testing.T) {
ctx := setupTestContext(t)
t.Run("concurrent_user_workflows", func(t *testing.T) {
users := ctx.createMultipleUsersWithCleanup(t, 3, "concurrent", "StrongPass123!")
type result struct {
userID uint
err error
}
results := make(chan result, len(users))
var wg sync.WaitGroup
done := make(chan struct{})
for _, user := range users {
u := user
wg.Add(1)
go func() {
defer wg.Done()
var err error
authClient, loginErr := ctx.loginUserSafe(t, u.Username, u.Password)
if loginErr != nil || authClient == nil || authClient.Token == "" {
err = fmt.Errorf("User %s failed to login", u.Username)
} else {
postURL := fmt.Sprintf("https://example.com/concurrent/%d", u.ID)
post, postErr := authClient.CreatePostSafe("Concurrent Post", postURL, "Content")
if postErr != nil || post == nil || post.ID == 0 {
err = fmt.Errorf("User %s failed to create post: %v", u.Username, postErr)
} else {
voteResp, voteErr := authClient.VoteOnPostSafe(post.ID, "up")
if voteErr != nil || voteResp == nil || !voteResp.Success {
err = fmt.Errorf("User %s failed to vote: %v", u.Username, voteErr)
}
}
}
select {
case results <- result{userID: u.ID, err: err}:
case <-done:
}
}()
}
go func() {
wg.Wait()
close(results)
}()
timeout := time.After(10 * time.Second)
successCount := 0
receivedCount := 0
for {
select {
case res, ok := <-results:
if !ok {
return
}
receivedCount++
if res.err != nil {
t.Errorf("Concurrent operation error for user %d: %v", res.userID, res.err)
} else {
successCount++
}
if receivedCount >= len(users) {
return
}
case <-timeout:
close(done)
t.Errorf("Timeout waiting for concurrent operations to complete")
return
}
}
})
}
func TestE2E_SystemMonitoringWorkflows(t *testing.T) {
ctx := setupTestContext(t)
t.Run("system_monitoring_workflows", func(t *testing.T) {
t.Run("health_endpoint", func(t *testing.T) {
health := getHealth(t, ctx.client, ctx.baseURL)
if !health.Success {
t.Errorf("Expected health check to succeed, got failure: %s", health.Message)
}
})
t.Run("metrics_endpoint", func(t *testing.T) {
metrics := getMetrics(t, ctx.client, ctx.baseURL)
if metrics == nil {
t.Errorf("Expected metrics to be returned")
}
})
})
}
func TestE2E_AccountDeletion(t *testing.T) {
ctx := setupTestContext(t)
t.Run("account_deletion_flow", func(t *testing.T) {
_, authClient := ctx.createUserAndLogin(t, "testuser", "StrongPass123!")
_ = authClient.CreatePost(t, "Test Post", "https://example.com/test", "Test content")
statusCode, deletionResp := ctx.requestAccountDeletionExpectStatus(t, authClient.Token, http.StatusOK)
if statusCode == http.StatusTooManyRequests {
statusCode = retryOnRateLimit(t, 3, func() int {
code, _ := ctx.requestAccountDeletionExpectStatus(t, authClient.Token, http.StatusOK)
return code
})
if statusCode == http.StatusTooManyRequests {
t.Skip("Skipping account deletion flow test: rate limited after retries")
return
}
}
if deletionResp == nil {
t.Fatalf("Expected account deletion response, got nil")
}
if !deletionResp.Success {
t.Errorf("Expected account deletion request to be successful, got %v", deletionResp.Success)
}
if deletionResp.Message == "" {
t.Errorf("Expected deletion message to be present, got empty string")
}
})
}