Files
MindSift/internal/storage/account_test.go
T
sandrodachandClaude Opus 5.5 0e9fd1b3ad
MindSift CI & Docker Release / Build, Test in Container & Push (push) Successful in 1m27s
feat(account): danger zone, restore import, larger API key field on phones
- Danger zone in settings: delete all own data or the account, both with
  password confirmation; the last server admin cannot delete the account
- DELETE /user/data and /user/account; PurgeUserData removes everything a
  user owns in one transaction (others' tasks in shared lists stay theirs),
  also used by admin user deletion
- MindSift import: 'Hinzufügen' or 'Wiederherstellen' (mode=restore, wipes
  own data only after the file is fully validated, confirm checkbox)
- API key name field and button 44 px on phones (flex-1 collapsed them)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-29 07:13:29 +01:00

93 lines
3.7 KiB
Go

package storage
import (
"errors"
"path/filepath"
"testing"
"github.com/sandrodach/mindsift/internal/domain"
)
func TestPurgeUserDataKeepsAccountAndOthersData(t *testing.T) {
store, err := NewStore(filepath.Join(t.TempDir(), "purge.db"))
if err != nil {
t.Fatalf("NewStore: %v", err)
}
defer store.Close()
openSharing(t, store)
anna, _ := store.CreateUser("anna@example.com", "hash", "Anna", "member")
ben, _ := store.CreateUser("ben@example.com", "hash", "Ben", "member")
blocks := []domain.DocumentBlock{{ID: "b1", Type: "bullet", Content: "x"}}
annaDoc, _ := store.CreateDocument(anna.ID, "", "Annas Notiz", "", "Ordner", blocks, nil, false)
_, _ = store.CreateDocShare(anna.ID, annaDoc.ID, ben.ID, "write")
benDoc, _ := store.CreateDocument(ben.ID, "", "Bens Notiz", "", "", blocks, nil, false)
_, _ = store.CreateDocShare(ben.ID, benDoc.ID, anna.ID, "write")
tl, _ := store.CreateTaskList(anna.ID, "", "", "Haushalt", "", "")
_, _ = store.AddTaskListShare(tl.ID, "user", ben.ID, "editor", anna.ID)
bensTask, _ := store.CreateThoughtWithDetails(ben.ID, "Bens Aufgabe", "normal", "manual", "", tl.ID, "todo", "")
_, _ = store.UpdateThoughtTriage(ben.ID, bensTask.ID, "keep", "task", "", "", tl.ID)
_, _ = store.CreateThought(anna.ID, "Annas Gedanke", "normal", "manual", "")
list, _ := store.CreateList(anna.ID, "Einkauf", "", "shopping")
_, _ = store.CreateListItem(list.ID, "Milch", "", "", 0, anna.ID)
_, _ = store.SaveUserTheme(anna.ID, "", "Wald", ":root{--accent-primary:#16a34a}")
if err := store.PurgeUserData(anna.ID); err != nil {
t.Fatalf("PurgeUserData: %v", err)
}
if docs, _ := store.GetDocuments(anna.ID); len(docs) != 1 || docs[0].ID != benDoc.ID {
t.Errorf("Anna should only still see Ben's shared note, got %+v", docs)
}
if th, _ := store.GetThoughts(anna.ID, "all", 100); len(th) != 0 {
t.Errorf("Anna's thoughts must be gone, got %d", len(th))
}
if tls, _ := store.GetTaskLists(anna.ID); len(tls) != 0 {
t.Errorf("Anna's task lists must be gone")
}
if l, _ := store.GetAllListsForUser(anna.ID); len(l) != 0 {
t.Errorf("Anna's lists must be gone")
}
if u, err := store.GetUserByID(anna.ID); err != nil || u == nil {
t.Errorf("the account must stay")
}
if th, _ := store.GetUserThemes(anna.ID); len(th) != 1 {
t.Errorf("settings such as themes must stay")
}
// Ben keeps his task (now without list) and no longer sees Anna's note
task, err := store.GetThoughtByID(ben.ID, bensTask.ID)
if err != nil || task.TaskListID != "" {
t.Errorf("Ben's task must survive as personal task, got %+v (%v)", task, err)
}
if docs, _ := store.GetDocuments(ben.ID); len(docs) != 1 {
t.Errorf("Ben should only see his own note, got %d", len(docs))
}
}
func TestDeleteOwnAccountProtectsLastAdmin(t *testing.T) {
store, err := NewStore(filepath.Join(t.TempDir(), "account.db"))
if err != nil {
t.Fatalf("NewStore: %v", err)
}
defer store.Close()
admin, _ := store.CreateUser("admin@example.com", "hash", "Admin", "admin")
member, _ := store.CreateUser("member@example.com", "hash", "Mitglied", "member")
if err := store.DeleteOwnAccount(admin.ID); !errors.Is(err, ErrLastAdmin) {
t.Fatalf("last admin must not delete the account, got %v", err)
}
if err := store.DeleteOwnAccount(member.ID); err != nil {
t.Fatalf("member deletes own account: %v", err)
}
if _, err := store.GetUserByID(member.ID); err == nil {
t.Errorf("member account should be gone")
}
second, _ := store.CreateUser("admin2@example.com", "hash", "Admin 2", "admin")
if err := store.DeleteOwnAccount(admin.ID); err != nil {
t.Fatalf("with a second admin the first may leave: %v", err)
}
if !store.IsLastAdmin(second.ID) {
t.Errorf("the remaining admin is now the last one")
}
}