//go:build integration // +build integration package integration import ( "context" "encoding/json" "fmt" "net/http" "net/http/httptest" "testing" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "go.uber.org/zap" "gorm.io/driver/postgres" "gorm.io/gorm" "veza-backend-api/internal/core/track" "veza-backend-api/internal/models" "veza-backend-api/internal/services" "veza-backend-api/internal/testutils" ) // TestTrackQuotaEndpoint_GetQuota tests the GET /api/v1/tracks/quota/:id endpoint // BE-API-026: Verify GET /api/v1/tracks/quota/:id returns correct upload quota info func TestTrackQuotaEndpoint_GetQuota(t *testing.T) { ctx := context.Background() gin.SetMode(gin.TestMode) // Setup PostgreSQL via testcontainers dsn, err := testutils.GetTestContainerDB(ctx) if err != nil { t.Skipf("Skipping test: PostgreSQL testcontainer not available: %v", err) return } db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) require.NoError(t, err) // Create test user userID := uuid.New() userIDShort := userID.String()[:8] usernameSafe := fmt.Sprintf("test_%s", userIDShort) user := &models.User{ ID: userID, Email: fmt.Sprintf("test_quota_%s@example.com", userIDShort), Username: usernameSafe, IsActive: true, } require.NoError(t, db.Create(user).Error) logger := zap.NewNop() uploadDir := t.TempDir() trackService := track.NewTrackService(db, logger, uploadDir) trackUploadService := services.NewTrackUploadService(db, logger) chunkService := services.NewTrackChunkService(uploadDir+"/chunks", nil, logger) likeService := services.NewTrackLikeService(db, logger) streamService := services.NewStreamService("", logger) trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService) // Setup router router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) api := router.Group("/api/v1") api.GET("/tracks/quota/:id", trackHandler.GetUploadQuota) // Test 1: Get quota for user with no tracks (empty quota) req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/tracks/quota/%s", userID.String()), nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code, "Should return 200 OK: %s", w.Body.String()) var resp map[string]interface{} err = json.Unmarshal(w.Body.Bytes(), &resp) require.NoError(t, err) require.True(t, resp["success"].(bool)) data := resp["data"].(map[string]interface{}) quota := data["quota"].(map[string]interface{}) // Verify initial quota (empty) assert.Equal(t, float64(0), quota["tracks_count"], "Should have 0 tracks initially") assert.Equal(t, float64(1000), quota["tracks_limit"], "Should have limit of 1000 tracks") assert.Equal(t, float64(0), quota["storage_used"], "Should have 0 bytes used initially") assert.Equal(t, float64(100*1024*1024*1024), quota["storage_limit"], "Should have limit of 100GB") // Test 2: Create some tracks and verify quota updates track1 := &models.Track{ ID: uuid.New(), Title: "Test Track 1", UserID: userID, FileSize: 5 * 1024 * 1024, // 5MB IsPublic: true, } require.NoError(t, db.Create(track1).Error) track2 := &models.Track{ ID: uuid.New(), Title: "Test Track 2", UserID: userID, FileSize: 10 * 1024 * 1024, // 10MB IsPublic: true, } require.NoError(t, db.Create(track2).Error) // Get quota again req2 := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/tracks/quota/%s", userID.String()), nil) w2 := httptest.NewRecorder() router.ServeHTTP(w2, req2) require.Equal(t, http.StatusOK, w2.Code) var resp2 map[string]interface{} err = json.Unmarshal(w2.Body.Bytes(), &resp2) require.NoError(t, err) require.True(t, resp2["success"].(bool)) data2 := resp2["data"].(map[string]interface{}) quota2 := data2["quota"].(map[string]interface{}) // Verify updated quota assert.Equal(t, float64(2), quota2["tracks_count"], "Should have 2 tracks") assert.Equal(t, float64(1000), quota2["tracks_limit"], "Should still have limit of 1000 tracks") assert.Equal(t, float64(15*1024*1024), quota2["storage_used"], "Should have 15MB used (5MB + 10MB)") assert.Equal(t, float64(100*1024*1024*1024), quota2["storage_limit"], "Should still have limit of 100GB") } // TestTrackQuotaEndpoint_WithMe tests the endpoint with "me" parameter func TestTrackQuotaEndpoint_WithMe(t *testing.T) { ctx := context.Background() gin.SetMode(gin.TestMode) dsn, err := testutils.GetTestContainerDB(ctx) if err != nil { t.Skipf("Skipping test: PostgreSQL testcontainer not available: %v", err) return } db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) require.NoError(t, err) userID := uuid.New() userIDShort := userID.String()[:8] usernameSafe := fmt.Sprintf("test_%s", userIDShort) user := &models.User{ ID: userID, Email: fmt.Sprintf("test_quota_me_%s@example.com", userIDShort), Username: usernameSafe, IsActive: true, } require.NoError(t, db.Create(user).Error) logger := zap.NewNop() uploadDir := t.TempDir() trackService := track.NewTrackService(db, logger, uploadDir) trackUploadService := services.NewTrackUploadService(db, logger) chunkService := services.NewTrackChunkService(uploadDir+"/chunks", nil, logger) likeService := services.NewTrackLikeService(db, logger) streamService := services.NewStreamService("", logger) trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService) router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) api := router.Group("/api/v1") api.GET("/tracks/quota/:id", trackHandler.GetUploadQuota) // Test with "me" parameter req := httptest.NewRequest("GET", "/api/v1/tracks/quota/me", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code, "Should return 200 OK: %s", w.Body.String()) var resp map[string]interface{} err = json.Unmarshal(w.Body.Bytes(), &resp) require.NoError(t, err) require.True(t, resp["success"].(bool)) data := resp["data"].(map[string]interface{}) quota := data["quota"].(map[string]interface{}) // Verify quota structure assert.NotNil(t, quota["tracks_count"]) assert.NotNil(t, quota["tracks_limit"]) assert.NotNil(t, quota["storage_used"]) assert.NotNil(t, quota["storage_limit"]) } // TestTrackQuotaEndpoint_Unauthorized tests that users cannot see other users' quotas func TestTrackQuotaEndpoint_Unauthorized(t *testing.T) { ctx := context.Background() gin.SetMode(gin.TestMode) dsn, err := testutils.GetTestContainerDB(ctx) if err != nil { t.Skipf("Skipping test: PostgreSQL testcontainer not available: %v", err) return } db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) require.NoError(t, err) // Create two users user1ID := uuid.New() user2ID := uuid.New() user1 := &models.User{ ID: user1ID, Email: fmt.Sprintf("test_user1_%s@example.com", user1ID.String()[:8]), Username: fmt.Sprintf("test_user1_%s", user1ID.String()[:8]), IsActive: true, } user2 := &models.User{ ID: user2ID, Email: fmt.Sprintf("test_user2_%s@example.com", user2ID.String()[:8]), Username: fmt.Sprintf("test_user2_%s", user2ID.String()[:8]), IsActive: true, } require.NoError(t, db.Create(user1).Error) require.NoError(t, db.Create(user2).Error) logger := zap.NewNop() uploadDir := t.TempDir() trackService := track.NewTrackService(db, logger, uploadDir) trackUploadService := services.NewTrackUploadService(db, logger) chunkService := services.NewTrackChunkService(uploadDir+"/chunks", nil, logger) likeService := services.NewTrackLikeService(db, logger) streamService := services.NewStreamService("", logger) trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService) // User1 tries to access user2's quota router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", user1ID) // Authenticated as user1 c.Next() }) api := router.Group("/api/v1") api.GET("/tracks/quota/:id", trackHandler.GetUploadQuota) req := httptest.NewRequest("GET", fmt.Sprintf("/api/v1/tracks/quota/%s", user2ID.String()), nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusForbidden, w.Code, "Should return 403 Forbidden for other user's quota") } // TestTrackQuotaEndpoint_InvalidUserID tests that invalid user ID returns 400 func TestTrackQuotaEndpoint_InvalidUserID(t *testing.T) { ctx := context.Background() gin.SetMode(gin.TestMode) dsn, err := testutils.GetTestContainerDB(ctx) if err != nil { t.Skipf("Skipping test: PostgreSQL testcontainer not available: %v", err) return } db, err := gorm.Open(postgres.Open(dsn), &gorm.Config{}) require.NoError(t, err) userID := uuid.New() userIDShort := userID.String()[:8] usernameSafe := fmt.Sprintf("test_%s", userIDShort) user := &models.User{ ID: userID, Email: fmt.Sprintf("test_quota_invalid_%s@example.com", userIDShort), Username: usernameSafe, IsActive: true, } require.NoError(t, db.Create(user).Error) logger := zap.NewNop() uploadDir := t.TempDir() trackService := track.NewTrackService(db, logger, uploadDir) trackUploadService := services.NewTrackUploadService(db, logger) chunkService := services.NewTrackChunkService(uploadDir+"/chunks", nil, logger) likeService := services.NewTrackLikeService(db, logger) streamService := services.NewStreamService("", logger) trackHandler := track.NewTrackHandler(trackService, trackUploadService, chunkService, likeService, streamService) router := gin.New() router.Use(func(c *gin.Context) { c.Set("user_id", userID) c.Next() }) api := router.Group("/api/v1") api.GET("/tracks/quota/:id", trackHandler.GetUploadQuota) // Test with invalid UUID req := httptest.NewRequest("GET", "/api/v1/tracks/quota/invalid-uuid", nil) w := httptest.NewRecorder() router.ServeHTTP(w, req) assert.Equal(t, http.StatusBadRequest, w.Code, "Should return 400 Bad Request for invalid UUID") }