Zweistufig wie der Werkzeugkatalog: Betreiber pflegt den plattformweiten Standard (/betreiber/email-vorlagen), jeder Mandant kann ihn für sich übersteuern (/verwaltung/email-vorlagen) - ResolveEmailVorlage nutzt die eigene Vorlage falls vorhanden, sonst fällt sie auf den Plattform- Standard zurück. Passwort-Zurücksetzen ist die einzige aktuell existierende E-Mail und nutzt jetzt diese Vorlage statt Hardcoding. Bug beim Live-Verifizieren gefunden: UNIQUE(account_id, typ) verhindert bei NULLABLE account_id keine Duplikate (NULL != NULL in SQL) - jedes Speichern des Plattform-Standards erzeugte eine neue Zeile statt sie zu aktualisieren. Fix: zwei partielle Unique-Indizes statt eines gemeinsamen Constraints, mit Regressionstest abgesichert. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
106 lines
4.1 KiB
Go
106 lines
4.1 KiB
Go
package web_test
|
|
|
|
import (
|
|
"net/http"
|
|
"net/url"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestAdminKannEigeneEmailVorlageSpeichernUndZuruecksetzen(t *testing.T) {
|
|
fs := newFakeStore()
|
|
s := newServer(t, fs)
|
|
cookie := seedAccountWithRole(t, fs, "Test-Mandant", "admin@example.com", "admin")
|
|
|
|
getResp := getWithCookie(t, s, cookie, "/verwaltung/email-vorlagen")
|
|
if getResp.Code != http.StatusOK {
|
|
t.Fatalf("GET status = %d, want 200", getResp.Code)
|
|
}
|
|
if !strings.Contains(getResp.Body.String(), "Plattform-Standard, nicht angepasst") {
|
|
t.Fatalf("erwartet Hinweis auf geerbten Plattform-Standard, body: %s", getResp.Body.String())
|
|
}
|
|
|
|
saveResp := postForm(t, s, cookie, "/verwaltung/email-vorlagen", url.Values{
|
|
"typ": {"passwort_zuruecksetzen"}, "betreff": {"Mein eigener Betreff"}, "text": {"Mein eigener Text {{link}}"},
|
|
})
|
|
if saveResp.Code != http.StatusSeeOther {
|
|
t.Fatalf("save status = %d, want 303, body: %s", saveResp.Code, saveResp.Body.String())
|
|
}
|
|
|
|
afterResp := getWithCookie(t, s, cookie, "/verwaltung/email-vorlagen")
|
|
body := afterResp.Body.String()
|
|
if !strings.Contains(body, "Mein eigener Betreff") {
|
|
t.Fatalf("gespeicherter Betreff fehlt, body: %s", body)
|
|
}
|
|
if !strings.Contains(body, "(eigene Vorlage)") {
|
|
t.Fatalf("erwartet Hinweis auf eigene Vorlage, body: %s", body)
|
|
}
|
|
|
|
resetResp := postForm(t, s, cookie, "/verwaltung/email-vorlagen/zuruecksetzen", url.Values{"typ": {"passwort_zuruecksetzen"}})
|
|
if resetResp.Code != http.StatusSeeOther {
|
|
t.Fatalf("reset status = %d, want 303", resetResp.Code)
|
|
}
|
|
finalResp := getWithCookie(t, s, cookie, "/verwaltung/email-vorlagen")
|
|
if strings.Contains(finalResp.Body.String(), "Mein eigener Betreff") {
|
|
t.Fatal("nach Zurücksetzen ist die eigene Vorlage immer noch aktiv")
|
|
}
|
|
}
|
|
|
|
func TestBetreiberKannPlattformStandardBearbeiten(t *testing.T) {
|
|
fs := newFakeStore()
|
|
s := newServer(t, fs)
|
|
cookie := seedAccountWithRole(t, fs, "Betreiber-Firma", "betreiber@example.com", "betreiber")
|
|
|
|
saveResp := postForm(t, s, cookie, "/betreiber/email-vorlagen", url.Values{
|
|
"typ": {"passwort_zuruecksetzen"}, "betreff": {"Neuer Plattform-Betreff"}, "text": {"Neuer Plattform-Text {{link}}"},
|
|
})
|
|
if saveResp.Code != http.StatusSeeOther {
|
|
t.Fatalf("save status = %d, want 303, body: %s", saveResp.Code, saveResp.Body.String())
|
|
}
|
|
|
|
getResp := getWithCookie(t, s, cookie, "/betreiber/email-vorlagen")
|
|
if !strings.Contains(getResp.Body.String(), "Neuer Plattform-Betreff") {
|
|
t.Fatalf("neuer Plattform-Standard fehlt, body: %s", getResp.Body.String())
|
|
}
|
|
}
|
|
|
|
func TestNichtAdminKannEmailVorlagenNichtBearbeiten(t *testing.T) {
|
|
fs := newFakeStore()
|
|
s := newServer(t, fs)
|
|
cookie := seedAccountWithRole(t, fs, "Test-Mandant", "mitarbeiter@example.com", "mitarbeiter")
|
|
|
|
resp := getWithCookie(t, s, cookie, "/verwaltung/email-vorlagen")
|
|
if resp.Code != http.StatusNotFound {
|
|
t.Fatalf("status = %d, want 404", resp.Code)
|
|
}
|
|
}
|
|
|
|
func TestPasswortVergessenNutztMandantenspezifischeVorlage(t *testing.T) {
|
|
fs := newFakeStore()
|
|
s, fm := newServerWithMailer(t, fs)
|
|
cookie := seedAccountWithRole(t, fs, "Vorlage-Firma", "vorlage-admin@example.com", "admin")
|
|
|
|
if resp := postForm(t, s, cookie, "/verwaltung/email-vorlagen", url.Values{
|
|
"typ": {"passwort_zuruecksetzen"}, "betreff": {"Firmenspezifischer Betreff"}, "text": {"Hallo! Link: {{link}} Ende."},
|
|
}); resp.Code != http.StatusSeeOther {
|
|
t.Fatalf("save status = %d, want 303", resp.Code)
|
|
}
|
|
|
|
if resp := postForm(t, s, nil, "/passwort-vergessen", url.Values{"email": {"vorlage-admin@example.com"}}); resp.Code != http.StatusOK {
|
|
t.Fatalf("passwort-vergessen status = %d, want 200", resp.Code)
|
|
}
|
|
if len(fm.Sent) != 1 {
|
|
t.Fatalf("got %d gesendete Mails, want 1", len(fm.Sent))
|
|
}
|
|
if fm.Sent[0].Subject != "Firmenspezifischer Betreff" {
|
|
t.Fatalf("Subject = %q, want die firmenspezifische Vorlage", fm.Sent[0].Subject)
|
|
}
|
|
if !strings.Contains(fm.Sent[0].Body, "Hallo! Link: ") || strings.Contains(fm.Sent[0].Body, "{{link}}") {
|
|
t.Fatalf("Body = %q, want Platzhalter ersetzt und Rest der eigenen Vorlage erhalten", fm.Sent[0].Body)
|
|
}
|
|
if !regexp.MustCompile(`/passwort-zuruecksetzen/[a-f0-9]+`).MatchString(fm.Sent[0].Body) {
|
|
t.Fatalf("kein Reset-Link im Body gefunden: %q", fm.Sent[0].Body)
|
|
}
|
|
}
|