Ship the product pass on the v0.1.3 schema with additive migrations, a replica migration lock, and bot accounts kept out of people lists.
103 lines
3.7 KiB
Go
103 lines
3.7 KiB
Go
package api_test
|
|
|
|
import (
|
|
"context"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
func TestAccountFreeze(t *testing.T) {
|
|
e := setup(t)
|
|
token, uid := register(t, e, "Freeze", "freeze@test.dev")
|
|
|
|
code, missing := e.do(t, "POST", "/api/me/freeze", token, map[string]any{})
|
|
missingMsg, _ := missing["error"].(string)
|
|
if code == http.StatusOK || !strings.Contains(missingMsg, "گذرواژه") {
|
|
t.Fatalf("freeze without password: %d %q", code, missingMsg)
|
|
}
|
|
code, wrong := e.do(t, "POST", "/api/me/freeze", token, map[string]any{"currentPassword": "not-the-password"})
|
|
wrongMsg, _ := wrong["error"].(string)
|
|
if code != http.StatusForbidden || !strings.Contains(wrongMsg, "گذرواژه") || !strings.Contains(wrongMsg, "نادرست") {
|
|
t.Fatalf("freeze with wrong password: %d %q", code, wrongMsg)
|
|
}
|
|
var untouched *time.Time
|
|
if err := e.pool.QueryRow(context.Background(), `SELECT frozen_at FROM users WHERE id=$1`, uid).Scan(&untouched); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if untouched != nil {
|
|
t.Fatal("failed freeze wrote frozen_at")
|
|
}
|
|
|
|
code, body := e.do(t, "POST", "/api/me/freeze", token, map[string]any{"currentPassword": "password123"})
|
|
if code != http.StatusOK {
|
|
t.Fatalf("freeze: %d %v", code, body["error"])
|
|
}
|
|
user := body["user"].(map[string]any)
|
|
frozenAt, _ := user["frozenAt"].(string)
|
|
if frozenAt == "" || user["isFrozen"] != true || user["isClosed"] == true {
|
|
t.Fatalf("freeze state: frozenAt=%v isFrozen=%v isClosed=%v", user["frozenAt"], user["isFrozen"], user["isClosed"])
|
|
}
|
|
if user["isLocked"] == true {
|
|
t.Fatal("freeze reused the admin lock flag")
|
|
}
|
|
var stored *time.Time
|
|
if err := e.pool.QueryRow(context.Background(), `SELECT frozen_at FROM users WHERE id=$1`, uid).Scan(&stored); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if stored == nil || stored.IsZero() {
|
|
t.Fatal("frozen_at was not stored")
|
|
}
|
|
|
|
code, profile := e.do(t, "GET", "/api/users/"+itoa(uid), token, nil)
|
|
if code != http.StatusOK {
|
|
t.Fatalf("profile: %d %v", code, profile["error"])
|
|
}
|
|
pub := profile["user"].(map[string]any)
|
|
if pub["isFrozen"] != true {
|
|
t.Fatalf("public profile missing freeze flag: %v", pub["isFrozen"])
|
|
}
|
|
if _, ok := pub["email"]; ok {
|
|
t.Fatalf("public profile leaked email: %v", pub["email"])
|
|
}
|
|
|
|
_ = login(t, e, "freeze@test.dev")
|
|
|
|
code, body = e.do(t, "POST", "/api/me/unfreeze", token, map[string]any{})
|
|
if code != http.StatusOK {
|
|
t.Fatalf("unfreeze: %d %v", code, body["error"])
|
|
}
|
|
cleared := body["user"].(map[string]any)
|
|
if cleared["isFrozen"] == true || cleared["frozenAt"] != nil {
|
|
t.Fatalf("unfreeze left a timestamp: %v", cleared["frozenAt"])
|
|
}
|
|
var after *time.Time
|
|
if err := e.pool.QueryRow(context.Background(), `SELECT frozen_at FROM users WHERE id=$1`, uid).Scan(&after); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if after != nil {
|
|
t.Fatalf("frozen_at still set: %s", after.Format(time.RFC3339))
|
|
}
|
|
|
|
if _, err := e.pool.Exec(context.Background(), `UPDATE users SET closed_at = now() WHERE id=$1`, uid); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
code, denied := e.do(t, "POST", "/api/auth/login", "", loginBody(t, e, "freeze@test.dev", "password123"))
|
|
errMsg, _ := denied["error"].(string)
|
|
if code != http.StatusForbidden || !strings.Contains(errMsg, "بسته") || !strings.Contains(errMsg, "ورود") {
|
|
t.Fatalf("closed login: %d %q", code, errMsg)
|
|
}
|
|
|
|
code, undo := e.do(t, "POST", "/api/me/unfreeze", token, map[string]any{})
|
|
undoMsg, _ := undo["error"].(string)
|
|
if code != http.StatusConflict || !strings.Contains(undoMsg, "لغو فریز") {
|
|
t.Fatalf("unfreeze after close: %d %q", code, undoMsg)
|
|
}
|
|
|
|
code, forbidden := e.do(t, "DELETE", "/api/admin/users/"+itoa(uid), token, nil)
|
|
if code != http.StatusForbidden {
|
|
t.Fatalf("student permanent delete: %d %v", code, forbidden["error"])
|
|
}
|
|
}
|