mirror of
https://github.com/warmbly/warmbly.git
synced 2026-10-18 16:03:00 +00:00
138 lines
4.5 KiB
Go
138 lines
4.5 KiB
Go
package handler
|
|
|
|
import (
|
|
"html"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
|
|
"github.com/warmbly/warmbly/internal/app/unsublink"
|
|
"github.com/warmbly/warmbly/internal/models"
|
|
)
|
|
|
|
func TestUnsubLanguageFollowsTheBrowser(t *testing.T) {
|
|
cases := map[string]string{
|
|
"": "en",
|
|
"*": "en",
|
|
"de-DE,de;q=0.9,en;q=0.8": "de",
|
|
"en-US,en;q=0.9,de;q=0.8": "en",
|
|
"fr;q=0.5, de;q=0.9": "de",
|
|
"xx-YY, pt-BR;q=0.7": "pt",
|
|
"de;q=0, fr;q=0.1": "fr",
|
|
"de;q=0": "en",
|
|
"de;q=abc, it": "it",
|
|
"zh-CN,zh;q=0.9": "zh",
|
|
"zh-TW": "zh-Hant",
|
|
"zh-Hant-HK": "zh-Hant",
|
|
"nn-NO": "nb",
|
|
"no": "nb",
|
|
"tl-PH": "fil",
|
|
"sr-Latn-RS": "sr",
|
|
"AR-eg": "ar",
|
|
"es_MX": "es",
|
|
"ja-JP;q=0.8, ko-KR;q=0.8": "ja",
|
|
"de;q=0.5,fr;q=NaN": "de",
|
|
"de;q=0.5,fr;q=2": "de",
|
|
"de;q=0.5,fr;q=+Inf": "de",
|
|
}
|
|
for header, want := range cases {
|
|
if got := unsubLanguage(header); got != want {
|
|
t.Errorf("unsubLanguage(%q) = %q, want %q", header, got, want)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Every tagging language has the whole page, not half of it in English.
|
|
func TestUnsubPageCoversEveryTaggingLanguage(t *testing.T) {
|
|
codes := []string{"zh-Hant"}
|
|
for code := range models.MailLanguageNames {
|
|
codes = append(codes, code)
|
|
}
|
|
for _, code := range codes {
|
|
c, ok := unsubCopies[code]
|
|
if !ok {
|
|
t.Errorf("%s: no unsubscribe page", code)
|
|
continue
|
|
}
|
|
v := reflect.ValueOf(c)
|
|
for i := range v.NumField() {
|
|
if f := v.Field(i); f.Kind() == reflect.String && strings.TrimSpace(f.String()) == "" {
|
|
t.Errorf("%s: %s is empty", code, v.Type().Field(i).Name)
|
|
}
|
|
}
|
|
if code != "en" && c.ConfirmTitle == unsubCopies["en"].ConfirmTitle {
|
|
t.Errorf("%s: the confirm page is still in English", code)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestUnsubPageRendersTheRecipientsLanguage(t *testing.T) {
|
|
signer := unsublink.New("secret", "https://api.example.com")
|
|
h := &Handler{UnsubscribeLinks: signer}
|
|
get := func(lang string) string {
|
|
w := httptest.NewRecorder()
|
|
r := httptest.NewRequest(http.MethodGet, "/unsubscribe/"+signer.Token(uuid.New(), uuid.New(), uuid.New(), time.Now()), nil)
|
|
r.Header.Set("Accept-Language", lang)
|
|
newUnsubRouter(h).ServeHTTP(w, r)
|
|
if w.Code != http.StatusOK || w.Header().Get("Vary") != "Accept-Language" {
|
|
t.Fatalf("%s: %d, Vary %q", lang, w.Code, w.Header().Get("Vary"))
|
|
}
|
|
return html.UnescapeString(w.Body.String())
|
|
}
|
|
de := get("de-DE,de;q=0.9")
|
|
for _, want := range []string{`<html lang="de">`, unsubCopies["de"].ConfirmTitle, unsubCopies["de"].ConfirmButton} {
|
|
if !strings.Contains(de, want) {
|
|
t.Errorf("German page lacks %q:\n%s", want, de)
|
|
}
|
|
}
|
|
if ar := get("ar"); !strings.Contains(ar, `<html lang="ar" dir="rtl">`) || !strings.Contains(ar, unsubCopies["ar"].ConfirmTitle) {
|
|
t.Errorf("Arabic page is not right to left:\n%s", ar)
|
|
}
|
|
if en := get("xx"); !strings.Contains(en, `<html lang="en">`) || !strings.Contains(en, "Unsubscribe from these emails?") {
|
|
t.Errorf("an unknown language should fall back to English:\n%s", en)
|
|
}
|
|
// A bad link is refused in the recipient's language too.
|
|
w := httptest.NewRecorder()
|
|
r := httptest.NewRequest(http.MethodGet, "/unsubscribe/nope", nil)
|
|
r.Header.Set("Accept-Language", "fr")
|
|
newUnsubRouter(h).ServeHTTP(w, r)
|
|
if body := html.UnescapeString(w.Body.String()); !strings.Contains(body, unsubCopies["fr"].InvalidTitle) {
|
|
t.Errorf("invalid link page not in French:\n%s", body)
|
|
}
|
|
}
|
|
|
|
// The tracking service answers a malformed token and an unreachable backend
|
|
// itself, with the backend's wording in every language.
|
|
func TestTrackingUnsubPagesAgreeWithTheBackend(t *testing.T) {
|
|
src, err := os.ReadFile("../../../tracking/src/unsubscribe_i18n.rs")
|
|
if err != nil {
|
|
t.Fatalf("read tracking copies: %v", err)
|
|
}
|
|
rust := string(src)
|
|
for code, c := range unsubCopies {
|
|
at := strings.Index(rust, "\""+code+"\",\n")
|
|
if at < 0 {
|
|
t.Errorf("%s: no tracking copy", code)
|
|
continue
|
|
}
|
|
block := rust[at:]
|
|
if end := strings.Index(block, " ),"); end > 0 {
|
|
block = block[:end]
|
|
}
|
|
for _, s := range []string{c.InvalidTitle, c.ReplyBody, c.RetryTitle} {
|
|
if !strings.Contains(block, "\""+s+"\"") {
|
|
t.Errorf("%s: tracking copy lacks %q", code, s)
|
|
}
|
|
}
|
|
if strings.Contains(block, "rtl: true") != c.RTL {
|
|
t.Errorf("%s: tracking copy disagrees on direction", code)
|
|
}
|
|
}
|
|
}
|