- graders registry collected at boot from every HasGraders plugin, manual first; a duplicate or reserved code fails boot - grading job on queue quizzes_grading with three attempts; a stale or reviewed answer is never touched; the last failure marks it failed - learning answers enqueue at once, tests at close; a grade recalculates a finished attempt and fires AnswerGradedEvent after commit
75 lines
2.6 KiB
Go
75 lines
2.6 KiB
Go
package classes_test
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/sm-quizzes-plugin/classes"
|
|
"git.golem15.com/golem15/sm-quizzes-plugin/graders"
|
|
"git.golem15.com/golem15/sm-quizzes-plugin/models"
|
|
)
|
|
|
|
type stubGrader struct{ code string }
|
|
|
|
func (g stubGrader) Code() string { return g.code }
|
|
func (g stubGrader) Label() string { return g.code }
|
|
func (g stubGrader) Grade(context.Context, graders.Input) (graders.Result, error) {
|
|
return graders.Result{}, nil
|
|
}
|
|
|
|
func TestGraderFor(t *testing.T) {
|
|
reg := graders.NewRegistry()
|
|
if err := reg.Add("acme.x", stubGrader{"x"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if g, ok := classes.GraderFor(reg, &models.Quiz{Grader: "x"}); !ok || g.Code() != "x" {
|
|
t.Errorf("GraderFor(x) = %v, %v", g, ok)
|
|
}
|
|
for _, code := range []string{graders.Manual, "unknown", ""} {
|
|
if _, ok := classes.GraderFor(reg, &models.Quiz{Grader: code}); ok {
|
|
t.Errorf("GraderFor(%q) = true, want false", code)
|
|
}
|
|
}
|
|
if _, ok := classes.GraderFor(nil, &models.Quiz{Grader: "x"}); ok {
|
|
t.Error("GraderFor(nil registry) = true")
|
|
}
|
|
if _, ok := classes.GraderFor(reg, nil); ok {
|
|
t.Error("GraderFor(nil quiz) = true")
|
|
}
|
|
}
|
|
|
|
func TestNextGrading(t *testing.T) {
|
|
str := func(s string) *string { return &s }
|
|
boom := errors.New("boom")
|
|
cases := []struct {
|
|
name string
|
|
status *string
|
|
rowSeq int
|
|
jobSeq int
|
|
attempts int
|
|
err error
|
|
want classes.GradingStep
|
|
}{
|
|
{"nil status", nil, 1, 1, 1, nil, classes.GradingSkip},
|
|
{"graded", str(models.GradingGraded), 1, 1, 1, nil, classes.GradingSkip},
|
|
{"failed", str(models.GradingFailed), 1, 1, 1, nil, classes.GradingSkip},
|
|
{"reviewed", str(models.GradingReviewed), 1, 1, 1, nil, classes.GradingSkip},
|
|
{"stale seq", str(models.GradingPending), 2, 1, 1, nil, classes.GradingSkip},
|
|
{"stale seq with error", str(models.GradingPending), 2, 1, 3, boom, classes.GradingSkip},
|
|
{"store", str(models.GradingPending), 1, 1, 1, nil, classes.GradingStore},
|
|
{"retry 1", str(models.GradingPending), 1, 1, 1, boom, classes.GradingRetry},
|
|
{"retry 2", str(models.GradingPending), 1, 1, 2, boom, classes.GradingRetry},
|
|
{"fail at max", str(models.GradingPending), 1, 1, classes.GradeMaxAttempts, boom, classes.GradingFail},
|
|
{"fail past max", str(models.GradingPending), 1, 1, classes.GradeMaxAttempts + 1, boom, classes.GradingFail},
|
|
}
|
|
for _, tc := range cases {
|
|
if got := classes.NextGrading(tc.status, tc.rowSeq, tc.jobSeq, tc.attempts, tc.err); got != tc.want {
|
|
t.Errorf("%s: NextGrading = %v, want %v", tc.name, got, tc.want)
|
|
}
|
|
}
|
|
if classes.GradeMaxAttempts != 3 {
|
|
t.Errorf("GradeMaxAttempts = %d, want 3", classes.GradeMaxAttempts)
|
|
}
|
|
}
|