90 lines
3.5 KiB
Go
90 lines
3.5 KiB
Go
package postgres
|
|
|
|
import (
|
|
"database/sql"
|
|
"errors"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"gardomatic.kleiax.de/internal/storage"
|
|
_ "github.com/lib/pq"
|
|
)
|
|
|
|
func TestDeleteUserAnonymizesAccountAndPreservesAuthoredContent(t *testing.T) {
|
|
dsn := os.Getenv("GARDOMATIC_TEST_DB_DSN")
|
|
if dsn == "" {
|
|
t.Skip("set GARDOMATIC_TEST_DB_DSN to run PostgreSQL integration tests")
|
|
}
|
|
db, err := sql.Open("postgres", dsn)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() { _ = db.Close() })
|
|
if err = db.Ping(); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
stamp := strings.ReplaceAll(time.Now().Format("150405.000000000"), ".", "")
|
|
originalEmail := "delete-" + stamp + "@example.com"
|
|
var ownerID, targetID, gardenID, entryID int
|
|
if err = db.QueryRow(`INSERT INTO users (name,email,password_hash,activated) VALUES ('Owner',$1,'hash',true) RETURNING id`, "owner-delete-"+stamp+"@example.com").Scan(&ownerID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.QueryRow(`INSERT INTO users (name,email,password_hash,activated) VALUES ('Personal Name',$1,'hash',true) RETURNING id`, originalEmail).Scan(&targetID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.QueryRow(`INSERT INTO gardens (name) VALUES ('Deletion integration') RETURNING id`).Scan(&gardenID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
t.Cleanup(func() {
|
|
_, _ = db.Exec(`DELETE FROM gardens WHERE id=$1`, gardenID)
|
|
_, _ = db.Exec(`DELETE FROM users WHERE id IN ($1,$2)`, ownerID, targetID)
|
|
})
|
|
if _, err = db.Exec(`INSERT INTO garden_members (garden_id,user_id,role) VALUES ($1,$2,'owner'),($1,$3,'member')`, gardenID, ownerID, targetID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.QueryRow(`INSERT INTO journal_entries (garden_id,author_id,title) VALUES ($1,$2,'Bleibt erhalten') RETURNING id`, gardenID, targetID).Scan(&entryID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
users := UserModel{DB: db}
|
|
if err = users.Delete(targetID); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err = users.GetByEmail(originalEmail); !errors.Is(err, storage.ErrRecordNotFound) {
|
|
t.Fatalf("deleted email still resolves: %v", err)
|
|
}
|
|
if _, err = users.GetByID(targetID); !errors.Is(err, storage.ErrRecordNotFound) {
|
|
t.Fatalf("deleted user ID still resolves: %v", err)
|
|
}
|
|
var name, email string
|
|
var activated bool
|
|
var deletedAt time.Time
|
|
if err = db.QueryRow(`SELECT name,email,activated,deleted_at FROM users WHERE id=$1`, targetID).Scan(&name, &email, &activated, &deletedAt); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if name != "Gelöschter Nutzer" || email == originalEmail || activated || deletedAt.IsZero() {
|
|
t.Fatalf("account was not anonymized: name=%q email=%q activated=%t deleted_at=%v", name, email, activated, deletedAt)
|
|
}
|
|
var membershipCount, entryCount int
|
|
if err = db.QueryRow(`SELECT count(*) FROM garden_members WHERE user_id=$1`, targetID).Scan(&membershipCount); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err = db.QueryRow(`SELECT count(*) FROM journal_entries WHERE id=$1 AND author_id=$2`, entryID, targetID).Scan(&entryCount); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if membershipCount != 0 || entryCount != 1 {
|
|
t.Fatalf("membership=%d preserved entries=%d", membershipCount, entryCount)
|
|
}
|
|
var replacementID int
|
|
if err = db.QueryRow(`INSERT INTO users (name,email,password_hash,activated) VALUES ('Replacement',$1,'hash',true) RETURNING id`, originalEmail).Scan(&replacementID); err != nil {
|
|
t.Fatalf("original email was not released: %v", err)
|
|
}
|
|
t.Cleanup(func() { _, _ = db.Exec(`DELETE FROM users WHERE id=$1`, replacementID) })
|
|
if err = users.Delete(ownerID); !errors.Is(err, storage.ErrConflict) {
|
|
t.Fatalf("owner deletion error: got %v, want conflict", err)
|
|
}
|
|
}
|