327 lines
11 KiB
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")
|
|
}
|