176 lines
4.3 KiB
Go
176 lines
4.3 KiB
Go
package questiongen
|
|
|
|
import (
|
|
"reflect"
|
|
"regexp"
|
|
"strconv"
|
|
"testing"
|
|
|
|
"boostai-backend/internal/sqlc"
|
|
)
|
|
|
|
func TestServiceGenerateDeterministicWithSeed(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
service := NewService()
|
|
params := GenerateParams{
|
|
Topic: sqlc.QuestionTopicFractions,
|
|
Difficulty: sqlc.QuestionDifficultyMedium,
|
|
Count: 3,
|
|
Seed: 123456,
|
|
}
|
|
|
|
first, firstSeed, err := service.Generate(params)
|
|
if err != nil {
|
|
t.Fatalf("first generate returned error: %v", err)
|
|
}
|
|
|
|
second, secondSeed, err := service.Generate(params)
|
|
if err != nil {
|
|
t.Fatalf("second generate returned error: %v", err)
|
|
}
|
|
|
|
if firstSeed != params.Seed || secondSeed != params.Seed {
|
|
t.Fatalf("expected seed %d to be reused, got %d and %d", params.Seed, firstSeed, secondSeed)
|
|
}
|
|
|
|
if !reflect.DeepEqual(first, second) {
|
|
t.Fatalf("expected deterministic output for identical seed\nfirst: %#v\nsecond: %#v", first, second)
|
|
}
|
|
}
|
|
|
|
func TestServiceGenerateSupportsAllTopicsAndDifficulties(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
service := NewService()
|
|
topics := []sqlc.QuestionTopic{
|
|
sqlc.QuestionTopicPlaceValue,
|
|
sqlc.QuestionTopicArithmetic,
|
|
sqlc.QuestionTopicNegativeNumbers,
|
|
sqlc.QuestionTopicBidmas,
|
|
sqlc.QuestionTopicFractions,
|
|
sqlc.QuestionTopicAlgebra,
|
|
sqlc.QuestionTopicGeometry,
|
|
sqlc.QuestionTopicData,
|
|
}
|
|
difficulties := []sqlc.QuestionDifficulty{
|
|
sqlc.QuestionDifficultyEasy,
|
|
sqlc.QuestionDifficultyMedium,
|
|
sqlc.QuestionDifficultyHard,
|
|
}
|
|
|
|
for _, topic := range topics {
|
|
topic := topic
|
|
for _, difficulty := range difficulties {
|
|
difficulty := difficulty
|
|
t.Run(string(topic)+"_"+string(difficulty), func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
items, usedSeed, err := service.Generate(GenerateParams{
|
|
Topic: topic,
|
|
Difficulty: difficulty,
|
|
Count: 2,
|
|
Seed: 99,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("generate returned error: %v", err)
|
|
}
|
|
if usedSeed != 99 {
|
|
t.Fatalf("expected used seed 99, got %d", usedSeed)
|
|
}
|
|
if len(items) != 2 {
|
|
t.Fatalf("expected 2 generated questions, got %d", len(items))
|
|
}
|
|
|
|
for i, item := range items {
|
|
if item.Title == "" {
|
|
t.Fatalf("item %d: title should not be empty", i)
|
|
}
|
|
if item.Prompt == "" {
|
|
t.Fatalf("item %d: prompt should not be empty", i)
|
|
}
|
|
if item.CorrectAnswer == "" {
|
|
t.Fatalf("item %d: correct answer should not be empty", i)
|
|
}
|
|
if len(item.WorkedSolution) == 0 {
|
|
t.Fatalf("item %d: worked solution should not be empty", i)
|
|
}
|
|
assertContainsTag(t, item.Tags, string(topic))
|
|
assertContainsTag(t, item.Tags, string(difficulty))
|
|
assertContainsTag(t, item.Tags, "rng_generated")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestFractionsEasyUsesSingleDigitPromptValues(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
service := NewService()
|
|
items, _, err := service.Generate(GenerateParams{
|
|
Topic: sqlc.QuestionTopicFractions,
|
|
Difficulty: sqlc.QuestionDifficultyEasy,
|
|
Count: 20,
|
|
Seed: 20260522,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("generate returned error: %v", err)
|
|
}
|
|
|
|
for i, item := range items {
|
|
values := extractIntegers(item.Prompt)
|
|
if len(values) != 3 {
|
|
t.Fatalf("item %d: expected 3 integers in prompt, got %v from %q", i, values, item.Prompt)
|
|
}
|
|
|
|
for _, value := range values {
|
|
if value < 0 || value > 9 {
|
|
t.Fatalf("item %d: expected easy fraction prompt values to be single-digit, got %d in %q", i, value, item.Prompt)
|
|
}
|
|
}
|
|
|
|
assertContainsTag(t, item.Tags, "single_digit")
|
|
}
|
|
}
|
|
|
|
func TestServiceGenerateRejectsUnsupportedTopic(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
service := NewService()
|
|
_, _, err := service.Generate(GenerateParams{
|
|
Topic: sqlc.QuestionTopic("unsupported_topic"),
|
|
Difficulty: sqlc.QuestionDifficultyEasy,
|
|
Count: 1,
|
|
Seed: 1,
|
|
})
|
|
if err == nil {
|
|
t.Fatal("expected unsupported topic to return an error")
|
|
}
|
|
}
|
|
|
|
func assertContainsTag(t *testing.T, tags []string, want string) {
|
|
t.Helper()
|
|
for _, tag := range tags {
|
|
if tag == want {
|
|
return
|
|
}
|
|
}
|
|
t.Fatalf("expected tags %v to contain %q", tags, want)
|
|
}
|
|
|
|
var integerPattern = regexp.MustCompile(`-?\d+`)
|
|
|
|
func extractIntegers(input string) []int {
|
|
matches := integerPattern.FindAllString(input, -1)
|
|
values := make([]int, 0, len(matches))
|
|
for _, match := range matches {
|
|
value, err := strconv.Atoi(match)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
values = append(values, value)
|
|
}
|
|
return values
|
|
}
|