Prefix replacement of the old application-nested module path in go.mod and every import. No identifier, string, table, migration ID, config key or comment changes.
225 lines
7.3 KiB
Go
225 lines
7.3 KiB
Go
package user
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"os/exec"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"context"
|
|
|
|
"git.golem15.com/golem15/sm-user-plugin/classes"
|
|
"git.golem15.com/golem15/sm-user-plugin/controllers"
|
|
"git.golem15.com/golem15/sm-user-plugin/models"
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/postcard"
|
|
)
|
|
|
|
func TestRegister(t *testing.T) {
|
|
app, _ := sessionApp(t, "0s")
|
|
disableRegisterThrottle(t, app)
|
|
body := `{"email":"new-ada@example.test","password":"correct-horse","name":"Ada"}`
|
|
rec := postJSON(t, controllers.Register(app), "/_user/api/v1/register", body)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("register %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
var created map[string]any
|
|
if err := json.Unmarshal(rec.Body.Bytes(), &created); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
token, _ := created["token"].(string)
|
|
if token == "" {
|
|
t.Fatalf("body %s", rec.Body.String())
|
|
}
|
|
userBody, _ := created["user"].(map[string]any)
|
|
if userBody["feedback_widget_hidden"] != false {
|
|
t.Fatalf("feedback_widget_hidden = %#v", userBody["feedback_widget_hidden"])
|
|
}
|
|
fetch := bearer(t, controllers.Fetch(app), http.MethodGet, "/_user/api/v1/fetch", token)
|
|
if fetch.Code != http.StatusOK || !strings.Contains(fetch.Body.String(), `"email":"new-ada@example.test"`) {
|
|
t.Fatalf("fetch %d %s", fetch.Code, fetch.Body.String())
|
|
}
|
|
|
|
missing := postJSON(t, controllers.Register(app), "/_user/api/v1/register", `{"password":"correct-horse"}`)
|
|
if missing.Code != http.StatusUnprocessableEntity {
|
|
t.Fatalf("missing email %d %s", missing.Code, missing.Body.String())
|
|
}
|
|
var verr map[string]any
|
|
if err := json.Unmarshal(missing.Body.Bytes(), &verr); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
errorsBody, _ := verr["errors"].(map[string]any)
|
|
if _, ok := errorsBody["email"]; !ok || verr["error"] == "" {
|
|
t.Fatalf("validation body %#v", verr)
|
|
}
|
|
}
|
|
|
|
func TestRegisterDisabled(t *testing.T) {
|
|
app, _ := sessionApp(t, "0s")
|
|
if err := app.Config.Set("golem15.user.registration.allow_registration", false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := app.Config.Set("app.debug", false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rec := postJSON(t, controllers.Register(app), "/_user/api/v1/register", `{"email":"off@example.test","password":"correct-horse"}`)
|
|
if rec.Code != http.StatusInternalServerError || rec.Body.String() != `{"error":"Internal server error"}` {
|
|
t.Fatalf("production %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
if err := app.Config.Set("app.debug", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
debug := postJSON(t, controllers.Register(app), "/_user/api/v1/register", `{"email":"off@example.test","password":"correct-horse"}`)
|
|
if debug.Code != http.StatusInternalServerError || debug.Body.String() != `{"error":"Registrations are currently disabled."}` {
|
|
t.Fatalf("debug %d %s", debug.Code, debug.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestRegisterThrottle(t *testing.T) {
|
|
app, gdb := sessionApp(t, "0s")
|
|
ip := "203.0.113.9"
|
|
for i := 1; i <= 3; i++ {
|
|
addr := ip
|
|
user := &models.User{Email: "throttle-reg-" + string(rune('a'+i-1)) + "@example.test", Password: "x", CreatedIPAddress: &addr}
|
|
if err := gdb.Create(user).Error; err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
rec := postRegister(t, app, ip, `{"email":"fourth@example.test","password":"correct-horse"}`)
|
|
if rec.Code != http.StatusInternalServerError || rec.Body.String() != `{"error":"Internal server error"}` {
|
|
t.Fatalf("throttled %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
if err := app.Config.Set("app.debug", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
debug := postRegister(t, app, ip, `{"email":"fifth@example.test","password":"correct-horse"}`)
|
|
if debug.Body.String() != `{"error":"Registration is throttled. Please try again later."}` {
|
|
t.Fatalf("debug throttle %d %s", debug.Code, debug.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestRegisterUserModeMail(t *testing.T) {
|
|
app, _ := sessionApp(t, "0s")
|
|
disableRegisterThrottle(t, app)
|
|
if err := app.Config.Set("golem15.user.activation.activate_mode", "user"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mail := &captureMail{}
|
|
if err := app.Publish[postcard.Mailer](mail); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rec := postJSON(t, controllers.Register(app), "/_user/api/v1/register", `{"email":"en-user@example.test","password":"correct-horse","preferred_locale":"en"}`)
|
|
if rec.Code != http.StatusOK || rec.Body.String() != `{"message":"Activation email sent"}` {
|
|
t.Fatalf("user mode %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
if len(mail.got) != 1 || mail.got[0] != "golem15.user::mail.activate-en" {
|
|
t.Fatalf("template = %#v", mail.got)
|
|
}
|
|
}
|
|
|
|
func TestRegisterAdminMode(t *testing.T) {
|
|
app, _ := sessionApp(t, "0s")
|
|
disableRegisterThrottle(t, app)
|
|
if err := app.Config.Set("golem15.user.activation.activate_mode", "admin"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := app.Config.Set("golem15.user.activation.require_activation", true); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rec := postJSON(t, controllers.Register(app), "/_user/api/v1/register", `{"email":"admin-mode@example.test","password":"correct-horse"}`)
|
|
if rec.Code != http.StatusOK || rec.Body.String() != `{}` {
|
|
t.Fatalf("admin mode %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestRegisterEvent(t *testing.T) {
|
|
app, _ := sessionApp(t, "0s")
|
|
disableRegisterThrottle(t, app)
|
|
var seen string
|
|
app.Events.Listen[*classes.RegisterEvent]("test", func(_ context.Context, e *classes.RegisterEvent) error {
|
|
if e != nil && e.User != nil {
|
|
seen = e.User.Email
|
|
}
|
|
return nil
|
|
})
|
|
rec := postJSON(t, controllers.Register(app), "/_user/api/v1/register", `{"email":"event@example.test","password":"correct-horse"}`)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("register %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
if seen != "event@example.test" {
|
|
t.Fatalf("event email = %q", seen)
|
|
}
|
|
}
|
|
|
|
func TestRegisterImportDirection(t *testing.T) {
|
|
cmd := exec.Command("go", "list", "-deps", "./...")
|
|
cmd.Dir = moduleRoot(t)
|
|
out, err := cmd.CombinedOutput()
|
|
if err != nil {
|
|
t.Fatalf("go list: %v\n%s", err, out)
|
|
}
|
|
if strings.Contains(string(out), "plugins/golem15/fonoteka") {
|
|
t.Fatal("golem15.user imports golem15.fonoteka")
|
|
}
|
|
}
|
|
|
|
func TestRegisterCORSPath(t *testing.T) {
|
|
body, err := os.ReadFile(filepath.Join(appRoot(t), "config", "http.yaml"))
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !strings.Contains(string(body), "_user/api/*") {
|
|
t.Fatal("cors paths missing _user/api/*")
|
|
}
|
|
}
|
|
|
|
func disableRegisterThrottle(t *testing.T, app *backpack.App) {
|
|
t.Helper()
|
|
if err := app.Config.Set("golem15.user.registration.use_register_throttle", false); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
}
|
|
|
|
func postRegister(t *testing.T, app *backpack.App, ip, body string) *httptest.ResponseRecorder {
|
|
t.Helper()
|
|
req := httptest.NewRequest(http.MethodPost, "/_user/api/v1/register", strings.NewReader(body))
|
|
req.Host = "example.com"
|
|
req.RemoteAddr = ip + ":9999"
|
|
req.Header.Set("Content-Type", "application/json")
|
|
rec := httptest.NewRecorder()
|
|
controllers.Register(app)(rec, req)
|
|
return rec
|
|
}
|
|
|
|
func moduleRoot(t *testing.T) string {
|
|
t.Helper()
|
|
return findUp(t, "go.mod")
|
|
}
|
|
|
|
func appRoot(t *testing.T) string {
|
|
t.Helper()
|
|
return findUp(t, filepath.Join("config", "http.yaml"))
|
|
}
|
|
|
|
func findUp(t *testing.T, rel string) string {
|
|
t.Helper()
|
|
dir, err := os.Getwd()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for {
|
|
if _, err := os.Stat(filepath.Join(dir, rel)); err == nil {
|
|
return dir
|
|
}
|
|
parent := filepath.Dir(dir)
|
|
if parent == dir {
|
|
t.Fatalf("%s not found", rel)
|
|
}
|
|
dir = parent
|
|
}
|
|
}
|