mirror of https://github.com/jlelse/GoBlog
Refactorings, Lint fixes
This commit is contained in:
parent
6028c04a09
commit
56d3e22afa
|
@ -1,7 +1,7 @@
|
||||||
run:
|
run:
|
||||||
timeout: 5m
|
timeout: 5m
|
||||||
issue-exit-code: 0
|
issues-exit-code: 0
|
||||||
skip-tests: true
|
tests: false
|
||||||
build-tags:
|
build-tags:
|
||||||
- linux
|
- linux
|
||||||
- libsqlite3
|
- libsqlite3
|
||||||
|
@ -21,7 +21,7 @@ linters:
|
||||||
- bidichk
|
- bidichk
|
||||||
- bodyclose
|
- bodyclose
|
||||||
- containedctx
|
- containedctx
|
||||||
- contextcheck
|
# - contextcheck
|
||||||
- dupl
|
- dupl
|
||||||
- durationcheck
|
- durationcheck
|
||||||
- gofmt
|
- gofmt
|
||||||
|
@ -32,10 +32,10 @@ linters:
|
||||||
- unparam
|
- unparam
|
||||||
linters-settings:
|
linters-settings:
|
||||||
gosimple:
|
gosimple:
|
||||||
go: "1.19"
|
go: "1.21"
|
||||||
checks: ["all"]
|
checks: ["all"]
|
||||||
gostatichcheck:
|
staticcheck:
|
||||||
go: "1.19"
|
go: "1.21"
|
||||||
checks: ["all"]
|
checks: ["all"]
|
||||||
dupl:
|
dupl:
|
||||||
threshold: 125
|
threshold: 125
|
1
cache.go
1
cache.go
|
@ -171,6 +171,7 @@ func (c *cache) getCache(key string, next http.Handler, r *http.Request) *cacheI
|
||||||
}
|
}
|
||||||
// No cache available
|
// No cache available
|
||||||
// Make and use copy of r
|
// Make and use copy of r
|
||||||
|
//nolint:contextcheck
|
||||||
cr := r.Clone(valueOnlyContext{r.Context()})
|
cr := r.Clone(valueOnlyContext{r.Context()})
|
||||||
// Remove problematic headers
|
// Remove problematic headers
|
||||||
cr.Header.Del("If-Modified-Since")
|
cr.Header.Del("If-Modified-Since")
|
||||||
|
|
2
http.go
2
http.go
|
@ -263,7 +263,7 @@ func (a *goBlog) servePostsAliasesRedirects() http.HandlerFunc {
|
||||||
}
|
}
|
||||||
// Check if post or alias
|
// Check if post or alias
|
||||||
path := r.URL.Path
|
path := r.URL.Path
|
||||||
row, err := a.db.QueryRow(`
|
row, err := a.db.QueryRowContext(r.Context(), `
|
||||||
-- normal posts
|
-- normal posts
|
||||||
select 'post', status, visibility, 200 from posts where path = @path
|
select 'post', status, visibility, 200 from posts where path = @path
|
||||||
union all
|
union all
|
||||||
|
|
218
micropub_test.go
218
micropub_test.go
|
@ -15,12 +15,16 @@ import (
|
||||||
"go.hacdias.com/indielib/micropub"
|
"go.hacdias.com/indielib/micropub"
|
||||||
)
|
)
|
||||||
|
|
||||||
func Test_micropubQuery(t *testing.T) {
|
func createMicropubTestEnv(t *testing.T) *goBlog {
|
||||||
|
|
||||||
app := &goBlog{
|
app := &goBlog{
|
||||||
cfg: createDefaultTestConfig(t),
|
cfg: createDefaultTestConfig(t),
|
||||||
}
|
}
|
||||||
_ = app.initConfig(false)
|
_ = app.initConfig(false)
|
||||||
|
return app
|
||||||
|
}
|
||||||
|
|
||||||
|
func Test_micropubQuery(t *testing.T) {
|
||||||
|
app := createMicropubTestEnv(t)
|
||||||
|
|
||||||
// Create a test post with tags
|
// Create a test post with tags
|
||||||
err := app.createPost(&post{
|
err := app.createPost(&post{
|
||||||
|
@ -86,10 +90,7 @@ func Test_micropubQuery(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_micropubCreate(t *testing.T) {
|
func Test_micropubCreate(t *testing.T) {
|
||||||
app := &goBlog{
|
app := createMicropubTestEnv(t)
|
||||||
cfg: createDefaultTestConfig(t),
|
|
||||||
}
|
|
||||||
_ = app.initConfig(false)
|
|
||||||
|
|
||||||
// Modify settings for easier testing
|
// Modify settings for easier testing
|
||||||
bc := app.cfg.Blogs[app.cfg.DefaultBlog]
|
bc := app.cfg.Blogs[app.cfg.DefaultBlog]
|
||||||
|
@ -98,120 +99,127 @@ func Test_micropubCreate(t *testing.T) {
|
||||||
|
|
||||||
handler := addAllScopes(app.getMicropubImplementation().getHandler())
|
handler := addAllScopes(app.getMicropubImplementation().getHandler())
|
||||||
|
|
||||||
t.Run("Normal (JSON)", func(t *testing.T) {
|
t.Run("JSON", func(t *testing.T) {
|
||||||
postPath := "/create1"
|
testCases := []struct {
|
||||||
reqBody := `{"type":["h-entry"],"properties":{"content":["Test post"],"mp-slug":["create1"],"category":["Cool"]}}`
|
name, postPath, reqBody string
|
||||||
|
assertions func(t *testing.T, p *post)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Normal",
|
||||||
|
"/create1",
|
||||||
|
`{"type":["h-entry"],"properties":{"content":["Test post"],"mp-slug":["create1"],"category":["Cool"]}}`,
|
||||||
|
func(t *testing.T, p *post) {
|
||||||
|
assert.Equal(t, "Test post", p.Content)
|
||||||
|
assert.Equal(t, []string{"Cool"}, p.Parameters["tags"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Photo",
|
||||||
|
"/create2",
|
||||||
|
`{"type":["h-entry"],"properties":{"mp-slug":["create2"],"photo":["https://photos.example.com/123.jpg"]}}`,
|
||||||
|
func(t *testing.T, p *post) {
|
||||||
|
assert.Equal(t, "\n![](https://photos.example.com/123.jpg)", p.Content)
|
||||||
|
assert.Equal(t, []string{"https://photos.example.com/123.jpg"}, p.Parameters["images"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Photo with alternative text",
|
||||||
|
"/create3",
|
||||||
|
`{"type":["h-entry"],"properties":{"mp-slug":["create3"],"photo":[{
|
||||||
|
"value": "https://photos.example.com/123.jpg",
|
||||||
|
"alt": "This is a photo"
|
||||||
|
}]}}`,
|
||||||
|
func(t *testing.T, p *post) {
|
||||||
|
assert.Equal(t, "\n![This is a photo](https://photos.example.com/123.jpg \"This is a photo\")", p.Content)
|
||||||
|
assert.Equal(t, []string{"https://photos.example.com/123.jpg"}, p.Parameters["images"])
|
||||||
|
assert.Equal(t, []string{"This is a photo"}, p.Parameters["imagealts"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
for _, tc := range testCases {
|
||||||
req, _ := requests.New().Post().BodyReader(strings.NewReader(reqBody)).ContentType(contenttype.JSONUTF8).Request(context.Background())
|
tc := tc
|
||||||
handler.ServeHTTP(recorder, req)
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
result := recorder.Result()
|
recorder := httptest.NewRecorder()
|
||||||
require.Equal(t, http.StatusAccepted, result.StatusCode)
|
req, _ := requests.New().Post().BodyReader(strings.NewReader(tc.reqBody)).ContentType(contenttype.JSONUTF8).Request(context.Background())
|
||||||
require.Equal(t, "http://localhost:8080"+postPath, result.Header.Get("Location"))
|
handler.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
p, err := app.getPost(postPath)
|
result := recorder.Result()
|
||||||
require.NoError(t, err)
|
require.Equal(t, http.StatusAccepted, result.StatusCode)
|
||||||
|
require.Equal(t, "http://localhost:8080"+tc.postPath, result.Header.Get("Location"))
|
||||||
|
_ = result.Body.Close()
|
||||||
|
|
||||||
assert.Equal(t, "Test post", p.Content)
|
p, err := app.getPost(tc.postPath)
|
||||||
assert.Equal(t, []string{"Cool"}, p.Parameters["tags"])
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tc.assertions(t, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Photo (JSON)", func(t *testing.T) {
|
t.Run("Form", func(t *testing.T) {
|
||||||
postPath := "/create2"
|
testCases := []struct {
|
||||||
reqBody := `{"type":["h-entry"],"properties":{"mp-slug":["create2"],"photo":["https://photos.example.com/123.jpg"]}}`
|
name, postPath string
|
||||||
|
bodyForm url.Values
|
||||||
|
assertions func(t *testing.T, p *post)
|
||||||
|
}{
|
||||||
|
{
|
||||||
|
"Photo with alternative text",
|
||||||
|
"/create4",
|
||||||
|
url.Values{
|
||||||
|
"h": []string{"entry"},
|
||||||
|
"mp-slug": []string{"create4"},
|
||||||
|
"photo": []string{"https://photos.example.com/123.jpg"},
|
||||||
|
"mp-photo-alt": []string{"This is a photo"},
|
||||||
|
},
|
||||||
|
func(t *testing.T, p *post) {
|
||||||
|
assert.Equal(t, "\n![This is a photo](https://photos.example.com/123.jpg \"This is a photo\")", p.Content)
|
||||||
|
assert.Equal(t, []string{"https://photos.example.com/123.jpg"}, p.Parameters["images"])
|
||||||
|
assert.Equal(t, []string{"This is a photo"}, p.Parameters["imagealts"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Custom parameter",
|
||||||
|
"/create5",
|
||||||
|
url.Values{
|
||||||
|
"h": []string{"entry"},
|
||||||
|
"mp-slug": []string{"create5"},
|
||||||
|
"random": []string{"Abc", "Def"},
|
||||||
|
},
|
||||||
|
func(t *testing.T, p *post) {
|
||||||
|
assert.Equal(t, []string{"Abc", "Def"}, p.Parameters["random"])
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
for _, tc := range testCases {
|
||||||
req, _ := requests.New().Post().BodyReader(strings.NewReader(reqBody)).ContentType(contenttype.JSONUTF8).Request(context.Background())
|
tc := tc
|
||||||
handler.ServeHTTP(recorder, req)
|
t.Run(tc.name, func(t *testing.T) {
|
||||||
|
t.Parallel()
|
||||||
|
|
||||||
result := recorder.Result()
|
recorder := httptest.NewRecorder()
|
||||||
require.Equal(t, http.StatusAccepted, result.StatusCode)
|
req, _ := requests.New().Post().BodyForm(tc.bodyForm).Request(context.Background())
|
||||||
require.Equal(t, "http://localhost:8080"+postPath, result.Header.Get("Location"))
|
handler.ServeHTTP(recorder, req)
|
||||||
|
|
||||||
p, err := app.getPost(postPath)
|
result := recorder.Result()
|
||||||
require.NoError(t, err)
|
require.Equal(t, http.StatusAccepted, result.StatusCode)
|
||||||
|
require.Equal(t, "http://localhost:8080"+tc.postPath, result.Header.Get("Location"))
|
||||||
|
_ = result.Body.Close()
|
||||||
|
|
||||||
assert.Equal(t, "\n![](https://photos.example.com/123.jpg)", p.Content)
|
p, err := app.getPost(tc.postPath)
|
||||||
assert.Equal(t, []string{"https://photos.example.com/123.jpg"}, p.Parameters["images"])
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
tc.assertions(t, p)
|
||||||
|
})
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
t.Run("Photo with alternative text (JSON)", func(t *testing.T) {
|
|
||||||
postPath := "/create3"
|
|
||||||
reqBody := `{"type":["h-entry"],"properties":{"mp-slug":["create3"],"photo":[{
|
|
||||||
"value": "https://photos.example.com/123.jpg",
|
|
||||||
"alt": "This is a photo"
|
|
||||||
}]}}`
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
req, _ := requests.New().Post().BodyReader(strings.NewReader(reqBody)).ContentType(contenttype.JSONUTF8).Request(context.Background())
|
|
||||||
handler.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
result := recorder.Result()
|
|
||||||
require.Equal(t, http.StatusAccepted, result.StatusCode)
|
|
||||||
require.Equal(t, "http://localhost:8080"+postPath, result.Header.Get("Location"))
|
|
||||||
|
|
||||||
p, err := app.getPost(postPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "\n![This is a photo](https://photos.example.com/123.jpg \"This is a photo\")", p.Content)
|
|
||||||
assert.Equal(t, []string{"https://photos.example.com/123.jpg"}, p.Parameters["images"])
|
|
||||||
assert.Equal(t, []string{"This is a photo"}, p.Parameters["imagealts"])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Photo with alternative text (Form)", func(t *testing.T) {
|
|
||||||
postPath := "/create4"
|
|
||||||
|
|
||||||
bodyForm := url.Values{}
|
|
||||||
bodyForm["h"] = []string{"entry"}
|
|
||||||
bodyForm["mp-slug"] = []string{"create4"}
|
|
||||||
bodyForm["photo"] = []string{"https://photos.example.com/123.jpg"}
|
|
||||||
bodyForm["mp-photo-alt"] = []string{"This is a photo"}
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
req, _ := requests.New().Post().BodyForm(bodyForm).Request(context.Background())
|
|
||||||
handler.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
result := recorder.Result()
|
|
||||||
require.Equal(t, http.StatusAccepted, result.StatusCode)
|
|
||||||
require.Equal(t, "http://localhost:8080"+postPath, result.Header.Get("Location"))
|
|
||||||
|
|
||||||
p, err := app.getPost(postPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, "\n![This is a photo](https://photos.example.com/123.jpg \"This is a photo\")", p.Content)
|
|
||||||
assert.Equal(t, []string{"https://photos.example.com/123.jpg"}, p.Parameters["images"])
|
|
||||||
assert.Equal(t, []string{"This is a photo"}, p.Parameters["imagealts"])
|
|
||||||
})
|
|
||||||
|
|
||||||
t.Run("Custom parameter (Form)", func(t *testing.T) {
|
|
||||||
postPath := "/create5"
|
|
||||||
|
|
||||||
bodyForm := url.Values{}
|
|
||||||
bodyForm["h"] = []string{"entry"}
|
|
||||||
bodyForm["mp-slug"] = []string{"create5"}
|
|
||||||
bodyForm["random"] = []string{"Abc", "Def"}
|
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
|
||||||
req, _ := requests.New().Post().BodyForm(bodyForm).Request(context.Background())
|
|
||||||
handler.ServeHTTP(recorder, req)
|
|
||||||
|
|
||||||
result := recorder.Result()
|
|
||||||
require.Equal(t, http.StatusAccepted, result.StatusCode)
|
|
||||||
require.Equal(t, "http://localhost:8080"+postPath, result.Header.Get("Location"))
|
|
||||||
|
|
||||||
p, err := app.getPost(postPath)
|
|
||||||
require.NoError(t, err)
|
|
||||||
|
|
||||||
assert.Equal(t, []string{"Abc", "Def"}, p.Parameters["random"])
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_micropubUpdate(t *testing.T) {
|
func Test_micropubUpdate(t *testing.T) {
|
||||||
app := &goBlog{
|
app := createMicropubTestEnv(t)
|
||||||
cfg: createDefaultTestConfig(t),
|
|
||||||
}
|
|
||||||
_ = app.initConfig(false)
|
|
||||||
|
|
||||||
t.Run("Delete", func(t *testing.T) {
|
t.Run("Delete", func(t *testing.T) {
|
||||||
postPath := "/delete1"
|
postPath := "/delete1"
|
||||||
|
|
|
@ -49,7 +49,7 @@ func (a *goBlog) serveProfileImage(format profileImageFormat) http.HandlerFunc {
|
||||||
switch format {
|
switch format {
|
||||||
case profileImageFormatPNG:
|
case profileImageFormatPNG:
|
||||||
mediaType = "image/png"
|
mediaType = "image/png"
|
||||||
encode = func(output io.Writer, img *image.NRGBA, quality int) error {
|
encode = func(output io.Writer, img *image.NRGBA, _ int) error {
|
||||||
return imaging.Encode(output, img, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression))
|
return imaging.Encode(output, img, imaging.PNG, imaging.PNGCompressionLevel(png.BestCompression))
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
|
|
12
utils.go
12
utils.go
|
@ -233,11 +233,6 @@ func mBytesString(size int64) string {
|
||||||
return fmt.Sprintf("%.2f MB", datasize.ByteSize(size).MBytes())
|
return fmt.Sprintf("%.2f MB", datasize.ByteSize(size).MBytes())
|
||||||
}
|
}
|
||||||
|
|
||||||
func htmlText(s string) string {
|
|
||||||
text, _ := htmlTextFromReader(strings.NewReader(s))
|
|
||||||
return text
|
|
||||||
}
|
|
||||||
|
|
||||||
// Build policy to only allow a subset of HTML tags
|
// Build policy to only allow a subset of HTML tags
|
||||||
var textPolicy = bluemonday.StrictPolicy().
|
var textPolicy = bluemonday.StrictPolicy().
|
||||||
AllowElements("h1", "h2", "h3", "h4", "h5", "h6"). // Headers
|
AllowElements("h1", "h2", "h3", "h4", "h5", "h6"). // Headers
|
||||||
|
@ -411,10 +406,9 @@ func matchTimeDiffLocale(lang string) tdl.Locale {
|
||||||
timeDiffLocaleMutex.Lock()
|
timeDiffLocaleMutex.Lock()
|
||||||
defer timeDiffLocaleMutex.Unlock()
|
defer timeDiffLocaleMutex.Unlock()
|
||||||
supportedLangs := []string{"en", "de", "es", "hi", "pt", "ru", "zh-CN"}
|
supportedLangs := []string{"en", "de", "es", "hi", "pt", "ru", "zh-CN"}
|
||||||
var supportedTags []language.Tag
|
supportedTags := lo.Map(supportedLangs, func(lang string, _ int) language.Tag {
|
||||||
for _, lang := range supportedLangs {
|
return language.Make(lang)
|
||||||
supportedTags = append(supportedTags, language.Make(lang))
|
})
|
||||||
}
|
|
||||||
matcher := language.NewMatcher(supportedTags)
|
matcher := language.NewMatcher(supportedTags)
|
||||||
_, idx, _ := matcher.Match(language.Make(lang))
|
_, idx, _ := matcher.Match(language.Make(lang))
|
||||||
locale := tdl.Locale(supportedLangs[idx])
|
locale := tdl.Locale(supportedLangs[idx])
|
||||||
|
|
|
@ -88,6 +88,11 @@ func Test_urlHasExt(t *testing.T) {
|
||||||
}
|
}
|
||||||
|
|
||||||
func Test_htmlText(t *testing.T) {
|
func Test_htmlText(t *testing.T) {
|
||||||
|
htmlText := func(s string) string {
|
||||||
|
text, _ := htmlTextFromReader(strings.NewReader(s))
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
// Text without HTML
|
// Text without HTML
|
||||||
assert.Equal(t, "This is a test", htmlText("This is a test"))
|
assert.Equal(t, "This is a test", htmlText("This is a test"))
|
||||||
// Text without HTML and Emojis
|
// Text without HTML and Emojis
|
||||||
|
|
Loading…
Reference in New Issue