Tests for blog stats

This commit is contained in:
Jan-Lukas Else 2021-07-30 15:43:13 +02:00
parent e62e4f32d6
commit fcf299d1a1
10 changed files with 223 additions and 67 deletions

View File

@ -267,14 +267,13 @@ func (a *goBlog) apGetRemoteActor(iri string) (*asPerson, int, error) {
return actor, 0, nil
}
func (db *database) apGetAllInboxes(blog string) ([]string, error) {
func (db *database) apGetAllInboxes(blog string) (inboxes []string, err error) {
rows, err := db.query("select distinct inbox from activitypub_followers where blog = @blog", sql.Named("blog", blog))
if err != nil {
return nil, err
}
inboxes := []string{}
for rows.Next() {
var inbox string
for rows.Next() {
err = rows.Scan(&inbox)
if err != nil {
return nil, err

View File

@ -44,9 +44,7 @@ func Test_authMiddleware(t *testing.T) {
}
_ = app.initDatabase(false)
app.initSessions()
_ = app.initTemplateStrings()
_ = app.initRendering()
app.initComponents()
app.d = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
_, _ = rw.Write([]byte("ABC Test"))

153
blogstats_test.go Normal file
View File

@ -0,0 +1,153 @@
package main
import (
"context"
"io"
"net/http"
"net/http/httptest"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"go.goblog.app/app/pkgs/contenttype"
)
func Test_blogStats(t *testing.T) {
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",
BlogStats: &configBlogStats{
Enabled: true,
Path: "/stats",
},
Sections: map[string]*configSection{
"test": {},
},
},
},
DefaultBlog: "en",
User: &configUser{},
Webmention: &configWebmention{
DisableSending: true,
},
},
}
_ = app.initDatabase(false)
app.initComponents()
// Insert post
err := app.createPost(&post{
Content: "This is a simple **test** post",
Blog: "en",
Section: "test",
Published: "2020-06-01",
Status: statusPublished,
})
require.NoError(t, err)
err = app.createPost(&post{
Content: "This is another simple **test** post",
Blog: "en",
Section: "test",
Published: "2021-05-01",
Status: statusPublished,
})
require.NoError(t, err)
// Test stats
sd, err := app.db.getBlogStats("en")
require.NoError(t, err)
require.NotNil(t, sd)
require.NotNil(t, sd.Total)
assert.Equal(t, "2", sd.Total.Posts)
assert.Equal(t, "12", sd.Total.Words)
assert.Equal(t, "48", sd.Total.Chars)
// 2021
require.NotNil(t, sd.Years)
row := sd.Years[0]
require.NotNil(t, row)
assert.Equal(t, "2021", row.Name)
assert.Equal(t, "1", row.Posts)
assert.Equal(t, "6", row.Words)
assert.Equal(t, "27", row.Chars)
// 2021-05
require.NotNil(t, sd.Months)
require.NotEmpty(t, sd.Months["2021"])
row = sd.Months["2021"][0]
require.NotNil(t, row)
assert.Equal(t, "05", row.Name)
assert.Equal(t, "1", row.Posts)
assert.Equal(t, "6", row.Words)
assert.Equal(t, "27", row.Chars)
// 2020
require.NotNil(t, sd.Years)
row = sd.Years[1]
require.NotNil(t, row)
assert.Equal(t, "2020", row.Name)
assert.Equal(t, "1", row.Posts)
assert.Equal(t, "6", row.Words)
assert.Equal(t, "21", row.Chars)
// Test if cache exists
assert.NotNil(t, app.db.loadBlogStatsCache("en"))
// Test HTML
t.Run("Test stats table", func(t *testing.T) {
h := http.HandlerFunc(app.serveBlogStatsTable)
req := httptest.NewRequest(http.MethodGet, "/abc", nil)
req = req.WithContext(context.WithValue(req.Context(), blogKey, "en"))
rec := httptest.NewRecorder()
h(rec, req)
res := rec.Result()
resBody, _ := io.ReadAll(res.Body)
_ = res.Body.Close()
resString := string(resBody)
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Contains(t, resString, "class=statsyear data-year=2021")
assert.Contains(t, res.Header.Get(contentType), contenttype.HTML)
})
t.Run("Test stats page", func(t *testing.T) {
h := http.HandlerFunc(app.serveBlogStats)
req := httptest.NewRequest(http.MethodGet, "/abc", nil)
req = req.WithContext(context.WithValue(req.Context(), blogKey, "en"))
rec := httptest.NewRecorder()
h(rec, req)
res := rec.Result()
resBody, _ := io.ReadAll(res.Body)
_ = res.Body.Close()
resString := string(resBody)
assert.Equal(t, http.StatusOK, res.StatusCode)
assert.Contains(t, resString, "data-table=/stats.table.html")
assert.Contains(t, res.Header.Get(contentType), contenttype.HTML)
})
}

View File

@ -31,9 +31,7 @@ func Test_captchaMiddleware(t *testing.T) {
}
_ = app.initDatabase(false)
app.initSessions()
_ = app.initTemplateStrings()
_ = app.initRendering()
app.initComponents()
h := app.captchaMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
_, _ = rw.Write([]byte("ABC Test"))

View File

@ -31,10 +31,7 @@ func Test_errors(t *testing.T) {
}
_ = app.initDatabase(false)
app.initMarkdown()
app.initSessions()
_ = app.initTemplateStrings()
_ = app.initRendering()
app.initComponents()
t.Run("Test 404, no HTML", func(t *testing.T) {
h := http.HandlerFunc(app.serve404)

View File

@ -21,7 +21,8 @@ type postHookFunc func(*post)
func (a *goBlog) postPostHooks(p *post) {
// Hooks after post published
for _, cmdTmplString := range a.cfg.Hooks.PostPost {
if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostPost {
go func(p *post, cmdTmplString string) {
a.cfg.Hooks.executeTemplateCommand("post-post", cmdTmplString, map[string]interface{}{
"URL": a.fullPostURL(p),
@ -29,6 +30,7 @@ func (a *goBlog) postPostHooks(p *post) {
})
}(p, cmdTmplString)
}
}
for _, f := range a.pPostHooks {
go f(p)
}
@ -36,7 +38,8 @@ func (a *goBlog) postPostHooks(p *post) {
func (a *goBlog) postUpdateHooks(p *post) {
// Hooks after post updated
for _, cmdTmplString := range a.cfg.Hooks.PostUpdate {
if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostUpdate {
go func(p *post, cmdTmplString string) {
a.cfg.Hooks.executeTemplateCommand("post-update", cmdTmplString, map[string]interface{}{
"URL": a.fullPostURL(p),
@ -44,13 +47,15 @@ func (a *goBlog) postUpdateHooks(p *post) {
})
}(p, cmdTmplString)
}
}
for _, f := range a.pUpdateHooks {
go f(p)
}
}
func (a *goBlog) postDeleteHooks(p *post) {
for _, cmdTmplString := range a.cfg.Hooks.PostDelete {
if hc := a.cfg.Hooks; hc != nil {
for _, cmdTmplString := range hc.PostDelete {
go func(p *post, cmdTmplString string) {
a.cfg.Hooks.executeTemplateCommand("post-delete", cmdTmplString, map[string]interface{}{
"URL": a.fullPostURL(p),
@ -58,6 +63,7 @@ func (a *goBlog) postDeleteHooks(p *post) {
})
}(p, cmdTmplString)
}
}
for _, f := range a.pDeleteHooks {
go f(p)
}

View File

@ -17,7 +17,3 @@ func getHTTPClient() httpClient {
},
}
}
func (a *goBlog) initHTTPClient() {
a.httpClient = getHTTPClient()
}

54
main.go
View File

@ -46,8 +46,12 @@ func main() {
}()
}
app := &goBlog{}
app.initHTTPClient()
// Init regular garbage collection
initGC()
app := &goBlog{
httpClient: getHTTPClient(),
}
// Initialize config
if err = app.initConfig(); err != nil {
@ -79,9 +83,6 @@ func main() {
return
}
// Init regular garbage collection
initGC()
// Execute pre-start hooks
app.preStartHooks()
@ -91,18 +92,36 @@ func main() {
return
}
log.Println("Initialize components...")
app.initMarkdown()
// Link check tool after init of markdown
if len(os.Args) >= 2 && os.Args[1] == "check" {
app.initMarkdown()
app.checkAllExternalLinks()
app.shutdown.ShutdownAndWait()
return
}
// More initializations
// Initialize components
app.initComponents()
// Start cron hooks
app.startHourlyHooks()
// Start the server
err = app.startServer()
if err != nil {
app.logErrAndQuit("Failed to start server(s):", err.Error())
return
}
// Wait till everything is shutdown
app.shutdown.Wait()
}
func (app *goBlog) initComponents() {
var err error
// Log start
log.Println("Initialize components...")
app.initMarkdown()
if err = app.initTemplateAssets(); err != nil { // Needs minify
app.logErrAndQuit("Failed to init template assets:", err.Error())
return
@ -135,21 +154,8 @@ func main() {
app.initTelegram()
app.initBlogStats()
app.initSessions()
// Start cron hooks
app.startHourlyHooks()
// Log finish
log.Println("Initialized components")
// Start the server
err = app.startServer()
if err != nil {
app.logErrAndQuit("Failed to start server(s):", err.Error())
return
}
// Wait till everything is shutdown
app.shutdown.Wait()
}
func (a *goBlog) logErrAndQuit(v ...interface{}) {

View File

@ -80,10 +80,10 @@ func (a *goBlog) checkPost(p *post) (err error) {
p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), random)
}
published := timeNoErr(dateparse.ParseLocal(p.Published))
pathTmplString := a.cfg.Blogs[p.Blog].Sections[p.Section].PathTemplate
if pathTmplString == "" {
return errors.New("path template empty")
}
pathTmplString := defaultIfEmpty(
a.cfg.Blogs[p.Blog].Sections[p.Section].PathTemplate,
"{{printf \""+a.getRelativePath(p.Blog, "/%v/%02d/%02d/%v")+"\" .Section .Year .Month .Slug}}",
)
pathTmpl, err := template.New("location").Parse(pathTmplString)
if err != nil {
return errors.New("failed to parse location template")

View File

@ -35,7 +35,10 @@ func (a *goBlog) sendWebmentions(p *post) error {
return err
}
links = append(links, contentLinks...)
links = append(links, p.firstParameter("link"), p.firstParameter(a.cfg.Micropub.LikeParam), p.firstParameter(a.cfg.Micropub.ReplyParam), p.firstParameter(a.cfg.Micropub.BookmarkParam))
links = append(links, p.firstParameter("link"))
if mpc := a.cfg.Micropub; mpc != nil {
links = append(links, p.firstParameter(a.cfg.Micropub.LikeParam), p.firstParameter(a.cfg.Micropub.ReplyParam), p.firstParameter(a.cfg.Micropub.BookmarkParam))
}
for _, link := range funk.UniqString(links) {
if link == "" {
continue