package learning import ( "context" "encoding/json" "errors" "testing" "time" "github.com/gooseek/backend/internal/llm" ) func TestSanitizeSlug(t *testing.T) { got := sanitizeSlug(" Go Backend: Production _ Course ") if got != "go-backend-production-course" { t.Fatalf("sanitizeSlug result mismatch: %q", got) } } func TestNormalizeDifficulty(t *testing.T) { if normalizeDifficulty("advanced") != "advanced" { t.Fatalf("expected advanced difficulty") } if normalizeDifficulty("unknown") != "intermediate" { t.Fatalf("expected fallback to intermediate") } } func TestValidateCourseArtifacts(t *testing.T) { outline := json.RawMessage(`{ "modules": [ {"index":0,"title":"m1","description":"d","skills":["a"],"estimated_hours":4,"practice_focus":"p"}, {"index":1,"title":"m2","description":"d","skills":["a"],"estimated_hours":4,"practice_focus":"p"}, {"index":2,"title":"m3","description":"d","skills":["a"],"estimated_hours":4,"practice_focus":"p"}, {"index":3,"title":"m4","description":"d","skills":["a"],"estimated_hours":4,"practice_focus":"p"}, {"index":4,"title":"m5","description":"d","skills":["a"],"estimated_hours":4,"practice_focus":"p"}, {"index":5,"title":"m6","description":"d","skills":["a"],"estimated_hours":4,"practice_focus":"p"}, {"index":6,"title":"m7","description":"d","skills":["a"],"estimated_hours":4,"practice_focus":"p"}, {"index":7,"title":"m8","description":"d","skills":["a"],"estimated_hours":4,"practice_focus":"p"} ] }`) landing := json.RawMessage(`{ "hero_title":"Hero", "hero_subtitle":"Subtitle", "benefits":["b1","b2","b3"], "outcomes":["o1","o2"], "salary_range":"200k", "faq":[{"question":"q","answer":"a"}] }`) err := validateCourseArtifacts("Course", "Это достаточно длинное описание для валидации курса в тесте.", outline, landing) if err != nil { t.Fatalf("validateCourseArtifacts unexpected error: %v", err) } } func TestGenerateTextWithRetry(t *testing.T) { attempt := 0 client := &mockLLMClient{ generateFunc: func(ctx context.Context, req llm.StreamRequest) (string, error) { attempt++ if attempt < 3 { return "", errors.New("temporary") } return "ok", nil }, } got, err := generateTextWithRetry(context.Background(), client, llm.StreamRequest{}, 3, time.Millisecond) if err != nil { t.Fatalf("generateTextWithRetry error: %v", err) } if got != "ok" { t.Fatalf("unexpected result: %q", got) } if attempt != 3 { t.Fatalf("expected 3 attempts, got %d", attempt) } }