package storage import ( "path/filepath" "testing" "github.com/sandrodach/mindsift/internal/domain" ) func TestTrashHidesRestoresAndPurgesNotes(t *testing.T) { store, err := NewStore(filepath.Join(t.TempDir(), "trash.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: "Zebrastreifen", Level: 0}} doc, _ := store.CreateDocument(anna.ID, "", "Verkehr", "🚦", "Projekte/Stadt", blocks, nil, false) _, _ = store.CreateFolder(anna.ID, "Projekte/Stadt") if _, err := store.CreateDocShare(anna.ID, doc.ID, ben.ID, "write"); err != nil { t.Fatalf("share: %v", err) } if err := store.DeleteDocument(anna.ID, doc.ID); err != nil { t.Fatalf("trash: %v", err) } visible := func(uid string) bool { docs, _ := store.GetDocuments(uid) for _, d := range docs { if d.ID == doc.ID { return true } } return false } if visible(anna.ID) || visible(ben.ID) { t.Errorf("trashed note must leave the tree of owner and shared users") } if res, _ := store.SearchGlobal(anna.ID, "Zebrastreifen"); len(res.Documents) != 0 { t.Errorf("trashed note must not be found: %+v", res.Documents) } if store.DocRole(anna.ID, doc.ID) != DocRoleViewer || store.DocRole(ben.ID, doc.ID) != "" { t.Errorf("trash is read-only for the owner and hidden from others") } if got, err := store.GetDocumentByID(anna.ID, doc.ID); err != nil || !got.IsArchived || got.Permission != "read" { t.Errorf("owner reads the trashed note read-only: %+v %v", got, err) } if _, err := store.GetDocumentByID(ben.ID, doc.ID); err == nil { t.Errorf("shared user must not open a trashed note") } title := "Neu" if _, err := store.UpdateDocument(anna.ID, doc.ID, &title, nil, nil, nil); err == nil { t.Errorf("trashed note must not be renamed") } trash, _ := store.GetTrash(anna.ID) if len(trash) != 1 || trash[0].FolderPath != "Projekte/Stadt" || trash[0].DeletedAt == "" { t.Fatalf("trash list wrong: %+v", trash) } if err := store.RestoreDocument(ben.ID, doc.ID); err == nil { t.Errorf("only the owner may restore") } // The folder was deleted meanwhile; restoring creates it again _, _ = store.db.Exec(`DELETE FROM workspace_folders WHERE user_id = ?`, anna.ID) if err := store.RestoreDocument(anna.ID, doc.ID); err != nil { t.Fatalf("restore: %v", err) } if !visible(anna.ID) || !visible(ben.ID) || store.DocRole(ben.ID, doc.ID) != DocRoleEditor { t.Errorf("restored note is back for everyone, shares intact") } if folders, _ := store.GetFolders(anna.ID); len(folders) == 0 { t.Errorf("folder should be created again on restore") } // Deleting a folder moves its notes to the trash; emptying deletes them for good if _, err := store.DeleteFolder(anna.ID, "Projekte"); err != nil { t.Fatalf("DeleteFolder: %v", err) } if trash, _ := store.GetTrash(anna.ID); len(trash) != 1 { t.Fatalf("folder deletion should trash the note, got %+v", trash) } if ids, err := store.EmptyTrash(anna.ID); err != nil || len(ids) != 1 { t.Fatalf("EmptyTrash: %v %v", ids, err) } var docs, shares int _ = store.db.QueryRow(`SELECT COUNT(*) FROM documents WHERE id = ?`, doc.ID).Scan(&docs) _ = store.db.QueryRow(`SELECT COUNT(*) FROM doc_shares WHERE doc_id = ?`, doc.ID).Scan(&shares) if docs+shares != 0 { t.Errorf("purge must remove the note and its shares") } }