package quizzes_test import ( "bytes" "os" "path/filepath" "strings" "testing" "time" "git.golem15.com/golem15/sm-quizzes-plugin/models" "git.golem15.com/golem15/summercms/modules/bonfire" "git.golem15.com/golem15/summercms/modules/pact" ) func TestImportCommand(t *testing.T) { _, plugins, gdb := migrated(t) cmds := quizzesPlugin(t, plugins).(pact.HasCommands).Commands() run := func(args ...string) (string, error) { var out bytes.Buffer err := bonfire.Call(t.Context(), cmds, "quizzes:import", args, &out) return out.String(), err } out, err := run("testdata/quiz.json") if err != nil { t.Fatalf("import: %v\n%s", err, out) } if !strings.Contains(out, "created quiz bezpieczenstwo-pojecia") || !strings.Contains(out, "3 questions, 5 answers") { t.Errorf("output = %q", out) } var quiz models.Quiz must(t, gdb.Preload("Questions.Answers").Where("slug = ?", "bezpieczenstwo-pojecia").Take(&quiz).Error) if !quiz.IsPublished || quiz.PassingScore != 60 || quiz.RoundSize == nil || *quiz.RoundSize != 5 || *quiz.Context != "acme.courses:course:12" { t.Errorf("quiz = %+v", quiz) } if len(quiz.Questions) != 3 { t.Fatalf("questions = %d", len(quiz.Questions)) } byOrder := map[int]models.Question{} for _, q := range quiz.Questions { byOrder[q.SortOrder] = q } if q := byOrder[1]; q.Type != models.TypeSelfCheck || *q.Category != "Teoria" || q.AnswerText == nil || q.Points != 1 { t.Errorf("first question = %+v", q) } if q := byOrder[3]; q.Points != 2 || len(q.Answers) != 3 || *q.Category != "Globalizacja" { t.Errorf("third question = %+v", q) } // Without --replace an existing slug is refused and nothing changes. if _, err := run("testdata/quiz.json"); err == nil || !strings.Contains(err.Error(), "--replace") { t.Errorf("second import without --replace = %v", err) } // --replace recreates the questions; attempt answers to the old ones go // with them. u, _ := newUser(t, gdb, "ala@example.com") a := models.Attempt{QuizID: quiz.ID, UserID: u.ID, StartedAt: time.Now()} must(t, gdb.Create(&a).Error) must(t, gdb.Create(&models.AttemptAnswer{AttemptID: a.ID, QuestionID: byOrder[1].ID}).Error) changed := filepath.Join(t.TempDir(), "quiz.json") raw, _ := os.ReadFile("testdata/quiz.json") raw = bytes.Replace(raw, []byte(`"passing_score": 60`), []byte(`"slug": "bezpieczenstwo-pojecia", "passing_score": 70`), 1) raw = bytes.Replace(raw, []byte(`"Czym jest odporność społeczna?"`), []byte(`"Czym jest obrona totalna?"`), 1) must(t, os.WriteFile(changed, raw, 0o644)) out, err = run(changed, "--replace") if err != nil || !strings.Contains(out, "replaced quiz bezpieczenstwo-pojecia (id ") { t.Fatalf("replace = %v %q", err, out) } var again models.Quiz must(t, gdb.Preload("Questions").Where("slug = ?", "bezpieczenstwo-pojecia").Take(&again).Error) if again.ID != quiz.ID || again.PassingScore != 70 || len(again.Questions) != 3 { t.Errorf("replaced quiz = %+v", again) } var n int64 must(t, gdb.Model(&models.Question{}).Where("text = ?", "Czym jest obrona totalna?").Count(&n).Error) if n != 1 { t.Errorf("new question rows = %d", n) } must(t, gdb.Model(&models.AttemptAnswer{}).Count(&n).Error) if n != 0 { t.Errorf("attempt answers after replace = %d", n) } must(t, gdb.Model(&models.Attempt{}).Count(&n).Error) if n != 1 { t.Errorf("attempts after replace = %d, want the attempt kept", n) } // Errors. if _, err := run("testdata/missing.json"); err == nil { t.Error("a missing file imported") } bad := filepath.Join(t.TempDir(), "bad.json") must(t, os.WriteFile(bad, []byte(`{"title":"X","questions":[]}`), 0o644)) if _, err := run(bad); err == nil || !strings.Contains(err.Error(), "questions must not be empty") { t.Errorf("invalid file = %v", err) } }