package education import ( "testing" "github.com/google/uuid" ) func TestCourseTableName(t *testing.T) { c := Course{} if c.TableName() != "courses" { t.Errorf("expected courses, got %s", c.TableName()) } } func TestLessonTableName(t *testing.T) { l := Lesson{} if l.TableName() != "lessons" { t.Errorf("expected lessons, got %s", l.TableName()) } } func TestCourseEnrollmentTableName(t *testing.T) { e := CourseEnrollment{} if e.TableName() != "course_enrollments" { t.Errorf("expected course_enrollments, got %s", e.TableName()) } } func TestLessonProgressTableName(t *testing.T) { p := LessonProgress{} if p.TableName() != "lesson_progress" { t.Errorf("expected lesson_progress, got %s", p.TableName()) } } func TestCertificateTableName(t *testing.T) { cert := Certificate{} if cert.TableName() != "certificates" { t.Errorf("expected certificates, got %s", cert.TableName()) } } func TestCourseReviewTableName(t *testing.T) { r := CourseReview{} if r.TableName() != "course_reviews" { t.Errorf("expected course_reviews, got %s", r.TableName()) } } func TestCourseStatusConstants(t *testing.T) { tests := []struct { name string status CourseStatus expected string }{ {"draft", CourseStatusDraft, "draft"}, {"published", CourseStatusPublished, "published"}, {"archived", CourseStatusArchived, "archived"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if string(tt.status) != tt.expected { t.Errorf("expected %s, got %s", tt.expected, string(tt.status)) } }) } } func TestCourseLevelConstants(t *testing.T) { tests := []struct { name string level CourseLevel expected string }{ {"beginner", CourseLevelBeginner, "beginner"}, {"intermediate", CourseLevelIntermediate, "intermediate"}, {"advanced", CourseLevelAdvanced, "advanced"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if string(tt.level) != tt.expected { t.Errorf("expected %s, got %s", tt.expected, string(tt.level)) } }) } } func TestPricingModelConstants(t *testing.T) { tests := []struct { name string model PricingModel expected string }{ {"fixed", PricingFixed, "fixed"}, {"pay_what_you_want", PricingPayWhatYou, "pay_what_you_want"}, {"free", PricingFree, "free"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if string(tt.model) != tt.expected { t.Errorf("expected %s, got %s", tt.expected, string(tt.model)) } }) } } func TestTranscodingStatusConstants(t *testing.T) { tests := []struct { name string status TranscodingStatus expected string }{ {"pending", TranscodingPending, "pending"}, {"processing", TranscodingProcessing, "processing"}, {"complete", TranscodingComplete, "complete"}, {"failed", TranscodingFailed, "failed"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if string(tt.status) != tt.expected { t.Errorf("expected %s, got %s", tt.expected, string(tt.status)) } }) } } func TestServiceErrors(t *testing.T) { tests := []struct { name string err error msg string }{ {"course not found", ErrCourseNotFound, "course not found"}, {"lesson not found", ErrLessonNotFound, "lesson not found"}, {"enrollment not found", ErrEnrollmentNotFound, "enrollment not found"}, {"already enrolled", ErrAlreadyEnrolled, "already enrolled in this course"}, {"not enrolled", ErrNotEnrolled, "not enrolled in this course"}, {"course not published", ErrCourseNotPublished, "course is not published"}, {"not course owner", ErrNotCourseOwner, "you are not the owner of this course"}, {"review exists", ErrReviewAlreadyExists, "you have already reviewed this course"}, {"invalid rating", ErrInvalidRating, "rating must be between 1 and 5"}, {"certificate not found", ErrCertificateNotFound, "certificate not found"}, {"course not completed", ErrCourseNotCompleted, "course is not fully completed"}, {"certificate exists", ErrCertificateExists, "certificate already issued for this course"}, {"cannot enroll own", ErrCannotEnrollOwnCourse, "cannot enroll in your own course"}, {"slug taken", ErrSlugAlreadyTaken, "slug is already taken"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if tt.err.Error() != tt.msg { t.Errorf("expected %q, got %q", tt.msg, tt.err.Error()) } }) } } func TestGenerateSlug(t *testing.T) { tests := []struct { input string expected string }{ {"My First Course", "my-first-course"}, {"Introduction à la Musique", "introduction--la-musique"}, {"Hello World 123", "hello-world-123"}, {" spaces ", "spaces"}, } for _, tt := range tests { t.Run(tt.input, func(t *testing.T) { got := generateSlug(tt.input) if got == "" { t.Error("slug should not be empty") } }) } } func TestNewService(t *testing.T) { svc := NewService(nil, nil) if svc == nil { t.Fatal("expected non-nil service") } } func TestCourseFields(t *testing.T) { course := Course{ ID: uuid.New(), CreatorID: uuid.New(), Title: "Test Course", Slug: "test-course", PriceCents: 1999, Currency: "USD", Status: CourseStatusDraft, Level: CourseLevelBeginner, Language: "en", } if course.Title != "Test Course" { t.Errorf("expected Test Course, got %s", course.Title) } if course.PriceCents != 1999 { t.Errorf("expected 1999, got %d", course.PriceCents) } if course.Status != CourseStatusDraft { t.Errorf("expected draft, got %s", course.Status) } } func TestLessonFields(t *testing.T) { lesson := Lesson{ ID: uuid.New(), CourseID: uuid.New(), OrderIndex: 1, Title: "Lesson 1", DurationSeconds: 600, IsPreviewFree: true, TranscodingStatus: TranscodingPending, } if lesson.OrderIndex != 1 { t.Errorf("expected 1, got %d", lesson.OrderIndex) } if !lesson.IsPreviewFree { t.Error("expected is_preview_free to be true") } if lesson.DurationSeconds != 600 { t.Errorf("expected 600, got %d", lesson.DurationSeconds) } } func TestEnrollmentFields(t *testing.T) { enrollment := CourseEnrollment{ ID: uuid.New(), UserID: uuid.New(), CourseID: uuid.New(), PurchasedPriceCents: 1999, Currency: "USD", Status: EnrollmentActive, } if enrollment.PurchasedPriceCents != 1999 { t.Errorf("expected 1999, got %d", enrollment.PurchasedPriceCents) } if enrollment.Status != EnrollmentActive { t.Errorf("expected active, got %s", enrollment.Status) } } func TestCertificateFields(t *testing.T) { cert := Certificate{ ID: uuid.New(), CertificateCode: "VEZA-CERT-abc12345-def67890", Status: CertificateActive, } if cert.Status != CertificateActive { t.Errorf("expected active, got %s", cert.Status) } if cert.CertificateCode == "" { t.Error("expected non-empty certificate code") } } func TestReviewFields(t *testing.T) { review := CourseReview{ ID: uuid.New(), Rating: 4, Title: "Great course", Content: "Loved it!", Status: ReviewApproved, } if review.Rating != 4 { t.Errorf("expected 4, got %d", review.Rating) } if review.Status != ReviewApproved { t.Errorf("expected approved, got %s", review.Status) } } func TestProgressPercentageClamping(t *testing.T) { tests := []struct { input int expected int }{ {-10, 0}, {0, 0}, {50, 50}, {100, 100}, {150, 100}, } for _, tt := range tests { clamped := tt.input if clamped < 0 { clamped = 0 } if clamped > 100 { clamped = 100 } if clamped != tt.expected { t.Errorf("clamp(%d) = %d, want %d", tt.input, clamped, tt.expected) } } } func TestRatingValidation(t *testing.T) { tests := []struct { rating int isValid bool }{ {0, false}, {1, true}, {3, true}, {5, true}, {6, false}, {-1, false}, } for _, tt := range tests { valid := tt.rating >= 1 && tt.rating <= 5 if valid != tt.isValid { t.Errorf("rating %d: expected valid=%v, got %v", tt.rating, tt.isValid, valid) } } } func TestUploadLessonVideoResult(t *testing.T) { result := UploadLessonVideoResult{ LessonID: uuid.New(), VideoFilePath: "/tmp/test/video.mp4", Status: string(TranscodingPending), } if result.LessonID == uuid.Nil { t.Error("expected non-nil lesson ID") } if result.VideoFilePath == "" { t.Error("expected non-empty video path") } if result.Status != "pending" { t.Errorf("expected pending, got %s", result.Status) } } func TestBeforeCreateUUIDs(t *testing.T) { // All models should generate UUID on create if nil models := []struct { name string fn func() uuid.UUID }{ {"Course", func() uuid.UUID { c := &Course{} c.BeforeCreate(nil) return c.ID }}, {"Lesson", func() uuid.UUID { l := &Lesson{} l.BeforeCreate(nil) return l.ID }}, {"CourseEnrollment", func() uuid.UUID { e := &CourseEnrollment{} e.BeforeCreate(nil) return e.ID }}, {"LessonProgress", func() uuid.UUID { p := &LessonProgress{} p.BeforeCreate(nil) return p.ID }}, {"Certificate", func() uuid.UUID { c := &Certificate{} c.BeforeCreate(nil) return c.ID }}, {"CourseReview", func() uuid.UUID { r := &CourseReview{} r.BeforeCreate(nil) return r.ID }}, } for _, m := range models { t.Run(m.name, func(t *testing.T) { id := m.fn() if id == uuid.Nil { t.Errorf("%s: expected non-nil UUID after BeforeCreate", m.name) } }) } }