mirror of https://github.com/jlelse/GoBlog
Add automatic test for contact submissions and some other small improvements
This commit is contained in:
parent
e9632720a3
commit
4857a82493
89
contact.go
89
contact.go
|
@ -26,68 +26,38 @@ func (a *goBlog) serveContactForm(w http.ResponseWriter, r *http.Request) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) sendContactSubmission(w http.ResponseWriter, r *http.Request) {
|
func (a *goBlog) sendContactSubmission(w http.ResponseWriter, r *http.Request) {
|
||||||
// Get form values
|
// Get blog
|
||||||
// Name
|
blog, bc := a.getBlog(r)
|
||||||
formName := cleanHTMLText(r.FormValue("name"))
|
// Get form values and build message
|
||||||
// Email
|
var message bytes.Buffer
|
||||||
formEmail := cleanHTMLText(r.FormValue("email"))
|
|
||||||
// Website
|
|
||||||
formWebsite := cleanHTMLText(r.FormValue("website"))
|
|
||||||
// Message
|
// Message
|
||||||
formMessage := cleanHTMLText(r.FormValue("message"))
|
formMessage := cleanHTMLText(r.FormValue("message"))
|
||||||
if formMessage == "" {
|
if formMessage == "" {
|
||||||
a.serveError(w, r, "Message is empty", http.StatusBadRequest)
|
a.serveError(w, r, "Message is empty", http.StatusBadRequest)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
// Build message
|
// Name
|
||||||
var message bytes.Buffer
|
if formName := cleanHTMLText(r.FormValue("name")); formName != "" {
|
||||||
if formName != "" {
|
_, _ = fmt.Fprintf(&message, "Name: %s\n", formName)
|
||||||
_, _ = fmt.Fprintf(&message, "Name: %s", formName)
|
|
||||||
_, _ = fmt.Fprintln(&message)
|
|
||||||
}
|
}
|
||||||
|
// Email
|
||||||
|
formEmail := cleanHTMLText(r.FormValue("email"))
|
||||||
if formEmail != "" {
|
if formEmail != "" {
|
||||||
_, _ = fmt.Fprintf(&message, "Email: %s", formEmail)
|
_, _ = fmt.Fprintf(&message, "Email: %s\n", formEmail)
|
||||||
_, _ = fmt.Fprintln(&message)
|
|
||||||
}
|
}
|
||||||
if formWebsite != "" {
|
// Website
|
||||||
_, _ = fmt.Fprintf(&message, "Website: %s", formWebsite)
|
if formWebsite := cleanHTMLText(r.FormValue("website")); formWebsite != "" {
|
||||||
_, _ = fmt.Fprintln(&message)
|
_, _ = fmt.Fprintf(&message, "Website: %s\n", formWebsite)
|
||||||
}
|
}
|
||||||
|
// Add line break if message is not empty
|
||||||
if message.Len() > 0 {
|
if message.Len() > 0 {
|
||||||
_, _ = fmt.Fprintln(&message)
|
_, _ = fmt.Fprintf(&message, "\n")
|
||||||
}
|
}
|
||||||
|
// Add message text to message
|
||||||
_, _ = message.WriteString(formMessage)
|
_, _ = message.WriteString(formMessage)
|
||||||
// Send submission
|
// Send submission
|
||||||
blog, bc := a.getBlog(r)
|
if err := a.sendContactEmail(bc.Contact, message.String(), formEmail); err != nil {
|
||||||
if cc := bc.Contact; cc != nil && cc.SMTPHost != "" && cc.EmailFrom != "" && cc.EmailTo != "" {
|
log.Println(err.Error())
|
||||||
// Build email
|
|
||||||
var email bytes.Buffer
|
|
||||||
if ef := cc.EmailFrom; ef != "" {
|
|
||||||
_, _ = fmt.Fprintf(&email, "From: %s <%s>", defaultIfEmpty(bc.Title, "GoBlog"), cc.EmailFrom)
|
|
||||||
_, _ = fmt.Fprintln(&email)
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(&email, "To: %s", cc.EmailTo)
|
|
||||||
_, _ = fmt.Fprintln(&email)
|
|
||||||
if formEmail != "" {
|
|
||||||
_, _ = fmt.Fprintf(&email, "Reply-To: %s", formEmail)
|
|
||||||
_, _ = fmt.Fprintln(&email)
|
|
||||||
}
|
|
||||||
_, _ = fmt.Fprintf(&email, "Date: %s", time.Now().UTC().Format(time.RFC1123Z))
|
|
||||||
_, _ = fmt.Fprintln(&email)
|
|
||||||
_, _ = fmt.Fprintln(&email, "Subject: New message")
|
|
||||||
_, _ = fmt.Fprintln(&email)
|
|
||||||
_, _ = fmt.Fprintln(&email, message.String())
|
|
||||||
// Send email
|
|
||||||
auth := smtp.PlainAuth("", cc.SMTPUser, cc.SMTPPassword, cc.SMTPHost)
|
|
||||||
port := cc.SMTPPort
|
|
||||||
if port == 0 {
|
|
||||||
port = 587
|
|
||||||
}
|
|
||||||
if err := smtp.SendMail(cc.SMTPHost+":"+strconv.Itoa(port), auth, cc.EmailFrom, []string{cc.EmailTo}, email.Bytes()); err != nil {
|
|
||||||
log.Println("Failed to send mail:", err.Error())
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Println("New contact submission not send as email, config missing")
|
|
||||||
}
|
}
|
||||||
// Send notification
|
// Send notification
|
||||||
a.sendNotification(message.String())
|
a.sendNotification(message.String())
|
||||||
|
@ -99,3 +69,26 @@ func (a *goBlog) sendContactSubmission(w http.ResponseWriter, r *http.Request) {
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (a *goBlog) sendContactEmail(cc *configContact, body, replyTo string) error {
|
||||||
|
// Check required config
|
||||||
|
if cc == nil || cc.SMTPHost == "" || cc.EmailFrom == "" || cc.EmailTo == "" {
|
||||||
|
return fmt.Errorf("email not send as config is missing")
|
||||||
|
}
|
||||||
|
// Build email
|
||||||
|
var email bytes.Buffer
|
||||||
|
_, _ = fmt.Fprintf(&email, "To: %s\n", cc.EmailTo)
|
||||||
|
if replyTo != "" {
|
||||||
|
_, _ = fmt.Fprintf(&email, "Reply-To: %s\n", replyTo)
|
||||||
|
}
|
||||||
|
_, _ = fmt.Fprintf(&email, "Date: %s\n", time.Now().UTC().Format(time.RFC1123Z))
|
||||||
|
_, _ = fmt.Fprintf(&email, "Subject: New message\n\n")
|
||||||
|
_, _ = fmt.Fprintf(&email, "%s\n", body)
|
||||||
|
// Send email using SMTP
|
||||||
|
auth := smtp.PlainAuth("", cc.SMTPUser, cc.SMTPPassword, cc.SMTPHost)
|
||||||
|
port := cc.SMTPPort
|
||||||
|
if port == 0 {
|
||||||
|
port = 587
|
||||||
|
}
|
||||||
|
return smtp.SendMail(cc.SMTPHost+":"+strconv.Itoa(port), auth, cc.EmailFrom, []string{cc.EmailTo}, email.Bytes())
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/http/httptest"
|
||||||
|
"net/url"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
"go.goblog.app/app/pkgs/contenttype"
|
||||||
|
"go.goblog.app/app/pkgs/mocksmtp"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_contact(t *testing.T) {
|
||||||
|
|
||||||
|
// Start the SMTP server
|
||||||
|
port, rd, err := mocksmtp.StartMockSMTPServer()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Init everything
|
||||||
|
app := &goBlog{
|
||||||
|
cfg: &config{
|
||||||
|
Db: &configDb{
|
||||||
|
File: filepath.Join(t.TempDir(), "test.db"),
|
||||||
|
},
|
||||||
|
Server: &configServer{
|
||||||
|
PublicAddress: "https://example.com",
|
||||||
|
},
|
||||||
|
Blogs: map[string]*configBlog{
|
||||||
|
"en": {
|
||||||
|
Lang: "en",
|
||||||
|
// Config for contact
|
||||||
|
Contact: &configContact{
|
||||||
|
Enabled: true,
|
||||||
|
SMTPPort: port,
|
||||||
|
SMTPHost: "127.0.0.1",
|
||||||
|
SMTPUser: "user",
|
||||||
|
SMTPPassword: "pass",
|
||||||
|
EmailTo: "to@example.org",
|
||||||
|
EmailFrom: "from@example.org",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
DefaultBlog: "en",
|
||||||
|
User: &configUser{},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_ = app.initDatabase(false)
|
||||||
|
app.initComponents(false)
|
||||||
|
|
||||||
|
// Make contact form request
|
||||||
|
rec := httptest.NewRecorder()
|
||||||
|
data := url.Values{}
|
||||||
|
data.Add("name", "Test User")
|
||||||
|
data.Add("email", "test@example.net")
|
||||||
|
data.Add("website", "https://test.example.com")
|
||||||
|
data.Add("message", "This is a test contact message")
|
||||||
|
req := httptest.NewRequest(http.MethodPost, "/contact", strings.NewReader(data.Encode()))
|
||||||
|
req.Header.Add(contentType, contenttype.WWWForm)
|
||||||
|
app.sendContactSubmission(rec, req.WithContext(context.WithValue(req.Context(), blogKey, "en")))
|
||||||
|
require.Equal(t, http.StatusOK, rec.Result().StatusCode)
|
||||||
|
|
||||||
|
// Check sent mail
|
||||||
|
received, err := rd()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, received, "This is a test contact message")
|
||||||
|
assert.Contains(t, received, "test@example.net")
|
||||||
|
assert.Contains(t, received, "https://test.example.com")
|
||||||
|
assert.Contains(t, received, "Test User")
|
||||||
|
|
||||||
|
}
|
6
go.mod
6
go.mod
|
@ -41,11 +41,11 @@ require (
|
||||||
github.com/thoas/go-funk v0.9.1
|
github.com/thoas/go-funk v0.9.1
|
||||||
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
github.com/tomnomnom/linkheader v0.0.0-20180905144013-02ca5825eb80
|
||||||
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
|
github.com/vcraescu/go-paginator v1.0.1-0.20201114172518-2cfc59fe05c2
|
||||||
github.com/yuin/goldmark v1.4.2
|
github.com/yuin/goldmark v1.4.3
|
||||||
// master
|
// master
|
||||||
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
|
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38
|
||||||
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
|
golang.org/x/crypto v0.0.0-20210921155107-089bfa567519
|
||||||
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3
|
golang.org/x/net v0.0.0-20211105192438-b53810dc28af
|
||||||
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
|
||||||
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b
|
||||||
tailscale.com v1.16.2
|
tailscale.com v1.16.2
|
||||||
|
@ -99,7 +99,7 @@ require (
|
||||||
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
go4.org/intern v0.0.0-20210108033219-3eb7198706b2 // indirect
|
||||||
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 // indirect
|
go4.org/mem v0.0.0-20201119185036-c04c5a6ff174 // indirect
|
||||||
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
go4.org/unsafe/assume-no-moving-gc v0.0.0-20201222180813-1025295fd063 // indirect
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b // indirect
|
golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 // indirect
|
||||||
golang.org/x/text v0.3.7 // indirect
|
golang.org/x/text v0.3.7 // indirect
|
||||||
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
golang.org/x/time v0.0.0-20210611083556-38a9dc6acbc6 // indirect
|
||||||
golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c // indirect
|
golang.zx2c4.com/wireguard v0.0.0-20210905140043-2ef39d47540c // indirect
|
||||||
|
|
12
go.sum
12
go.sum
|
@ -459,8 +459,8 @@ github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de
|
||||||
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=
|
||||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
github.com/yuin/goldmark v1.3.7/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||||
github.com/yuin/goldmark v1.4.2 h1:5qVKCqCRBaGz8EepBTi7pbIw8gGCFnB1Mi6kXU4dYv8=
|
github.com/yuin/goldmark v1.4.3 h1:eTEYYWtQLjK7+WK45Tk81OTkp/0UvAyqUj8flU0nTO4=
|
||||||
github.com/yuin/goldmark v1.4.2/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
github.com/yuin/goldmark v1.4.3/go.mod h1:rmuwmfZ0+bvzB24eSC//bk1R1Zp3hM0OXYv/G2LIilg=
|
||||||
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 h1:XZjLcLoTPNZuxppY3gwhRqo/T2XF6JMGFFdkAjX3w1w=
|
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38 h1:XZjLcLoTPNZuxppY3gwhRqo/T2XF6JMGFFdkAjX3w1w=
|
||||||
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
|
github.com/yuin/goldmark-emoji v1.0.2-0.20210607094911-0487583eca38/go.mod h1:RhP/RWpexdp+KHs7ghKnifRoIs/Bq4nDS7tRbCkOwKY=
|
||||||
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs=
|
||||||
|
@ -582,8 +582,8 @@ golang.org/x/net v0.0.0-20210525063256-abc453219eb5/go.mod h1:9nx3DQGgdP8bBQD5qx
|
||||||
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210903162142-ad29c8ab022f/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20210916014120-12bc252f5db8/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3 h1:VrJZAjbekhoRn7n5FBujY31gboH+iB3pdLxn3gE9FjU=
|
golang.org/x/net v0.0.0-20211105192438-b53810dc28af h1:SMeNJG/vclJ5wyBBd4xupMsSJIHTd1coW9g7q6KOjmY=
|
||||||
golang.org/x/net v0.0.0-20211101193420-4a448f8816b3/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
golang.org/x/net v0.0.0-20211105192438-b53810dc28af/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
|
||||||
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
|
||||||
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
golang.org/x/oauth2 v0.0.0-20190604053449-0f29369cfe45/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
|
||||||
|
@ -682,8 +682,8 @@ golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||||
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210806184541-e5e7981a1069/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210823070655-63515b42dcdf/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20210906170528-6f6e22806c34/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b h1:1VkfZQv42XQlA/jchYumAnv1UPo6RgF9rJFkTgZIxO4=
|
golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42 h1:G2DDmludOQZoWbpCr7OKDxnl478ZBGMcOhrv+ooX/Q4=
|
||||||
golang.org/x/sys v0.0.0-20211103235746-7861aae1554b/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
golang.org/x/sys v0.0.0-20211107104306-e0b2ad06fe42/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||||
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
|
||||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||||
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
golang.org/x/term v0.0.0-20210503060354-a79de5458b56 h1:b8jxX3zqjpqb2LklXPzKSGJhzyxCOZSz8ncv8Nv+y7w=
|
||||||
|
|
|
@ -9,11 +9,20 @@ type httpClient interface {
|
||||||
Do(req *http.Request) (*http.Response, error)
|
Do(req *http.Request) (*http.Response, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getHTTPClient() httpClient {
|
type appHttpClient struct {
|
||||||
return &http.Client{
|
hc *http.Client
|
||||||
Timeout: 5 * time.Minute,
|
}
|
||||||
Transport: &http.Transport{
|
|
||||||
DisableKeepAlives: true,
|
var _ httpClient = &appHttpClient{}
|
||||||
},
|
|
||||||
}
|
func (c *appHttpClient) Do(req *http.Request) (*http.Response, error) {
|
||||||
|
if c.hc == nil {
|
||||||
|
c.hc = &http.Client{
|
||||||
|
Timeout: 5 * time.Minute,
|
||||||
|
Transport: &http.Transport{
|
||||||
|
DisableKeepAlives: true,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return c.hc.Do(req)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
type fakeHttpClient struct {
|
type fakeHttpClient struct {
|
||||||
|
httpClient
|
||||||
req *http.Request
|
req *http.Request
|
||||||
res *http.Response
|
res *http.Response
|
||||||
handler http.Handler
|
handler http.Handler
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ httpClient = &fakeHttpClient{}
|
||||||
|
|
||||||
func (c *fakeHttpClient) Do(req *http.Request) (*http.Response, error) {
|
func (c *fakeHttpClient) Do(req *http.Request) (*http.Response, error) {
|
||||||
if c.handler == nil {
|
if c.handler == nil {
|
||||||
return nil, nil
|
return nil, nil
|
||||||
|
@ -39,7 +42,3 @@ func (c *fakeHttpClient) setFakeResponse(statusCode int, body string) {
|
||||||
_, _ = rw.Write([]byte(body))
|
_, _ = rw.Write([]byte(body))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
func getFakeHTTPClient() *fakeHttpClient {
|
|
||||||
return &fakeHttpClient{}
|
|
||||||
}
|
|
||||||
|
|
2
main.go
2
main.go
|
@ -50,7 +50,7 @@ func main() {
|
||||||
initGC()
|
initGC()
|
||||||
|
|
||||||
app := &goBlog{
|
app := &goBlog{
|
||||||
httpClient: getHTTPClient(),
|
httpClient: &appHttpClient{},
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize config
|
// Initialize config
|
||||||
|
|
|
@ -53,6 +53,8 @@ type shortpixel struct {
|
||||||
key string
|
key string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ mediaCompression = &shortpixel{}
|
||||||
|
|
||||||
func (sp *shortpixel) compress(url string, upload mediaStorageSaveFunc, hc httpClient) (location string, err error) {
|
func (sp *shortpixel) compress(url string, upload mediaStorageSaveFunc, hc httpClient) (location string, err error) {
|
||||||
// Check url
|
// Check url
|
||||||
fileExtension, allowed := urlHasExt(url, "jpg", "jpeg", "png")
|
fileExtension, allowed := urlHasExt(url, "jpg", "jpeg", "png")
|
||||||
|
@ -107,6 +109,8 @@ type tinify struct {
|
||||||
key string
|
key string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ mediaCompression = &tinify{}
|
||||||
|
|
||||||
func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc httpClient) (location string, err error) {
|
func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc httpClient) (location string, err error) {
|
||||||
// Check url
|
// Check url
|
||||||
fileExtension, allowed := urlHasExt(url, "jpg", "jpeg", "png")
|
fileExtension, allowed := urlHasExt(url, "jpg", "jpeg", "png")
|
||||||
|
@ -182,6 +186,8 @@ func (tf *tinify) compress(url string, upload mediaStorageSaveFunc, hc httpClien
|
||||||
type cloudflare struct {
|
type cloudflare struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var _ mediaCompression = &cloudflare{}
|
||||||
|
|
||||||
func (cf *cloudflare) compress(url string, upload mediaStorageSaveFunc, hc httpClient) (location string, err error) {
|
func (cf *cloudflare) compress(url string, upload mediaStorageSaveFunc, hc httpClient) (location string, err error) {
|
||||||
// Check url
|
// Check url
|
||||||
_, allowed := urlHasExt(url, "jpg", "jpeg", "png")
|
_, allowed := urlHasExt(url, "jpg", "jpeg", "png")
|
||||||
|
|
|
@ -27,7 +27,7 @@ func Test_compress(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
t.Run("Cloudflare", func(t *testing.T) {
|
t.Run("Cloudflare", func(t *testing.T) {
|
||||||
fakeClient := getFakeHTTPClient()
|
fakeClient := &fakeHttpClient{}
|
||||||
fakeClient.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
fakeClient.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
assert.Equal(t, "https://www.cloudflare.com/cdn-cgi/image/f=jpeg,q=75,metadata=none,fit=scale-down,w=2000,h=3000/https://example.com/original.jpg", r.URL.String())
|
assert.Equal(t, "https://www.cloudflare.com/cdn-cgi/image/f=jpeg,q=75,metadata=none,fit=scale-down,w=2000,h=3000/https://example.com/original.jpg", r.URL.String())
|
||||||
|
|
||||||
|
@ -43,7 +43,7 @@ func Test_compress(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Shortpixel", func(t *testing.T) {
|
t.Run("Shortpixel", func(t *testing.T) {
|
||||||
fakeClient := getFakeHTTPClient()
|
fakeClient := &fakeHttpClient{}
|
||||||
fakeClient.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
fakeClient.setHandler(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
assert.Equal(t, "https://api.shortpixel.com/v2/reducer-sync.php", r.URL.String())
|
assert.Equal(t, "https://api.shortpixel.com/v2/reducer-sync.php", r.URL.String())
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,118 @@
|
||||||
|
// This package contains code to mock an SMTP server and test mail sending.
|
||||||
|
|
||||||
|
package mocksmtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"net/textproto"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Inspired by https://play.golang.org/p/8mfrqNVWTPK
|
||||||
|
|
||||||
|
type ReceivedGetter func() (string, error)
|
||||||
|
|
||||||
|
func StartMockSMTPServer() (port int, rg ReceivedGetter, err error) {
|
||||||
|
|
||||||
|
// Default server responses
|
||||||
|
serverResponses := []string{
|
||||||
|
"220 smtp.example.com Service ready",
|
||||||
|
"250-ELHO -> ok",
|
||||||
|
"250-Show Options for ESMTP",
|
||||||
|
"250-8BITMIME",
|
||||||
|
"250-SIZE",
|
||||||
|
"250-AUTH LOGIN PLAIN",
|
||||||
|
"250 HELP",
|
||||||
|
"235 AUTH -> ok",
|
||||||
|
"250 MAIL FROM -> ok",
|
||||||
|
"250 RCPT TO -> ok",
|
||||||
|
"354 DATA",
|
||||||
|
"250 ... -> ok",
|
||||||
|
"221 QUIT",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Channel to check if error occured or done
|
||||||
|
var errOrDone = make(chan error)
|
||||||
|
|
||||||
|
// Received data buffer
|
||||||
|
var buffer bytes.Buffer
|
||||||
|
bufferWriter := bufio.NewWriter(&buffer)
|
||||||
|
|
||||||
|
// Start server on random port
|
||||||
|
mockSmtpServer, err := net.Listen("tcp", "127.0.0.1:0")
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get port from listener
|
||||||
|
_, portStr, err := net.SplitHostPort(mockSmtpServer.Addr().String())
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
port, err = strconv.Atoi(portStr)
|
||||||
|
if err != nil {
|
||||||
|
return 0, nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run mock SMTP server
|
||||||
|
go func() {
|
||||||
|
defer close(errOrDone)
|
||||||
|
defer bufferWriter.Flush()
|
||||||
|
defer mockSmtpServer.Close()
|
||||||
|
|
||||||
|
conn, err := mockSmtpServer.Accept()
|
||||||
|
if err != nil {
|
||||||
|
errOrDone <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
defer conn.Close()
|
||||||
|
|
||||||
|
tc := textproto.NewConn(conn)
|
||||||
|
defer tc.Close()
|
||||||
|
|
||||||
|
for _, res := range serverResponses {
|
||||||
|
if res == "" {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
_ = tc.PrintfLine("%s", res)
|
||||||
|
|
||||||
|
if len(res) >= 4 && res[3] == '-' {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if res == "221 QUIT" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
msg, err := tc.ReadLine()
|
||||||
|
if err != nil {
|
||||||
|
errOrDone <- err
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = fmt.Fprintf(bufferWriter, "%s\n", msg)
|
||||||
|
|
||||||
|
if res != "354 DATA" || msg == "." {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Define function to get received data
|
||||||
|
getReceivedData := func() (string, error) {
|
||||||
|
err, hasErr := <-errOrDone
|
||||||
|
if hasErr {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return buffer.String(), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return port and function
|
||||||
|
return port, getReceivedData, nil
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
package mocksmtp
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"net/smtp"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
"github.com/stretchr/testify/require"
|
||||||
|
)
|
||||||
|
|
||||||
|
func Test_mocksmtp(t *testing.T) {
|
||||||
|
// Start mock SMTP server
|
||||||
|
port, getRecievedData, err := StartMockSMTPServer()
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Send mail
|
||||||
|
err = smtp.SendMail(
|
||||||
|
fmt.Sprintf("127.0.0.1:%d", port),
|
||||||
|
smtp.PlainAuth("", "user", "pass", "127.0.0.1"),
|
||||||
|
"admin@smtp.example.com",
|
||||||
|
[]string{"user@smtp.example.com"},
|
||||||
|
[]byte("From: admin@smtp.example.com\nTo: user@smtp.example.com\nSubject: Test\nMIME-version: 1.0\nContent-Type: text/html; charset=\"UTF-8\"\n\nThis is a test mail."),
|
||||||
|
)
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
// Get received data
|
||||||
|
recievedData, err := getRecievedData()
|
||||||
|
require.NoError(t, err)
|
||||||
|
assert.Contains(t, recievedData, "From:")
|
||||||
|
assert.Contains(t, recievedData, "This is a test mail")
|
||||||
|
}
|
|
@ -72,7 +72,7 @@ func Test_configTelegram_generateHTML(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_configTelegram_send(t *testing.T) {
|
func Test_configTelegram_send(t *testing.T) {
|
||||||
fakeClient := getFakeHTTPClient()
|
fakeClient := &fakeHttpClient{}
|
||||||
|
|
||||||
tg := &configTelegram{
|
tg := &configTelegram{
|
||||||
Enabled: true,
|
Enabled: true,
|
||||||
|
@ -108,7 +108,7 @@ func Test_goBlog_initTelegram(t *testing.T) {
|
||||||
|
|
||||||
func Test_telegram(t *testing.T) {
|
func Test_telegram(t *testing.T) {
|
||||||
t.Run("Send post to Telegram", func(t *testing.T) {
|
t.Run("Send post to Telegram", func(t *testing.T) {
|
||||||
fakeClient := getFakeHTTPClient()
|
fakeClient := &fakeHttpClient{}
|
||||||
|
|
||||||
fakeClient.setFakeResponse(200, "")
|
fakeClient.setFakeResponse(200, "")
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ func Test_telegram(t *testing.T) {
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Telegram disabled", func(t *testing.T) {
|
t.Run("Telegram disabled", func(t *testing.T) {
|
||||||
fakeClient := getFakeHTTPClient()
|
fakeClient := &fakeHttpClient{}
|
||||||
|
|
||||||
fakeClient.setFakeResponse(200, "")
|
fakeClient.setFakeResponse(200, "")
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue