2026-08-15 16:31:27 +01:00
|
|
|
package webui
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"net/http"
|
|
|
|
|
"net/http/httptest"
|
|
|
|
|
"net/url"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
"testing"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// TestWebmailSignatureCreateEditDeleteAndDefaults exercises the full signature CRUD
|
|
|
|
|
// flow plus setting/clearing the default-for-new and default-for-reply flags.
|
|
|
|
|
func TestWebmailSignatureCreateEditDeleteAndDefaults(t *testing.T) {
|
|
|
|
|
app := newTestApp(t)
|
|
|
|
|
mux := app.Mux()
|
|
|
|
|
domains, _ := app.DB.ListDomains()
|
|
|
|
|
mailboxID := createTestMailboxWithPassword(t, app, "sigtest@example.com", domains[0].ID, "sigtest-password-1!")
|
|
|
|
|
cookie := webmailLoginSession(t, app, mailboxID)
|
|
|
|
|
|
|
|
|
|
save := func(id, name, html string) *httptest.ResponseRecorder {
|
|
|
|
|
form := url.Values{"id": {id}, "name": {name}, "content_html": {html}}
|
|
|
|
|
req := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/signatures/save", strings.NewReader(form.Encode()))
|
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
|
req.AddCookie(cookie)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
|
return rec
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if rec := save("", "Work", "<p>Jane Doe<br>Acme Inc</p>"); rec.Code != http.StatusFound {
|
|
|
|
|
t.Fatalf("create: status=%d body=%s", rec.Code, rec.Body.String())
|
|
|
|
|
}
|
|
|
|
|
sigs, err := app.DB.ListSignatures(mailboxID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if len(sigs) != 1 || sigs[0].Name != "Work" {
|
|
|
|
|
t.Fatalf("expected 1 signature named Work, got %+v", sigs)
|
|
|
|
|
}
|
|
|
|
|
id := sigs[0].ID
|
|
|
|
|
|
|
|
|
|
// Edit it.
|
|
|
|
|
if rec := save(strconv.FormatInt(id, 10), "Work Updated", "<p>Jane Doe, CEO</p>"); rec.Code != http.StatusFound {
|
|
|
|
|
t.Fatalf("edit: status=%d body=%s", rec.Code, rec.Body.String())
|
|
|
|
|
}
|
|
|
|
|
updated, err := app.DB.GetSignatureByID(mailboxID, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if updated == nil || updated.Name != "Work Updated" || !strings.Contains(updated.ContentHTML, "CEO") {
|
|
|
|
|
t.Fatalf("expected updated signature, got %+v", updated)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Set as default for both new and reply.
|
|
|
|
|
for _, which := range []string{"new", "reply"} {
|
|
|
|
|
req := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/signatures/"+strconv.FormatInt(id, 10)+"/default?which="+which, nil)
|
|
|
|
|
req.AddCookie(cookie)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
|
if rec.Code != http.StatusFound {
|
|
|
|
|
t.Fatalf("set default %s: status=%d body=%s", which, rec.Code, rec.Body.String())
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
got, err := app.DB.GetSignatureByID(mailboxID, id)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if !got.IsDefaultNew || !got.IsDefaultReply {
|
|
|
|
|
t.Fatalf("expected both defaults set, got %+v", got)
|
|
|
|
|
}
|
2026-08-15 21:49:25 +01:00
|
|
|
def, err := app.DB.GetDefaultSignature(mailboxID, false, "")
|
2026-08-15 16:31:27 +01:00
|
|
|
if err != nil || def == nil || def.ID != id {
|
|
|
|
|
t.Fatalf("GetDefaultSignature(new) = %+v, err=%v", def, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Delete it — defaults should just disappear along with the row.
|
|
|
|
|
delReq := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/signatures/"+strconv.FormatInt(id, 10)+"/delete", nil)
|
|
|
|
|
delReq.AddCookie(cookie)
|
|
|
|
|
delRec := httptest.NewRecorder()
|
|
|
|
|
mux.ServeHTTP(delRec, delReq)
|
|
|
|
|
if delRec.Code != http.StatusFound {
|
|
|
|
|
t.Fatalf("delete: status=%d body=%s", delRec.Code, delRec.Body.String())
|
|
|
|
|
}
|
|
|
|
|
remaining, err := app.DB.ListSignatures(mailboxID)
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if len(remaining) != 0 {
|
|
|
|
|
t.Fatalf("expected no signatures left, got %+v", remaining)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-15 21:49:25 +01:00
|
|
|
// TestWebmailSignatureSaveSetsAliasDefault confirms the "also set as default for a
|
|
|
|
|
// specific alias" fields on the add/edit form (default_for_email/default_new/
|
|
|
|
|
// default_reply) actually persist an alias-scoped default via
|
|
|
|
|
// SetSignatureAliasDefault, and that GetDefaultSignature picks it up for that alias
|
|
|
|
|
// specifically (not the mailbox's own primary address).
|
|
|
|
|
func TestWebmailSignatureSaveSetsAliasDefault(t *testing.T) {
|
|
|
|
|
app := newTestApp(t)
|
|
|
|
|
mux := app.Mux()
|
|
|
|
|
domains, _ := app.DB.ListDomains()
|
|
|
|
|
mailboxID := createTestMailboxWithPassword(t, app, "sigalias@example.com", domains[0].ID, "sigalias-password-1!")
|
|
|
|
|
if _, err := app.DB.CreateAlias(mailboxID, "support@example.com", domains[0].ID, true); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
cookie := webmailLoginSession(t, app, mailboxID)
|
|
|
|
|
|
|
|
|
|
form := url.Values{
|
|
|
|
|
"id": {""}, "name": {"Support"}, "content_html": {"<p>Support team</p>"},
|
|
|
|
|
"default_for_email": {"support@example.com"}, "default_new": {"1"}, "default_reply": {"1"},
|
|
|
|
|
}
|
|
|
|
|
req := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/signatures/save", strings.NewReader(form.Encode()))
|
|
|
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
|
|
|
req.AddCookie(cookie)
|
|
|
|
|
rec := httptest.NewRecorder()
|
|
|
|
|
mux.ServeHTTP(rec, req)
|
|
|
|
|
if rec.Code != http.StatusFound {
|
|
|
|
|
t.Fatalf("save: status=%d body=%s", rec.Code, rec.Body.String())
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sigs, err := app.DB.ListSignatures(mailboxID)
|
|
|
|
|
if err != nil || len(sigs) != 1 {
|
|
|
|
|
t.Fatalf("expected 1 signature, got %d (err=%v)", len(sigs), err)
|
|
|
|
|
}
|
|
|
|
|
sigID := sigs[0].ID
|
|
|
|
|
|
|
|
|
|
forNew, err := app.DB.GetDefaultSignature(mailboxID, false, "support@example.com")
|
|
|
|
|
if err != nil || forNew == nil || forNew.ID != sigID {
|
|
|
|
|
t.Fatalf("expected support@example.com's new-message default to be the saved signature, got %+v (err=%v)", forNew, err)
|
|
|
|
|
}
|
|
|
|
|
forReply, err := app.DB.GetDefaultSignature(mailboxID, true, "support@example.com")
|
|
|
|
|
if err != nil || forReply == nil || forReply.ID != sigID {
|
|
|
|
|
t.Fatalf("expected support@example.com's reply default to be the saved signature, got %+v (err=%v)", forReply, err)
|
|
|
|
|
}
|
|
|
|
|
// The mailbox's own primary address is untouched — alias defaults are additive,
|
|
|
|
|
// not a replacement for the mailbox-wide default.
|
|
|
|
|
primary, err := app.DB.GetDefaultSignature(mailboxID, false, "sigalias@example.com")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if primary != nil {
|
|
|
|
|
t.Fatalf("expected no default for the mailbox's own primary address, got %+v", primary)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-08-15 16:31:27 +01:00
|
|
|
// TestWebmailSignatureOnlyOneDefaultPerMailbox confirms setting a second signature as
|
|
|
|
|
// the default-for-new clears the flag from whichever one had it before.
|
|
|
|
|
func TestWebmailSignatureOnlyOneDefaultPerMailbox(t *testing.T) {
|
|
|
|
|
app := newTestApp(t)
|
|
|
|
|
domains, _ := app.DB.ListDomains()
|
|
|
|
|
mailboxID := createTestMailboxWithPassword(t, app, "sigdefault@example.com", domains[0].ID, "sigdefault-password-1!")
|
|
|
|
|
|
|
|
|
|
id1, err := app.DB.CreateSignature(mailboxID, "One", "<p>1</p>")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
id2, err := app.DB.CreateSignature(mailboxID, "Two", "<p>2</p>")
|
|
|
|
|
if err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if err := app.DB.SetDefaultSignature(mailboxID, id1, false); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
if err := app.DB.SetDefaultSignature(mailboxID, id2, false); err != nil {
|
|
|
|
|
t.Fatal(err)
|
|
|
|
|
}
|
|
|
|
|
s1, _ := app.DB.GetSignatureByID(mailboxID, id1)
|
|
|
|
|
s2, _ := app.DB.GetSignatureByID(mailboxID, id2)
|
|
|
|
|
if s1.IsDefaultNew {
|
|
|
|
|
t.Error("expected signature 1 to no longer be the default")
|
|
|
|
|
}
|
|
|
|
|
if !s2.IsDefaultNew {
|
|
|
|
|
t.Error("expected signature 2 to now be the default")
|
|
|
|
|
}
|
|
|
|
|
}
|