feat: create, publish, import and report on quizzes over MCP
- quizzes_create_quiz, update_quiz, delete_quiz (confirm repeats the slug), publish, unpublish, import_quiz (the existing import route) and quiz_stats on new manage routes - stats close expired open test attempts first, then report per student over completed whole-quiz attempts and per question over every completed attempt - input is checked before the database; TestManageAPIAgainstDatabase is written and skipped under -short
This commit is contained in:
@@ -2,21 +2,64 @@ package api
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
)
|
||||
|
||||
// TestManageHandlersWithoutDatabase: every manage handler answers 503
|
||||
// when the application has no database.
|
||||
// when the application has no database and the input is valid.
|
||||
func TestManageHandlersWithoutDatabase(t *testing.T) {
|
||||
h := New(backpack.New(nil))
|
||||
for name, fn := range map[string]http.HandlerFunc{
|
||||
"list": h.ManageListQuizzes, "show": h.ManageShowQuiz,
|
||||
for name, tc := range map[string]struct {
|
||||
fn http.HandlerFunc
|
||||
method string
|
||||
target string
|
||||
body string
|
||||
}{
|
||||
"list": {h.ManageListQuizzes, "GET", "/", ""},
|
||||
"show": {h.ManageShowQuiz, "GET", "/", ""},
|
||||
"create": {h.ManageCreateQuiz, "POST", "/", `{"title":"Pojęcia"}`},
|
||||
"update": {h.ManageUpdateQuiz, "PATCH", "/", `{"max_attempts":0}`},
|
||||
"delete": {h.ManageDeleteQuiz, "DELETE", "/?confirm=x", ""},
|
||||
"publish": {h.ManagePublish, "POST", "/", ""},
|
||||
"unpublish": {h.ManageUnpublish, "POST", "/", ""},
|
||||
"stats": {h.ManageQuizStats, "GET", "/", ""},
|
||||
} {
|
||||
code, _ := call(t, fn, "GET", "/", "", 7, map[string]string{"slug": "x"})
|
||||
code, _ := call(t, tc.fn, tc.method, tc.target, tc.body, 7, map[string]string{"slug": "x"})
|
||||
if code != http.StatusServiceUnavailable {
|
||||
t.Errorf("%s without a database = %d, want 503", name, code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestManageWritesRefuseBadInputBeforeDatabase: invalid input is a 422
|
||||
// even without a database, so it is checked before any read.
|
||||
func TestManageWritesRefuseBadInputBeforeDatabase(t *testing.T) {
|
||||
h := New(backpack.New(nil))
|
||||
for name, tc := range map[string]struct {
|
||||
fn http.HandlerFunc
|
||||
method string
|
||||
target string
|
||||
body string
|
||||
want string
|
||||
}{
|
||||
"create invalid JSON": {h.ManageCreateQuiz, "POST", "/", `{"title":`, "invalid JSON"},
|
||||
"create empty body": {h.ManageCreateQuiz, "POST", "/", "", "invalid JSON"},
|
||||
"create is_published": {h.ManageCreateQuiz, "POST", "/", `{"title":"A","is_published":true}`, "is_published"},
|
||||
"create missing title": {h.ManageCreateQuiz, "POST", "/", `{"slug":"a"}`, "title is required"},
|
||||
"create mode exam": {h.ManageCreateQuiz, "POST", "/", `{"title":"A","mode":"exam"}`, "mode must be one of"},
|
||||
"update slug": {h.ManageUpdateQuiz, "PATCH", "/", `{"slug":"b"}`, "slug"},
|
||||
"update is_published": {h.ManageUpdateQuiz, "PATCH", "/", `{"is_published":true}`, "is_published"},
|
||||
"update passing 101": {h.ManageUpdateQuiz, "PATCH", "/", `{"passing_score":101}`, "passing_score"},
|
||||
"delete without confirm": {h.ManageDeleteQuiz, "DELETE", "/", "", "confirm must repeat the quiz slug"},
|
||||
"delete wrong confirm": {h.ManageDeleteQuiz, "DELETE", "/?confirm=y", "", "confirm must repeat the quiz slug"},
|
||||
} {
|
||||
code, out := call(t, tc.fn, tc.method, tc.target, tc.body, 7, map[string]string{"slug": "x"})
|
||||
msg, _ := out["message"].(string)
|
||||
if code != http.StatusUnprocessableEntity || !strings.Contains(msg, tc.want) {
|
||||
t.Errorf("%s = %d %v, want 422 with %q", name, code, out, tc.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user