POST /api/v1/quizzes/import runs behind golem15.user's user.api_token with the write scope and the golem15.quizzes.access_quizzes permission.
87 lines
3.0 KiB
Go
87 lines
3.0 KiB
Go
package quizzes_test
|
|
|
|
import (
|
|
"net/http"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
|
|
userclasses "git.golem15.com/golem15/sm-user-plugin/classes"
|
|
"git.golem15.com/golem15/summercms/modules/surf"
|
|
"gorm.io/gorm"
|
|
)
|
|
|
|
// apiToken makes a shared golem15.user API token for a user.
|
|
func apiToken(t *testing.T, gdb *gorm.DB, userID uint, scopes ...string) string {
|
|
t.Helper()
|
|
secret, token, err := userclasses.MintAPIToken("usr_", userID, "test", scopes, nil, nil)
|
|
must(t, err)
|
|
must(t, gdb.Create(token).Error)
|
|
return secret
|
|
}
|
|
|
|
func TestImportRoute(t *testing.T) {
|
|
app, plugins, gdb := migrated(t)
|
|
h, err := surf.Assemble(app, plugins)
|
|
must(t, err)
|
|
c := &client{t: t, h: h}
|
|
raw, err := os.ReadFile("testdata/quiz.json")
|
|
must(t, err)
|
|
doc := string(raw)
|
|
const path = "/api/v1/quizzes/import"
|
|
|
|
editor, jwt := newUser(t, gdb, "ala@example.com")
|
|
student, _ := newUser(t, gdb, "ola@example.com")
|
|
var groupID uint
|
|
must(t, gdb.Raw(`INSERT INTO user_groups (name, code, permissions, created_at, updated_at)
|
|
VALUES ('Editors', 'editors', '{"golem15.quizzes.access_quizzes":1}', NOW(), NOW()) RETURNING id`).Scan(&groupID).Error)
|
|
must(t, gdb.Exec(`INSERT INTO users_groups (user_id, user_group_id) VALUES (?, ?)`, editor.ID, groupID).Error)
|
|
|
|
// No token, a session JWT, a read-only token and a user without the
|
|
// permission are all refused.
|
|
for name, tc := range map[string]struct {
|
|
token string
|
|
want int
|
|
}{
|
|
"no token": {"", http.StatusUnauthorized},
|
|
"session JWT": {jwt, http.StatusUnauthorized},
|
|
"read-only token": {apiToken(t, gdb, editor.ID, "read"), http.StatusForbidden},
|
|
"without permission": {apiToken(t, gdb, student.ID, "read", "write"), http.StatusForbidden},
|
|
} {
|
|
if code, _ := c.do("POST", path, tc.token, doc); code != tc.want {
|
|
t.Errorf("%s: POST %s = %d, want %d", name, path, code, tc.want)
|
|
}
|
|
}
|
|
if n := count(t, gdb); n != 0 {
|
|
t.Fatalf("quizzes after refused imports = %d", n)
|
|
}
|
|
|
|
token := apiToken(t, gdb, editor.ID, "read", "write")
|
|
code, body := c.do("POST", path, token, doc)
|
|
res := data(body)
|
|
if code != http.StatusOK || res["slug"] != "bezpieczenstwo-pojecia" || res["created"] != true || res["questions"] != float64(3) || res["answers"] != float64(5) {
|
|
t.Fatalf("import = %d %v", code, body)
|
|
}
|
|
if code, _ := c.do("POST", path, token, doc); code != http.StatusConflict {
|
|
t.Errorf("second import without replace = %d, want 409", code)
|
|
}
|
|
code, body = c.do("POST", path+"?replace=1", token, doc)
|
|
if code != http.StatusOK || data(body)["created"] != false {
|
|
t.Errorf("import with replace = %d %v", code, body)
|
|
}
|
|
code, body = c.do("POST", path, token, `{"title":"X","questions":[]}`)
|
|
if code != http.StatusUnprocessableEntity || !strings.Contains(body["message"].(string), "questions must not be empty") {
|
|
t.Errorf("invalid document = %d %v", code, body)
|
|
}
|
|
if n := count(t, gdb); n != 1 {
|
|
t.Errorf("quizzes = %d, want 1", n)
|
|
}
|
|
}
|
|
|
|
func count(t *testing.T, gdb *gorm.DB) int64 {
|
|
t.Helper()
|
|
var n int64
|
|
must(t, gdb.Table("golem15_quizzes_quizzes").Count(&n).Error)
|
|
return n
|
|
}
|