Ctrl K
ComunidadGuías…

internal/handler/performance_handler_test.go

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
package handler

import (
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/johnzastrow/actalog/internal/domain"
	"github.com/johnzastrow/actalog/internal/repository"
)

// createTestPerformanceHandler creates a performance handler with test database and sample data
func createTestPerformanceHandler(t *testing.T) (*PerformanceHandler, func()) {
	db, cleanup, err := repository.SetupTestDB()
	if err != nil {
		t.Fatalf("Failed to setup test database: %v", err)
	}

	// Create test user
	userRepo := repository.NewSQLiteUserRepository(db)
	now := time.Now()
	testUser := &domain.User{
		Email:        "perftest@example.com",
		PasswordHash: "hashedpassword",
		Role:         "athlete",
		CreatedAt:    now,
		UpdatedAt:    now,
	}
	if err := userRepo.Create(testUser); err != nil {
		cleanup()
		t.Fatalf("Failed to create test user: %v", err)
	}

	// Create repositories
	movementRepo := repository.NewMovementRepository(db)
	wodRepo := repository.NewWODRepository(db)
	userWorkoutMovementRepo := repository.NewUserWorkoutMovementRepository(db)
	userWorkoutWODRepo := repository.NewUserWorkoutWODRepository(db)

	// Create test movement
	movement := &domain.Movement{
		Name:        "Back Squat",
		Type:        "weightlifting",
		Description: "Barbell back squat",
		CreatedAt:   now,
		UpdatedAt:   now,
	}
	if err := movementRepo.Create(movement); err != nil {
		cleanup()
		t.Fatalf("Failed to create movement: %v", err)
	}

	// Create test WOD
	createdBy := int64(1)
	wod := &domain.WOD{
		Name:        "Fran",
		Type:        "For Time",
		ScoreType:   "time",
		Description: "21-15-9 Thrusters and Pull-ups",
		CreatedBy:   &createdBy,
		CreatedAt:   now,
		UpdatedAt:   now,
	}
	if err := wodRepo.Create(wod); err != nil {
		cleanup()
		t.Fatalf("Failed to create WOD: %v", err)
	}

	// Create a workout
	_, err = db.Exec(`
		INSERT INTO workouts (name, notes, created_by, created_at, updated_at)
		VALUES ('Test Workout', 'Test notes', 1, ?, ?)
	`, now, now)
	if err != nil {
		cleanup()
		t.Fatalf("Failed to create workout: %v", err)
	}

	// Create a user workout
	_, err = db.Exec(`
		INSERT INTO user_workouts (user_id, workout_id, workout_date, created_at, updated_at)
		VALUES (1, 1, ?, ?, ?)
	`, now.Format("2006-01-02"), now, now)
	if err != nil {
		cleanup()
		t.Fatalf("Failed to create user workout: %v", err)
	}

	// Create workout movement performance
	_, err = db.Exec(`
		INSERT INTO workout_movements (workout_id, movement_id, sets, reps, weight, is_pr, order_index, created_at, updated_at)
		VALUES (1, 1, 5, 5, 315.0, 1, 0, ?, ?)
	`, now, now)
	if err != nil {
		cleanup()
		t.Fatalf("Failed to create workout movement: %v", err)
	}

	// Create workout WOD performance
	_, err = db.Exec(`
		INSERT INTO workout_wods (workout_id, wod_id, score_value, division, is_pr, order_index, created_at, updated_at)
		VALUES (1, 1, '5:30', 'rx', 1, 0, ?, ?)
	`, now, now)
	if err != nil {
		cleanup()
		t.Fatalf("Failed to create workout WOD: %v", err)
	}

	handler := NewPerformanceHandler(movementRepo, wodRepo, userWorkoutMovementRepo, userWorkoutWODRepo, createTestLogger())
	return handler, cleanup
}

func TestPerformanceHandler_UnifiedSearch_Unauthorized(t *testing.T) {
	handler := &PerformanceHandler{}

	req := createTestRequest(http.MethodGet, "/api/performance/search?q=squat", "")
	rr := httptest.NewRecorder()

	handler.UnifiedSearch(rr, req)

	assertStatusCode(t, rr, http.StatusUnauthorized)
	assertBodyContains(t, rr, "Unauthorized")
}

func TestPerformanceHandler_UnifiedSearch_MissingQuery(t *testing.T) {
	handler := &PerformanceHandler{}

	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/search", "", 1, "test@example.com", "user")
	rr := httptest.NewRecorder()

	handler.UnifiedSearch(rr, req)

	assertStatusCode(t, rr, http.StatusBadRequest)
	assertBodyContains(t, rr, "Search query is required")
}

func TestPerformanceHandler_GetMovementPerformance_Unauthorized(t *testing.T) {
	handler := &PerformanceHandler{}

	req := createTestRequest(http.MethodGet, "/api/performance/movements/1", "")
	rr := httptest.NewRecorder()

	handler.GetMovementPerformance(rr, req)

	assertStatusCode(t, rr, http.StatusUnauthorized)
	assertBodyContains(t, rr, "Unauthorized")
}

func TestPerformanceHandler_GetMovementPerformance_InvalidID(t *testing.T) {
	handler := &PerformanceHandler{}

	// chi.URLParam returns empty string without router context
	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/movements/abc", "", 1, "test@example.com", "user")
	rr := httptest.NewRecorder()

	handler.GetMovementPerformance(rr, req)

	assertStatusCode(t, rr, http.StatusBadRequest)
	assertBodyContains(t, rr, "Invalid movement ID")
}

func TestPerformanceHandler_GetWODPerformance_Unauthorized(t *testing.T) {
	handler := &PerformanceHandler{}

	req := createTestRequest(http.MethodGet, "/api/performance/wods/1", "")
	rr := httptest.NewRecorder()

	handler.GetWODPerformance(rr, req)

	assertStatusCode(t, rr, http.StatusUnauthorized)
	assertBodyContains(t, rr, "Unauthorized")
}

func TestPerformanceHandler_GetWODPerformance_InvalidID(t *testing.T) {
	handler := &PerformanceHandler{}

	// chi.URLParam returns empty string without router context
	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/wods/abc", "", 1, "test@example.com", "user")
	rr := httptest.NewRecorder()

	handler.GetWODPerformance(rr, req)

	assertStatusCode(t, rr, http.StatusBadRequest)
	assertBodyContains(t, rr, "Invalid WOD ID")
}

func TestNewPerformanceHandler(t *testing.T) {
	handler := NewPerformanceHandler(nil, nil, nil, nil, nil)
	if handler == nil {
		t.Error("NewPerformanceHandler should return a non-nil handler")
	}
}

// Success path tests with real database

func TestPerformanceHandler_UnifiedSearch_Success(t *testing.T) {
	handler, cleanup := createTestPerformanceHandler(t)
	defer cleanup()

	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/search?q=squat", "", 1, "perftest@example.com", "user")
	rr := httptest.NewRecorder()

	handler.UnifiedSearch(rr, req)

	assertStatusCode(t, rr, http.StatusOK)
	assertContentType(t, rr, "application/json")

	body := rr.Body.String()
	if !strings.Contains(body, "results") {
		t.Error("Response should contain 'results' field")
	}
	if !strings.Contains(body, "Back Squat") {
		t.Error("Response should contain the movement 'Back Squat'")
	}
}

func TestPerformanceHandler_UnifiedSearch_WithLimit(t *testing.T) {
	handler, cleanup := createTestPerformanceHandler(t)
	defer cleanup()

	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/search?q=squat&limit=5", "", 1, "perftest@example.com", "user")
	rr := httptest.NewRecorder()

	handler.UnifiedSearch(rr, req)

	assertStatusCode(t, rr, http.StatusOK)
}

func TestPerformanceHandler_UnifiedSearch_InvalidLimit(t *testing.T) {
	handler, cleanup := createTestPerformanceHandler(t)
	defer cleanup()

	// Invalid limit should use default
	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/search?q=squat&limit=abc", "", 1, "perftest@example.com", "user")
	rr := httptest.NewRecorder()

	handler.UnifiedSearch(rr, req)

	assertStatusCode(t, rr, http.StatusOK)
}

func TestPerformanceHandler_UnifiedSearch_NoResults(t *testing.T) {
	handler, cleanup := createTestPerformanceHandler(t)
	defer cleanup()

	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/search?q=nonexistent", "", 1, "perftest@example.com", "user")
	rr := httptest.NewRecorder()

	handler.UnifiedSearch(rr, req)

	assertStatusCode(t, rr, http.StatusOK)
	// Should return empty results
	body := rr.Body.String()
	if !strings.Contains(body, "results") {
		t.Error("Response should contain 'results' field")
	}
}

func TestPerformanceHandler_GetMovementPerformance_Success(t *testing.T) {
	handler, cleanup := createTestPerformanceHandler(t)
	defer cleanup()

	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/movements/1", "", 1, "perftest@example.com", "user")
	req = addChiURLParam(req, "id", "1")
	rr := httptest.NewRecorder()

	handler.GetMovementPerformance(rr, req)

	assertStatusCode(t, rr, http.StatusOK)
	assertContentType(t, rr, "application/json")

	body := rr.Body.String()
	if !strings.Contains(body, "performances") {
		t.Error("Response should contain 'performances' field")
	}
	if !strings.Contains(body, "count") {
		t.Error("Response should contain 'count' field")
	}
}

func TestPerformanceHandler_GetMovementPerformance_EmptyResult(t *testing.T) {
	handler, cleanup := createTestPerformanceHandler(t)
	defer cleanup()

	// Non-existent movement returns empty performances (not 404)
	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/movements/9999", "", 1, "perftest@example.com", "user")
	req = addChiURLParam(req, "id", "9999")
	rr := httptest.NewRecorder()

	handler.GetMovementPerformance(rr, req)

	assertStatusCode(t, rr, http.StatusOK)
	body := rr.Body.String()
	if !strings.Contains(body, `"count":0`) {
		t.Error("Response should contain count of 0 for non-existent movement")
	}
}

func TestPerformanceHandler_GetWODPerformance_Success(t *testing.T) {
	handler, cleanup := createTestPerformanceHandler(t)
	defer cleanup()

	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/wods/1", "", 1, "perftest@example.com", "user")
	req = addChiURLParam(req, "id", "1")
	rr := httptest.NewRecorder()

	handler.GetWODPerformance(rr, req)

	assertStatusCode(t, rr, http.StatusOK)
	assertContentType(t, rr, "application/json")

	body := rr.Body.String()
	if !strings.Contains(body, "count") {
		t.Error("Response should contain 'count' field")
	}
}

func TestPerformanceHandler_GetWODPerformance_EmptyResult(t *testing.T) {
	handler, cleanup := createTestPerformanceHandler(t)
	defer cleanup()

	// Non-existent WOD returns empty performances (not 404)
	req := createAuthenticatedRequest(http.MethodGet, "/api/performance/wods/9999", "", 1, "perftest@example.com", "user")
	req = addChiURLParam(req, "id", "9999")
	rr := httptest.NewRecorder()

	handler.GetWODPerformance(rr, req)

	assertStatusCode(t, rr, http.StatusOK)
	body := rr.Body.String()
	if !strings.Contains(body, `"count":0`) {
		t.Error("Response should contain count of 0 for non-existent WOD")
	}
}