mirror of https://github.com/jlelse/GoBlog
Tests for blog stats
This commit is contained in:
parent
e62e4f32d6
commit
fcf299d1a1
|
@ -267,14 +267,13 @@ func (a *goBlog) apGetRemoteActor(iri string) (*asPerson, int, error) {
|
||||||
return actor, 0, nil
|
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))
|
rows, err := db.query("select distinct inbox from activitypub_followers where blog = @blog", sql.Named("blog", blog))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
inboxes := []string{}
|
|
||||||
for rows.Next() {
|
|
||||||
var inbox string
|
var inbox string
|
||||||
|
for rows.Next() {
|
||||||
err = rows.Scan(&inbox)
|
err = rows.Scan(&inbox)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
|
|
|
@ -44,9 +44,7 @@ func Test_authMiddleware(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = app.initDatabase(false)
|
_ = app.initDatabase(false)
|
||||||
app.initSessions()
|
app.initComponents()
|
||||||
_ = app.initTemplateStrings()
|
|
||||||
_ = app.initRendering()
|
|
||||||
|
|
||||||
app.d = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
app.d = http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
_, _ = rw.Write([]byte("ABC Test"))
|
_, _ = rw.Write([]byte("ABC Test"))
|
||||||
|
|
|
@ -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)
|
||||||
|
})
|
||||||
|
|
||||||
|
}
|
|
@ -31,9 +31,7 @@ func Test_captchaMiddleware(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = app.initDatabase(false)
|
_ = app.initDatabase(false)
|
||||||
app.initSessions()
|
app.initComponents()
|
||||||
_ = app.initTemplateStrings()
|
|
||||||
_ = app.initRendering()
|
|
||||||
|
|
||||||
h := app.captchaMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
h := app.captchaMiddleware(http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
|
||||||
_, _ = rw.Write([]byte("ABC Test"))
|
_, _ = rw.Write([]byte("ABC Test"))
|
||||||
|
|
|
@ -31,10 +31,7 @@ func Test_errors(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
_ = app.initDatabase(false)
|
_ = app.initDatabase(false)
|
||||||
app.initMarkdown()
|
app.initComponents()
|
||||||
app.initSessions()
|
|
||||||
_ = app.initTemplateStrings()
|
|
||||||
_ = app.initRendering()
|
|
||||||
|
|
||||||
t.Run("Test 404, no HTML", func(t *testing.T) {
|
t.Run("Test 404, no HTML", func(t *testing.T) {
|
||||||
h := http.HandlerFunc(app.serve404)
|
h := http.HandlerFunc(app.serve404)
|
||||||
|
|
12
hooks.go
12
hooks.go
|
@ -21,7 +21,8 @@ type postHookFunc func(*post)
|
||||||
|
|
||||||
func (a *goBlog) postPostHooks(p *post) {
|
func (a *goBlog) postPostHooks(p *post) {
|
||||||
// Hooks after post published
|
// 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) {
|
go func(p *post, cmdTmplString string) {
|
||||||
a.cfg.Hooks.executeTemplateCommand("post-post", cmdTmplString, map[string]interface{}{
|
a.cfg.Hooks.executeTemplateCommand("post-post", cmdTmplString, map[string]interface{}{
|
||||||
"URL": a.fullPostURL(p),
|
"URL": a.fullPostURL(p),
|
||||||
|
@ -29,6 +30,7 @@ func (a *goBlog) postPostHooks(p *post) {
|
||||||
})
|
})
|
||||||
}(p, cmdTmplString)
|
}(p, cmdTmplString)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for _, f := range a.pPostHooks {
|
for _, f := range a.pPostHooks {
|
||||||
go f(p)
|
go f(p)
|
||||||
}
|
}
|
||||||
|
@ -36,7 +38,8 @@ func (a *goBlog) postPostHooks(p *post) {
|
||||||
|
|
||||||
func (a *goBlog) postUpdateHooks(p *post) {
|
func (a *goBlog) postUpdateHooks(p *post) {
|
||||||
// Hooks after post updated
|
// 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) {
|
go func(p *post, cmdTmplString string) {
|
||||||
a.cfg.Hooks.executeTemplateCommand("post-update", cmdTmplString, map[string]interface{}{
|
a.cfg.Hooks.executeTemplateCommand("post-update", cmdTmplString, map[string]interface{}{
|
||||||
"URL": a.fullPostURL(p),
|
"URL": a.fullPostURL(p),
|
||||||
|
@ -44,13 +47,15 @@ func (a *goBlog) postUpdateHooks(p *post) {
|
||||||
})
|
})
|
||||||
}(p, cmdTmplString)
|
}(p, cmdTmplString)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for _, f := range a.pUpdateHooks {
|
for _, f := range a.pUpdateHooks {
|
||||||
go f(p)
|
go f(p)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) postDeleteHooks(p *post) {
|
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) {
|
go func(p *post, cmdTmplString string) {
|
||||||
a.cfg.Hooks.executeTemplateCommand("post-delete", cmdTmplString, map[string]interface{}{
|
a.cfg.Hooks.executeTemplateCommand("post-delete", cmdTmplString, map[string]interface{}{
|
||||||
"URL": a.fullPostURL(p),
|
"URL": a.fullPostURL(p),
|
||||||
|
@ -58,6 +63,7 @@ func (a *goBlog) postDeleteHooks(p *post) {
|
||||||
})
|
})
|
||||||
}(p, cmdTmplString)
|
}(p, cmdTmplString)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
for _, f := range a.pDeleteHooks {
|
for _, f := range a.pDeleteHooks {
|
||||||
go f(p)
|
go f(p)
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,7 +17,3 @@ func getHTTPClient() httpClient {
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (a *goBlog) initHTTPClient() {
|
|
||||||
a.httpClient = getHTTPClient()
|
|
||||||
}
|
|
||||||
|
|
54
main.go
54
main.go
|
@ -46,8 +46,12 @@ func main() {
|
||||||
}()
|
}()
|
||||||
}
|
}
|
||||||
|
|
||||||
app := &goBlog{}
|
// Init regular garbage collection
|
||||||
app.initHTTPClient()
|
initGC()
|
||||||
|
|
||||||
|
app := &goBlog{
|
||||||
|
httpClient: getHTTPClient(),
|
||||||
|
}
|
||||||
|
|
||||||
// Initialize config
|
// Initialize config
|
||||||
if err = app.initConfig(); err != nil {
|
if err = app.initConfig(); err != nil {
|
||||||
|
@ -79,9 +83,6 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init regular garbage collection
|
|
||||||
initGC()
|
|
||||||
|
|
||||||
// Execute pre-start hooks
|
// Execute pre-start hooks
|
||||||
app.preStartHooks()
|
app.preStartHooks()
|
||||||
|
|
||||||
|
@ -91,18 +92,36 @@ func main() {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
log.Println("Initialize components...")
|
|
||||||
|
|
||||||
app.initMarkdown()
|
|
||||||
|
|
||||||
// Link check tool after init of markdown
|
// Link check tool after init of markdown
|
||||||
if len(os.Args) >= 2 && os.Args[1] == "check" {
|
if len(os.Args) >= 2 && os.Args[1] == "check" {
|
||||||
|
app.initMarkdown()
|
||||||
app.checkAllExternalLinks()
|
app.checkAllExternalLinks()
|
||||||
app.shutdown.ShutdownAndWait()
|
app.shutdown.ShutdownAndWait()
|
||||||
return
|
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
|
if err = app.initTemplateAssets(); err != nil { // Needs minify
|
||||||
app.logErrAndQuit("Failed to init template assets:", err.Error())
|
app.logErrAndQuit("Failed to init template assets:", err.Error())
|
||||||
return
|
return
|
||||||
|
@ -135,21 +154,8 @@ func main() {
|
||||||
app.initTelegram()
|
app.initTelegram()
|
||||||
app.initBlogStats()
|
app.initBlogStats()
|
||||||
app.initSessions()
|
app.initSessions()
|
||||||
|
// Log finish
|
||||||
// Start cron hooks
|
|
||||||
app.startHourlyHooks()
|
|
||||||
|
|
||||||
log.Println("Initialized components")
|
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{}) {
|
func (a *goBlog) logErrAndQuit(v ...interface{}) {
|
||||||
|
|
|
@ -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)
|
p.Slug = fmt.Sprintf("%v-%02d-%02d-%v", now.Year(), int(now.Month()), now.Day(), random)
|
||||||
}
|
}
|
||||||
published := timeNoErr(dateparse.ParseLocal(p.Published))
|
published := timeNoErr(dateparse.ParseLocal(p.Published))
|
||||||
pathTmplString := a.cfg.Blogs[p.Blog].Sections[p.Section].PathTemplate
|
pathTmplString := defaultIfEmpty(
|
||||||
if pathTmplString == "" {
|
a.cfg.Blogs[p.Blog].Sections[p.Section].PathTemplate,
|
||||||
return errors.New("path template empty")
|
"{{printf \""+a.getRelativePath(p.Blog, "/%v/%02d/%02d/%v")+"\" .Section .Year .Month .Slug}}",
|
||||||
}
|
)
|
||||||
pathTmpl, err := template.New("location").Parse(pathTmplString)
|
pathTmpl, err := template.New("location").Parse(pathTmplString)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("failed to parse location template")
|
return errors.New("failed to parse location template")
|
||||||
|
|
|
@ -35,7 +35,10 @@ func (a *goBlog) sendWebmentions(p *post) error {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
links = append(links, contentLinks...)
|
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) {
|
for _, link := range funk.UniqString(links) {
|
||||||
if link == "" {
|
if link == "" {
|
||||||
continue
|
continue
|
||||||
|
|
Loading…
Reference in New Issue