veza/veza-backend-api/tests/transactions/social_transaction_test.go

327 lines
11 KiB
Go

package transactions
import (
"context"
"testing"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.uber.org/zap/zaptest"
"gorm.io/driver/postgres"
"gorm.io/gorm"
"veza-backend-api/internal/core/social"
"veza-backend-api/internal/models"
"veza-backend-api/internal/testutils"
)
// setupTestDB crée une DB de test avec testcontainers
func setupTestDBForSocial(t *testing.T) *gorm.DB {
ctx := context.Background()
dsn, err := testutils.GetTestContainerDB(ctx)
require.NoError(t, err, "Failed to setup test database")
db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{})
require.NoError(t, err, "Failed to open database connection")
// Auto-migrate models nécessaires
// Note: On suppose que les tables likes, comments, posts existent
// Si elles n'existent pas, il faudra les créer via migrations
err = db.AutoMigrate(
&models.User{},
)
require.NoError(t, err, "Failed to migrate database")
// Créer les tables si elles n'existent pas (simplifié pour les tests)
db.Exec(`
CREATE TABLE IF NOT EXISTS likes (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
target_id UUID NOT NULL,
target_type VARCHAR(50) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
UNIQUE(user_id, target_id, target_type)
)
`)
db.Exec(`
CREATE TABLE IF NOT EXISTS posts (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
content TEXT,
like_count INTEGER DEFAULT 0,
comment_count INTEGER DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
`)
db.Exec(`
CREATE TABLE IF NOT EXISTS comments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
user_id UUID NOT NULL,
target_id UUID NOT NULL,
target_type VARCHAR(20) NOT NULL,
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deleted_at TIMESTAMP
)
`)
return db
}
// cleanupTestDB nettoie la DB entre les tests
func cleanupTestDBForSocial(t *testing.T, db *gorm.DB) {
db.Exec("TRUNCATE TABLE likes CASCADE")
db.Exec("TRUNCATE TABLE comments CASCADE")
db.Exec("TRUNCATE TABLE posts CASCADE")
db.Exec("TRUNCATE TABLE users CASCADE")
}
// createTestUser crée un utilisateur de test
func createTestUserForSocial(t *testing.T, db *gorm.DB) *models.User {
user := &models.User{
Username: "testuser_" + uuid.New().String()[:8],
Slug: "testuser_" + uuid.New().String()[:8], // Unique slug
Email: "test_" + uuid.New().String()[:8] + "@example.com",
PasswordHash: "$2a$10$examplehash",
IsActive: true,
IsVerified: true,
}
err := db.Create(user).Error
require.NoError(t, err)
return user
}
// createTestPost crée un post de test
func createTestPost(t *testing.T, db *gorm.DB, userID uuid.UUID) uuid.UUID {
// Scan UUID as string to avoid driver type conversion issues
var postIDStr string
err := db.Raw(`
INSERT INTO posts (id, user_id, content, like_count, comment_count)
VALUES (gen_random_uuid(), ?, ?, 0, 0)
RETURNING id
`, userID, "Test post content").Scan(&postIDStr).Error
require.NoError(t, err)
postID, err := uuid.Parse(postIDStr)
require.NoError(t, err)
return postID
}
// TestToggleLike_Success vérifie que le like fonctionne correctement
func TestToggleLike_Success(t *testing.T) {
db := setupTestDBForSocial(t)
defer cleanupTestDBForSocial(t, db)
logger := zaptest.NewLogger(t)
socialService := social.NewService(db, logger)
user := createTestUserForSocial(t, db)
postID := createTestPost(t, db, user.ID)
// Liker le post
liked, err := socialService.ToggleLike(context.Background(), user.ID, postID, "post")
require.NoError(t, err, "ToggleLike should succeed")
assert.True(t, liked, "Post should be liked")
// Vérifier que le like existe
var likeCount int64
db.Raw("SELECT COUNT(*) FROM likes WHERE user_id = ? AND target_id = ? AND target_type = ?",
user.ID, postID, "post").Scan(&likeCount)
assert.Equal(t, int64(1), likeCount, "Like should exist")
// Vérifier que le compteur est incrémenté
var postLikeCount int
db.Raw("SELECT like_count FROM posts WHERE id = ?", postID).Scan(&postLikeCount)
assert.Equal(t, 1, postLikeCount, "Post like_count should be 1")
}
// TestToggleLike_Unlike vérifie que l'unlike fonctionne correctement
func TestToggleLike_Unlike(t *testing.T) {
db := setupTestDBForSocial(t)
defer cleanupTestDBForSocial(t, db)
logger := zaptest.NewLogger(t)
socialService := social.NewService(db, logger)
user := createTestUserForSocial(t, db)
postID := createTestPost(t, db, user.ID)
// Liker d'abord
liked, err := socialService.ToggleLike(context.Background(), user.ID, postID, "post")
require.NoError(t, err)
assert.True(t, liked)
// Unliker
liked, err = socialService.ToggleLike(context.Background(), user.ID, postID, "post")
require.NoError(t, err, "ToggleLike (unlike) should succeed")
assert.False(t, liked, "Post should be unliked")
// Vérifier que le like n'existe plus
var likeCount int64
db.Raw("SELECT COUNT(*) FROM likes WHERE user_id = ? AND target_id = ? AND target_type = ?",
user.ID, postID, "post").Scan(&likeCount)
assert.Equal(t, int64(0), likeCount, "Like should be removed")
// Vérifier que le compteur est décrémenté
var postLikeCount int
db.Raw("SELECT like_count FROM posts WHERE id = ?", postID).Scan(&postLikeCount)
assert.Equal(t, 0, postLikeCount, "Post like_count should be 0")
}
// TestToggleLike_RollbackOnError vérifie le rollback si une erreur survient
func TestToggleLike_RollbackOnError(t *testing.T) {
db := setupTestDBForSocial(t)
defer cleanupTestDBForSocial(t, db)
logger := zaptest.NewLogger(t)
socialService := social.NewService(db, logger)
user := createTestUserForSocial(t, db)
postID := createTestPost(t, db, user.ID)
// Supprimer le post pour forcer une erreur lors de l'UPDATE du compteur
db.Exec("DELETE FROM posts WHERE id = ?", postID)
// Tenter de liker (devrait échouer car le post n'existe plus)
_, err := socialService.ToggleLike(context.Background(), user.ID, postID, "post")
require.Error(t, err, "ToggleLike should fail")
// Vérifier qu'aucun like n'a été créé (rollback)
var likeCount int64
db.Raw("SELECT COUNT(*) FROM likes WHERE user_id = ? AND target_id = ?",
user.ID, postID).Scan(&likeCount)
assert.Equal(t, int64(0), likeCount, "No like should be created on error")
}
// TestToggleLike_Coherence vérifie la cohérence entre likes et compteurs
func TestToggleLike_Coherence(t *testing.T) {
db := setupTestDBForSocial(t)
defer cleanupTestDBForSocial(t, db)
logger := zaptest.NewLogger(t)
socialService := social.NewService(db, logger)
user1 := createTestUserForSocial(t, db)
user2 := createTestUserForSocial(t, db)
postID := createTestPost(t, db, user1.ID)
// User1 like
_, err := socialService.ToggleLike(context.Background(), user1.ID, postID, "post")
require.NoError(t, err)
// User2 like
_, err = socialService.ToggleLike(context.Background(), user2.ID, postID, "post")
require.NoError(t, err)
// Vérifier la cohérence
var actualLikeCount int64
db.Raw("SELECT COUNT(*) FROM likes WHERE target_id = ? AND target_type = ?",
postID, "post").Scan(&actualLikeCount)
var postLikeCount int
db.Raw("SELECT like_count FROM posts WHERE id = ?", postID).Scan(&postLikeCount)
assert.Equal(t, int64(postLikeCount), actualLikeCount, "Like count should match actual likes")
assert.Equal(t, int64(2), actualLikeCount, "Should have 2 likes")
}
// TestAddComment_Success vérifie que l'ajout de commentaire fonctionne
func TestAddComment_Success(t *testing.T) {
db := setupTestDBForSocial(t)
defer cleanupTestDBForSocial(t, db)
logger := zaptest.NewLogger(t)
socialService := social.NewService(db, logger)
user := createTestUserForSocial(t, db)
postID := createTestPost(t, db, user.ID)
// Ajouter un commentaire
comment, err := socialService.AddComment(
context.Background(),
user.ID,
postID,
"post",
"Test comment",
)
require.NoError(t, err, "AddComment should succeed")
require.NotNil(t, comment, "Comment should be created")
assert.Equal(t, "Test comment", comment.Content, "Comment content should match")
// Vérifier que le commentaire existe
var commentCount int64
db.Raw("SELECT COUNT(*) FROM comments WHERE user_id = ? AND target_id = ? AND target_type = ?",
user.ID, postID, "post").Scan(&commentCount)
assert.Equal(t, int64(1), commentCount, "Comment should exist")
// Vérifier que le compteur est incrémenté
var postCommentCount int
db.Raw("SELECT comment_count FROM posts WHERE id = ?", postID).Scan(&postCommentCount)
assert.Equal(t, 1, postCommentCount, "Post comment_count should be 1")
}
// TestAddComment_RollbackOnError vérifie le rollback si une erreur survient
func TestAddComment_RollbackOnError(t *testing.T) {
db := setupTestDBForSocial(t)
defer cleanupTestDBForSocial(t, db)
logger := zaptest.NewLogger(t)
socialService := social.NewService(db, logger)
user := createTestUserForSocial(t, db)
postID := createTestPost(t, db, user.ID)
// Supprimer le post pour forcer une erreur lors de l'UPDATE du compteur
db.Exec("DELETE FROM posts WHERE id = ?", postID)
// Tenter d'ajouter un commentaire (devrait échouer)
_, err := socialService.AddComment(
context.Background(),
user.ID,
postID,
"post",
"Test comment",
)
require.Error(t, err, "AddComment should fail")
// Vérifier qu'aucun commentaire n'a été créé (rollback)
var commentCount int64
db.Raw("SELECT COUNT(*) FROM comments WHERE user_id = ? AND target_id = ?",
user.ID, postID).Scan(&commentCount)
assert.Equal(t, int64(0), commentCount, "No comment should be created on error")
}
// TestAddComment_Coherence vérifie la cohérence entre comments et compteurs
func TestAddComment_Coherence(t *testing.T) {
db := setupTestDBForSocial(t)
defer cleanupTestDBForSocial(t, db)
logger := zaptest.NewLogger(t)
socialService := social.NewService(db, logger)
user1 := createTestUserForSocial(t, db)
user2 := createTestUserForSocial(t, db)
postID := createTestPost(t, db, user1.ID)
// User1 commente
_, err := socialService.AddComment(context.Background(), user1.ID, postID, "post", "Comment 1")
require.NoError(t, err)
// User2 commente
_, err = socialService.AddComment(context.Background(), user2.ID, postID, "post", "Comment 2")
require.NoError(t, err)
// Vérifier la cohérence
var actualCommentCount int64
db.Raw("SELECT COUNT(*) FROM comments WHERE target_id = ? AND target_type = ?",
postID, "post").Scan(&actualCommentCount)
var postCommentCount int
db.Raw("SELECT comment_count FROM posts WHERE id = ?", postID).Scan(&postCommentCount)
assert.Equal(t, int64(postCommentCount), actualCommentCount, "Comment count should match actual comments")
assert.Equal(t, int64(2), actualCommentCount, "Should have 2 comments")
}