veza/veza-backend-api/internal/core/education/service_test.go

393 lines
9.3 KiB
Go
Raw Normal View History

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)
}
})
}
}