Files
nuntius/internal/email/acknowledgement_test.go
T
petrbalvin 3a38f00dc0
Test / test (push) Successful in 2m1s
Release / gates (push) Successful in 1m57s
Release / build (amd64, freebsd) (push) Successful in 1m26s
Release / build (amd64, linux) (push) Successful in 1m30s
Release / build (arm64, freebsd) (push) Successful in 1m28s
Release / build (arm64, linux) (push) Successful in 1m49s
Release / build (loong64, linux) (push) Successful in 1m30s
Release / build (riscv64, linux) (push) Successful in 1m29s
Release / release (push) Successful in 41s
feat: contact form backend for linux and freebsd servers
Assisted-by: GLM 5.3 Flash
2026-09-29 00:32:56 +02:00

93 lines
2.6 KiB
Go

// Copyright (c) 2026 Petr Balvín <opensource@petrbalvin.org> (https://petrbalvin.org)
// SPDX-License-Identifier: MIT
//go:build linux || freebsd
package email
import (
"strings"
"testing"
"sourcedock.dev/petrbalvin/nuntius/internal/config"
)
func ackForm() *config.Form {
return &config.Form{
Name: "contact",
Type: "contact",
To: "owner@example.com",
From: "noreply@example.com",
SubjectPrefix: new("nuntius"),
EmailBrand: new("nuntius"),
}
}
func TestComposeAcknowledgement(t *testing.T) {
msg, err := composeAcknowledgement(ackForm(), "jane@example.com")
if err != nil {
t.Fatalf("compose: %v", err)
}
raw := string(msg)
for _, want := range []string{
"From: noreply@example.com\r\n",
"To: jane@example.com\r\n",
"Subject: [nuntius/contact] Message received\r\n",
// The reply lands in the owner's inbox, not in the void.
"Reply-To: owner@example.com\r\n",
"Content-Type: multipart/alternative; boundary=nuntius-",
"your message to contact was received",
"Delivered by nuntius",
} {
if !strings.Contains(raw, want) {
t.Errorf("message missing %q", want)
}
}
// The submitted values are deliberately never echoed back.
if strings.Contains(raw, "jane@example.com\r\n\r\n") && strings.Count(raw, "jane@example.com") != 1 {
t.Errorf("message echoes the submitter context beyond the To header")
}
}
func TestComposeAcknowledgementHeaderInjection(t *testing.T) {
msg, err := composeAcknowledgement(ackForm(), "jane@example.com\r\nBcc: victim@example.com")
if err != nil {
t.Fatalf("compose: %v", err)
}
raw := string(msg)
// The whole string collapses into one To header line: no line starts
// with the injected header name.
for line := range strings.SplitSeq(raw, "\r\n") {
if strings.HasPrefix(line, "Bcc:") {
t.Errorf("a CR/LF in the recipient injected a header line %q", line)
}
}
if !strings.Contains(raw, "To: jane@example.com Bcc: victim@example.com\r\n") {
t.Errorf("the CR/LF was not neutralised into spaces")
}
}
func TestComposeAcknowledgementSubjectWithoutPrefix(t *testing.T) {
form := ackForm()
form.SubjectPrefix = new("")
msg, err := composeAcknowledgement(form, "jane@example.com")
if err != nil {
t.Fatalf("compose: %v", err)
}
if !strings.Contains(string(msg), "Subject: Message received\r\n") {
t.Errorf("subject with an empty prefix = %q, want the bare subject", subjectOf(t, string(msg)))
}
}
func subjectOf(t *testing.T, raw string) string {
t.Helper()
for line := range strings.SplitSeq(raw, "\r\n") {
if after, ok := strings.CutPrefix(line, "Subject: "); ok {
return after
}
}
t.Fatalf("no subject line in message")
return ""
}