package lmsprogress import ( "testing" "Yimaru-Backend/internal/domain" ) func TestLMSProgressCounts(t *testing.T) { tests := []struct { name string completed int32 total int32 isCompleted bool wantCompleted int wantTotal int wantPercent int wantPercentFloat float64 }{ { name: "fractional progress rounds to two decimals", completed: 1, total: 3, wantCompleted: 1, wantTotal: 3, wantPercent: 33, wantPercentFloat: 33.33, }, { name: "larger fraction rounds precisely", completed: 2, total: 3, wantCompleted: 2, wantTotal: 3, wantPercent: 66, wantPercentFloat: 66.67, }, { name: "completed forces full progress", completed: 2, total: 3, isCompleted: true, wantCompleted: 3, wantTotal: 3, wantPercent: 100, wantPercentFloat: 100, }, { name: "empty scope stays zeroed", wantCompleted: 0, wantTotal: 0, wantPercent: 0, wantPercentFloat: 0, }, { name: "negative counts are sanitized", completed: -2, total: -5, wantCompleted: 0, wantTotal: 0, wantPercent: 0, wantPercentFloat: 0, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotCompleted, gotTotal, gotPercent, gotPercentFloat := lmsProgressCounts(tt.completed, tt.total, tt.isCompleted) if gotCompleted != tt.wantCompleted || gotTotal != tt.wantTotal { t.Fatalf("counts=(%d,%d), want (%d,%d)", gotCompleted, gotTotal, tt.wantCompleted, tt.wantTotal) } if gotPercent != tt.wantPercent { t.Fatalf("progress_percent=%d, want %d", gotPercent, tt.wantPercent) } if gotPercentFloat != tt.wantPercentFloat { t.Fatalf("progress_percent_precise=%v, want %v", gotPercentFloat, tt.wantPercentFloat) } }) } } func TestPracticeScopeFraction(t *testing.T) { fraction, done, completed, total := practiceScopeFraction(1, 5) if fraction != 0.2 || done || completed != 1 || total != 5 { t.Fatalf("practiceScopeFraction(1,5)=(%v,%v,%d,%d), want (0.2,false,1,5)", fraction, done, completed, total) } fraction, done, completed, total = practiceScopeFraction(5, 5) if fraction != 1 || !done || completed != 5 || total != 5 { t.Fatalf("practiceScopeFraction(5,5)=(%v,%v,%d,%d), want (1,true,5,5)", fraction, done, completed, total) } fraction, done, completed, total = practiceScopeFraction(0, 0) if fraction != 0 || done || completed != 0 || total != 0 { t.Fatalf("practiceScopeFraction(0,0)=(%v,%v,%d,%d), want (0,false,0,0)", fraction, done, completed, total) } } func TestFinalizeOpenLearnerAccess(t *testing.T) { incomplete := buildLMSEntityAccessFromFraction(false, "locked", false, 1, 5, 0.2) got := finalizeOpenLearnerAccess(domain.RoleOpenLearner, incomplete) if got == nil || !got.IsAccessible || !got.IsCompleted { t.Fatalf("expected accessible and completed, got %+v", got) } if got.CompletedCount != 5 || got.ProgressPercent != 100 { t.Fatalf("expected full progress, got %+v", got) } studentAccess := buildLMSEntityAccessFromFraction(false, "locked", false, 1, 5, 0.2) unchanged := finalizeOpenLearnerAccess(domain.RoleStudent, studentAccess) if unchanged != studentAccess || unchanged.IsCompleted || unchanged.IsAccessible { t.Fatalf("STUDENT access should be unchanged, got %+v", unchanged) } emptyScope := finalizeOpenLearnerAccess(domain.RoleOpenLearner, buildLMSEntityAccessFromFraction(true, "", false, 0, 0, 0)) if emptyScope == nil || !emptyScope.IsCompleted { t.Fatalf("expected completed even with zero total, got %+v", emptyScope) } } func TestLMSProgressComplete(t *testing.T) { tests := []struct { name string completed int32 total int32 want bool }{ {name: "complete when all practices done", completed: 3, total: 3, want: true}, {name: "incomplete when practices remain", completed: 2, total: 3, want: false}, {name: "zero total is not completed", completed: 0, total: 0, want: false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { if got := lmsProgressComplete(tt.completed, tt.total); got != tt.want { t.Fatalf("lmsProgressComplete(%d, %d)=%v, want %v", tt.completed, tt.total, got, tt.want) } }) } }