package webui import ( "net/http" "net/http/httptest" "net/url" "strconv" "strings" "testing" ) // TestWebmailSaveDraftThenSend confirms saving a draft stores it in Drafts without // sending, reopening it via the Drafts row prefills the compose form, re-saving // replaces the old copy (no duplicates), and finally sending it delivers the message // and removes it from Drafts. func TestWebmailSaveDraftThenSend(t *testing.T) { app := newTestApp(t) mux := app.Mux() domains, _ := app.DB.ListDomains() domainID := domains[0].ID senderID := createTestMailboxWithPassword(t, app, "draft-sender@example.com", domainID, "sender-password-1!") recipientID := createTestMailboxWithPassword(t, app, "draft-recip@example.com", domainID, "recip-password-1!") cookie := webmailLoginSession(t, app, senderID) // Save a draft with no recipient at all — a draft can be incomplete. saveForm := url.Values{"subject": {"WIP"}, "body_html": {"not done yet"}} saveReq := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/mail/save-draft", strings.NewReader(saveForm.Encode())) saveReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") saveReq.AddCookie(cookie) saveRec := httptest.NewRecorder() mux.ServeHTTP(saveRec, saveReq) if saveRec.Code != http.StatusFound { t.Fatalf("save draft: status=%d body=%s", saveRec.Code, saveRec.Body.String()) } drafts, err := app.DB.ListMessagesInFolder(senderID, "Drafts") if err != nil || len(drafts) != 1 { t.Fatalf("expected 1 draft, got %d (err=%v)", len(drafts), err) } draftID := drafts[0].ID // Reopening it via the Drafts prefill route shows the saved content. openReq := httptest.NewRequest(http.MethodGet, MailboxPrefix+"/mail/compose?draft="+strconv.FormatInt(draftID, 10)+"&folder=Drafts", nil) openReq.AddCookie(cookie) openRec := httptest.NewRecorder() mux.ServeHTTP(openRec, openReq) if openRec.Code != http.StatusOK { t.Fatalf("open draft: status=%d", openRec.Code) } body := openRec.Body.String() if !strings.Contains(body, "WIP") || !strings.Contains(body, "not done yet") { t.Fatalf("expected the draft's subject/body prefilled, got body: %s", body) } if !strings.Contains(body, `value="`+strconv.FormatInt(draftID, 10)+`"`) { t.Fatalf("expected draft_id round-tripped into the form, got body: %s", body) } // Re-save with the recipient now filled in and draft_id carried — should replace, // not duplicate. resaveForm := url.Values{ "to": {"draft-recip@example.com"}, "subject": {"WIP"}, "body_html": {"still not done"}, "draft_id": {strconv.FormatInt(draftID, 10)}, } resaveReq := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/mail/save-draft", strings.NewReader(resaveForm.Encode())) resaveReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") resaveReq.AddCookie(cookie) mux.ServeHTTP(httptest.NewRecorder(), resaveReq) drafts, err = app.DB.ListMessagesInFolder(senderID, "Drafts") if err != nil || len(drafts) != 1 { t.Fatalf("expected still 1 draft after re-save, got %d (err=%v)", len(drafts), err) } newDraftID := drafts[0].ID // Finally send it — the message is delivered and the draft is gone. sendForm := url.Values{ "to": {"draft-recip@example.com"}, "subject": {"WIP"}, "body_html": {"finally done"}, "draft_id": {strconv.FormatInt(newDraftID, 10)}, } sendReq := httptest.NewRequest(http.MethodPost, MailboxPrefix+"/mail/compose", strings.NewReader(sendForm.Encode())) sendReq.Header.Set("Content-Type", "application/x-www-form-urlencoded") sendReq.AddCookie(cookie) sendRec := httptest.NewRecorder() mux.ServeHTTP(sendRec, sendReq) if sendRec.Code != http.StatusFound { t.Fatalf("send: status=%d body=%s", sendRec.Code, sendRec.Body.String()) } msgs, err := app.DB.ListMessagesInFolder(recipientID, "INBOX") if err != nil || len(msgs) != 1 { t.Fatalf("expected 1 message delivered, got %d (err=%v)", len(msgs), err) } remainingDrafts, err := app.DB.ListMessagesInFolder(senderID, "Drafts") if err != nil || len(remainingDrafts) != 0 { t.Fatalf("expected the draft gone after sending, got %d (err=%v)", len(remainingDrafts), err) } }