unique quiz constraint
This commit is contained in:
@@ -3,8 +3,11 @@ package service
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/google/uuid"
|
||||
|
||||
"github.com/prabhupadasays/spsays-bff/internal/model"
|
||||
"github.com/prabhupadasays/spsays-bff/internal/repository"
|
||||
)
|
||||
@@ -318,3 +321,64 @@ func TestNormalizeContentPath(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A chapter, lecture or conversation carries at most one live quiz. The
|
||||
// service names the quiz already there; the unique index catches a race;
|
||||
// deleting a quiz frees its link; stand-alone quizzes are unlimited.
|
||||
func TestQuiz_OnePerTarget(t *testing.T) {
|
||||
svc, pg := newQuizSvc(t)
|
||||
ctx := context.Background()
|
||||
admin := chatUser(t, pg, "Admin")
|
||||
repo := repository.NewQuizRepo(pg)
|
||||
|
||||
path := "/books/zz-test/" + uuid.NewString()[:8]
|
||||
in := repository.QuizInput{
|
||||
Title: "First", TargetKind: model.QuizChapter, TargetPath: &path,
|
||||
Questions: []repository.QuestionInput{q("p", 0, "a", "b")},
|
||||
}
|
||||
first, err := repo.Create(ctx, in, admin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanupQuiz(t, pg, first)
|
||||
|
||||
// The friendly pre-check names the quiz that holds the link…
|
||||
err = svc.ensureTargetFree(ctx, in, "")
|
||||
if !errors.Is(err, repository.ErrQuizTargetTaken) || !strings.Contains(err.Error(), `"First"`) {
|
||||
t.Errorf("second quiz on a link: %v, want ErrQuizTargetTaken naming \"First\"", err)
|
||||
}
|
||||
// …but a quiz re-saving its own link is fine.
|
||||
if err := svc.ensureTargetFree(ctx, in, first); err != nil {
|
||||
t.Errorf("re-saving the same quiz: %v", err)
|
||||
}
|
||||
// The index refuses it even if the check is skipped (two saves racing).
|
||||
in.Title = "Second"
|
||||
if _, err := repo.Create(ctx, in, admin); !errors.Is(err, repository.ErrQuizTargetTaken) {
|
||||
t.Errorf("create past the index: %v, want ErrQuizTargetTaken", err)
|
||||
}
|
||||
// Moving another quiz onto the link is refused too.
|
||||
other := repository.QuizInput{Title: "Other", TargetKind: model.QuizStandalone,
|
||||
Questions: []repository.QuestionInput{q("p", 0, "a", "b")}}
|
||||
otherID, err := repo.Create(ctx, other, admin)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
cleanupQuiz(t, pg, otherID)
|
||||
other.TargetKind, other.TargetPath = model.QuizChapter, &path
|
||||
if err := repo.Update(ctx, otherID, other, false); !errors.Is(err, repository.ErrQuizTargetTaken) {
|
||||
t.Errorf("moving a quiz onto a taken link: %v, want ErrQuizTargetTaken", err)
|
||||
}
|
||||
// Stand-alone quizzes are unlimited.
|
||||
if solo, err := svc.Create(ctx, admin, QuizDraft{Title: "Solo", Questions: []repository.QuestionInput{q("p", 0, "a", "b")}}); err != nil {
|
||||
t.Errorf("a second stand-alone quiz: %v", err)
|
||||
} else {
|
||||
cleanupQuiz(t, pg, solo.ID)
|
||||
}
|
||||
// Deleting the first frees the link.
|
||||
if err := svc.Delete(ctx, first); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := repo.Update(ctx, otherID, other, false); err != nil {
|
||||
t.Errorf("taking a freed link: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user