259 lines
9.3 KiB
Go
259 lines
9.3 KiB
Go
package e2e
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"goyco/internal/database"
|
|
)
|
|
|
|
func TestE2E_VoteCountConsistency(t *testing.T) {
|
|
ctx := setupTestContext(t)
|
|
|
|
t.Run("vote_count_consistency", func(t *testing.T) {
|
|
user1 := ctx.createUserWithCleanup(t, "voteuser1", "Password123!")
|
|
user2 := ctx.createUserWithCleanup(t, "voteuser2", "Password123!")
|
|
user3 := ctx.createUserWithCleanup(t, "voteuser3", "Password123!")
|
|
|
|
client1 := ctx.loginUser(t, user1.Username, user1.Password)
|
|
post := client1.CreatePost(t, "Vote Count Test", "https://example.com/votecount", "Content")
|
|
|
|
client1.VoteOnPost(t, post.ID, "up")
|
|
client2 := ctx.loginUser(t, user2.Username, user2.Password)
|
|
client2.VoteOnPost(t, post.ID, "up")
|
|
client3 := ctx.loginUser(t, user3.Username, user3.Password)
|
|
client3.VoteOnPost(t, post.ID, "down")
|
|
|
|
var dbPost database.Post
|
|
if err := ctx.server.DB.First(&dbPost, post.ID).Error; err != nil {
|
|
t.Fatalf("Failed to find post in database: %v", err)
|
|
}
|
|
|
|
var voteCount int64
|
|
ctx.server.DB.Model(&database.Vote{}).Where("post_id = ? AND type = ?", post.ID, database.VoteUp).Count(&voteCount)
|
|
if voteCount != int64(dbPost.UpVotes) {
|
|
t.Errorf("Expected upvote count %d to match database count %d", dbPost.UpVotes, voteCount)
|
|
}
|
|
|
|
ctx.server.DB.Model(&database.Vote{}).Where("post_id = ? AND type = ?", post.ID, database.VoteDown).Count(&voteCount)
|
|
if voteCount != int64(dbPost.DownVotes) {
|
|
t.Errorf("Expected downvote count %d to match database count %d", dbPost.DownVotes, voteCount)
|
|
}
|
|
|
|
postsResp := client1.GetPosts(t)
|
|
apiPost := findPostInList(postsResp, post.ID)
|
|
if apiPost == nil {
|
|
t.Fatalf("Expected to find post in API response")
|
|
}
|
|
if apiPost.UpVotes != dbPost.UpVotes {
|
|
t.Errorf("Expected API upvote count %d to match database %d", apiPost.UpVotes, dbPost.UpVotes)
|
|
}
|
|
if apiPost.DownVotes != dbPost.DownVotes {
|
|
t.Errorf("Expected API downvote count %d to match database %d", apiPost.DownVotes, dbPost.DownVotes)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestE2E_PostScoreCalculation(t *testing.T) {
|
|
ctx := setupTestContext(t)
|
|
|
|
t.Run("post_score_calculation", func(t *testing.T) {
|
|
user1 := ctx.createUserWithCleanup(t, "scoreuser1", "Password123!")
|
|
user2 := ctx.createUserWithCleanup(t, "scoreuser2", "Password123!")
|
|
user3 := ctx.createUserWithCleanup(t, "scoreuser3", "Password123!")
|
|
|
|
client1 := ctx.loginUser(t, user1.Username, user1.Password)
|
|
post := client1.CreatePost(t, "Score Test", "https://example.com/score", "Content")
|
|
|
|
client1.VoteOnPost(t, post.ID, "up")
|
|
client2 := ctx.loginUser(t, user2.Username, user2.Password)
|
|
client2.VoteOnPost(t, post.ID, "up")
|
|
client3 := ctx.loginUser(t, user3.Username, user3.Password)
|
|
client3.VoteOnPost(t, post.ID, "down")
|
|
|
|
var dbPost database.Post
|
|
if err := ctx.server.DB.First(&dbPost, post.ID).Error; err != nil {
|
|
t.Fatalf("Failed to find post in database: %v", err)
|
|
}
|
|
|
|
expectedScore := dbPost.UpVotes - dbPost.DownVotes
|
|
if dbPost.Score != expectedScore {
|
|
t.Errorf("Expected score %d (upvotes %d - downvotes %d), got %d", expectedScore, dbPost.UpVotes, dbPost.DownVotes, dbPost.Score)
|
|
}
|
|
|
|
postsResp := client1.GetPosts(t)
|
|
apiPost := findPostInList(postsResp, post.ID)
|
|
if apiPost == nil {
|
|
t.Fatalf("Expected to find post in API response")
|
|
}
|
|
if apiPost.Score != expectedScore {
|
|
t.Errorf("Expected API score %d to match calculated score %d", apiPost.Score, expectedScore)
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestE2E_PostDeletionCascades(t *testing.T) {
|
|
ctx := setupTestContext(t)
|
|
|
|
t.Run("post_deletion_cascades", func(t *testing.T) {
|
|
user1 := ctx.createUserWithCleanup(t, "cascadeuser1", "Password123!")
|
|
user2 := ctx.createUserWithCleanup(t, "cascadeuser2", "Password123!")
|
|
|
|
client1 := ctx.loginUser(t, user1.Username, user1.Password)
|
|
post := client1.CreatePost(t, "Cascade Test", "https://example.com/cascade", "Content")
|
|
|
|
client1.VoteOnPost(t, post.ID, "up")
|
|
client2 := ctx.loginUser(t, user2.Username, user2.Password)
|
|
client2.VoteOnPost(t, post.ID, "down")
|
|
|
|
var voteCountBefore int64
|
|
ctx.server.DB.Model(&database.Vote{}).Where("post_id = ?", post.ID).Count(&voteCountBefore)
|
|
if voteCountBefore == 0 {
|
|
t.Fatalf("Expected votes to exist before deletion")
|
|
}
|
|
|
|
client1.DeletePost(t, post.ID)
|
|
|
|
var voteCountAfter int64
|
|
ctx.server.DB.Model(&database.Vote{}).Where("post_id = ?", post.ID).Count(&voteCountAfter)
|
|
if voteCountAfter != 0 {
|
|
t.Errorf("Expected votes to be deleted after post deletion, found %d votes", voteCountAfter)
|
|
}
|
|
|
|
var dbPost database.Post
|
|
if err := ctx.server.DB.First(&dbPost, post.ID).Error; err == nil {
|
|
t.Errorf("Expected post to be deleted from database")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestE2E_UserDeletionCascades(t *testing.T) {
|
|
ctx := setupTestContext(t)
|
|
|
|
t.Run("user_deletion_cascades", func(t *testing.T) {
|
|
user1 := ctx.createUserWithCleanup(t, "deleteuser1", "Password123!")
|
|
user2 := ctx.createUserWithCleanup(t, "deleteuser2", "Password123!")
|
|
|
|
client1 := ctx.loginUser(t, user1.Username, user1.Password)
|
|
post1 := client1.CreatePost(t, "Post 1", "https://example.com/post1", "Content 1")
|
|
post2 := client1.CreatePost(t, "Post 2", "https://example.com/post2", "Content 2")
|
|
|
|
client2 := ctx.loginUser(t, user2.Username, user2.Password)
|
|
client2.VoteOnPost(t, post1.ID, "up")
|
|
|
|
var postCountBefore int64
|
|
ctx.server.DB.Model(&database.Post{}).Where("author_id = ?", user1.ID).Count(&postCountBefore)
|
|
if postCountBefore == 0 {
|
|
t.Fatalf("Expected posts to exist before deletion")
|
|
}
|
|
|
|
var voteCountBefore int64
|
|
ctx.server.DB.Model(&database.Vote{}).Where("post_id IN (?)", []uint{post1.ID, post2.ID}).Count(&voteCountBefore)
|
|
if voteCountBefore == 0 {
|
|
t.Fatalf("Expected votes to exist before deletion")
|
|
}
|
|
|
|
ctx.server.EmailSender.Reset()
|
|
client1.RequestAccountDeletion(t)
|
|
deletionToken := ctx.server.EmailSender.DeletionToken()
|
|
if deletionToken == "" {
|
|
t.Fatalf("Expected deletion token")
|
|
}
|
|
|
|
client1.ConfirmAccountDeletion(t, deletionToken, false)
|
|
|
|
var postCountAfter int64
|
|
ctx.server.DB.Model(&database.Post{}).Where("author_id = ?", user1.ID).Count(&postCountAfter)
|
|
if postCountAfter != 0 {
|
|
t.Errorf("Expected posts to be deleted after user deletion, found %d posts", postCountAfter)
|
|
}
|
|
|
|
var voteCountAfter int64
|
|
ctx.server.DB.Model(&database.Vote{}).Where("post_id IN (?)", []uint{post1.ID, post2.ID}).Count(&voteCountAfter)
|
|
if voteCountAfter != 0 {
|
|
t.Errorf("Expected votes to be deleted after post deletion, found %d votes", voteCountAfter)
|
|
}
|
|
|
|
var dbUser database.User
|
|
if err := ctx.server.DB.First(&dbUser, user1.ID).Error; err == nil {
|
|
t.Errorf("Expected user to be deleted from database")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestE2E_ReferentialIntegrity(t *testing.T) {
|
|
ctx := setupTestContext(t)
|
|
|
|
t.Run("referential_integrity", func(t *testing.T) {
|
|
user1 := ctx.createUserWithCleanup(t, "refuser1", "Password123!")
|
|
user2 := ctx.createUserWithCleanup(t, "refuser2", "Password123!")
|
|
|
|
client1 := ctx.loginUser(t, user1.Username, user1.Password)
|
|
post := client1.CreatePost(t, "Ref Integrity Test", "https://example.com/ref", "Content")
|
|
|
|
client2 := ctx.loginUser(t, user2.Username, user2.Password)
|
|
client2.VoteOnPost(t, post.ID, "up")
|
|
|
|
var voteCount int64
|
|
ctx.server.DB.Model(&database.Vote{}).Where("post_id = ? AND user_id = ?", post.ID, user2.ID).Count(&voteCount)
|
|
if voteCount != 1 {
|
|
t.Errorf("Expected vote to exist with correct foreign keys")
|
|
}
|
|
|
|
var postCount int64
|
|
ctx.server.DB.Model(&database.Post{}).Where("author_id = ?", user1.ID).Count(&postCount)
|
|
if postCount == 0 {
|
|
t.Errorf("Expected post to exist with correct author foreign key")
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestE2E_OrphanedRecordsPrevention(t *testing.T) {
|
|
ctx := setupTestContext(t)
|
|
|
|
t.Run("orphaned_records_prevention", func(t *testing.T) {
|
|
user1 := ctx.createUserWithCleanup(t, "orphanuser1", "Password123!")
|
|
user2 := ctx.createUserWithCleanup(t, "orphanuser2", "Password123!")
|
|
|
|
client1 := ctx.loginUser(t, user1.Username, user1.Password)
|
|
post := client1.CreatePost(t, "Orphan Test", "https://example.com/orphan", "Content")
|
|
|
|
client2 := ctx.loginUser(t, user2.Username, user2.Password)
|
|
client2.VoteOnPost(t, post.ID, "up")
|
|
|
|
var voteCountBefore int64
|
|
ctx.server.DB.Model(&database.Vote{}).Where("post_id = ?", post.ID).Count(&voteCountBefore)
|
|
|
|
client1.DeletePost(t, post.ID)
|
|
|
|
var orphanedVotes int64
|
|
ctx.server.DB.Unscoped().Model(&database.Vote{}).Where("post_id = ?", post.ID).Count(&orphanedVotes)
|
|
if orphanedVotes != 0 {
|
|
t.Errorf("Expected no orphaned votes after post deletion, found %d", orphanedVotes)
|
|
}
|
|
|
|
post2 := client1.CreatePost(t, "Orphan Test 2", "https://example.com/orphan2", "Content")
|
|
client2.VoteOnPost(t, post2.ID, "up")
|
|
|
|
ctx.server.EmailSender.Reset()
|
|
client1.RequestAccountDeletion(t)
|
|
deletionToken := ctx.server.EmailSender.DeletionToken()
|
|
if deletionToken == "" {
|
|
t.Fatalf("Expected deletion token")
|
|
}
|
|
|
|
client1.ConfirmAccountDeletion(t, deletionToken, false)
|
|
|
|
var orphanedPosts int64
|
|
ctx.server.DB.Unscoped().Model(&database.Post{}).Where("author_id = ?", user1.ID).Count(&orphanedPosts)
|
|
if orphanedPosts != 0 {
|
|
t.Errorf("Expected no posts with author_id = %d after user deletion, found %d", user1.ID, orphanedPosts)
|
|
}
|
|
|
|
var orphanedVotesAfter int64
|
|
ctx.server.DB.Unscoped().Model(&database.Vote{}).Where("post_id = ?", post2.ID).Count(&orphanedVotesAfter)
|
|
if orphanedVotesAfter != 0 {
|
|
t.Errorf("Expected no orphaned votes after post deletion via user deletion, found %d", orphanedVotesAfter)
|
|
}
|
|
})
|
|
}
|