diff --git a/profile_test.go b/profile_test.go index 94b048e..da0c60e 100644 --- a/profile_test.go +++ b/profile_test.go @@ -82,6 +82,29 @@ func TestChangePassword(t *testing.T) { t.Fatalf("same password %d %s", same.Code, same.Body.String()) } + // PHP OAuthPasswordBootstrapTest::test_real_password_account_still_requires_current_password: + // a self-set password still needs current_password, and a wrong one is + // refused with the unchanged invalid_login body; the password stays. + t.Run("real_password_account_still_requires_current_password", func(t *testing.T) { + missing := bearerJSON(t, controllers.ChangePassword(app), "/_user/api/v1/change-password", token, `{"password":"another-password-2","password_confirmation":"another-password-2"}`) + var body map[string]any + if err := json.Unmarshal(missing.Body.Bytes(), &body); err != nil { + t.Fatal(err) + } + errs, _ := body["errors"].(map[string]any) + if missing.Code != http.StatusUnprocessableEntity || errs["current_password"] == nil { + t.Fatalf("missing current_password %d %s", missing.Code, missing.Body.String()) + } + wrong := bearerJSON(t, controllers.ChangePassword(app), "/_user/api/v1/change-password", token, `{"current_password":"definitely-wrong","password":"another-password-2","password_confirmation":"another-password-2"}`) + if wrong.Code != http.StatusUnprocessableEntity || wrong.Body.String() != `{"error":"Invalid email or password","errors":{"current_password":["Invalid email or password"]}}` { + t.Fatalf("wrong current_password %d %s", wrong.Code, wrong.Body.String()) + } + again := postJSON(t, controllers.Login(app), "/_user/api/v1/login", `{"email":"change@example.test","password":"brand-new-password"}`) + if again.Code != http.StatusOK { + t.Fatalf("the password changed: login %d %s", again.Code, again.Body.String()) + } + }) + locked := insertUser(t, gdb, "locked-password@example.test", "correct-horse") if err := gdb.Model(locked).UpdateColumn("has_self_set_password", false).Error; err != nil { t.Fatal(err)