package assignmentgen import ( "fmt" "testing" "boostai-backend/internal/sqlc" "github.com/jackc/pgx/v5/pgtype" ) func TestBuildWeaknessSummaryAggregatesSignals(t *testing.T) { rows := []sqlc.ListStudentPlanningPerformanceRow{ { Topic: sqlc.NullQuestionTopic{QuestionTopic: sqlc.QuestionTopicFractions, Valid: true}, QuestionTags: []string{"fractions", "simplify"}, IsCorrect: pgtype.Bool{Bool: false, Valid: true}, ReviewUnderstandingScore: mustNumeric(t, 0.2), ReviewNeedsAttention: true, ReviewIssueReason: pgtype.Text{String: "Missed simplification", Valid: true}, }, { Topic: sqlc.NullQuestionTopic{QuestionTopic: sqlc.QuestionTopicGeometry, Valid: true}, QuestionTags: []string{"geometry", "angles"}, IsCorrect: pgtype.Bool{Bool: true, Valid: true}, ReviewUnderstandingScore: mustNumeric(t, 0.9), ReviewIssueReason: pgtype.Text{String: "Explained angle sum well", Valid: true}, }, } summary := buildWeaknessSummary(rows) if got := summary.TopicScores[sqlc.QuestionTopicFractions]; got != 10 { t.Fatalf("expected fractions score 10, got %v", got) } if got := summary.TopicScores[sqlc.QuestionTopicGeometry]; got != 95 { t.Fatalf("expected geometry score 95, got %v", got) } if len(summary.WeakTags) == 0 || summary.WeakTags[0] != "fractions" { t.Fatalf("expected fractions weak tag to rank first, got %#v", summary.WeakTags) } if len(summary.RecentIssues) != 2 { t.Fatalf("expected 2 recent issues, got %d", len(summary.RecentIssues)) } } func TestSelectPersonalizedTopicPrefersWeakestNonPrimary(t *testing.T) { summary := WeaknessSummary{ TopicScores: map[sqlc.QuestionTopic]float64{ sqlc.QuestionTopicFractions: 35, sqlc.QuestionTopicGeometry: 82, sqlc.QuestionTopicAlgebra: 61, }, } topic, ok := selectPersonalizedTopic(sqlc.QuestionTopicGeometry, summary) if !ok { t.Fatal("expected personalized topic to be selected") } if topic != sqlc.QuestionTopicFractions { t.Fatalf("expected fractions, got %q", topic) } } func TestCalculatePersonalizedCountUsesSafeSplit(t *testing.T) { if got := calculatePersonalizedCount(10, 0.3, true); got != 3 { t.Fatalf("expected 3 personalized questions, got %d", got) } if got := calculatePersonalizedCount(2, 0.3, true); got != 0 { t.Fatalf("expected 0 personalized questions for tiny set, got %d", got) } if got := calculatePersonalizedCount(5, 0.3, false); got != 0 { t.Fatalf("expected 0 personalized questions without weakness topic, got %d", got) } } func mustNumeric(t *testing.T, value float64) pgtype.Numeric { t.Helper() var numeric pgtype.Numeric if err := numeric.ScanScientific(fmt.Sprintf("%f", value)); err != nil { t.Fatalf("failed to build numeric: %v", err) } return numeric }