mirror of
https://github.com/thomiceli/opengist.git
synced 2026-08-07 07:14:49 +00:00
528 lines
15 KiB
Go
528 lines
15 KiB
Go
package gist_test
|
|
|
|
import (
|
|
"encoding/json"
|
|
"io"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/thomiceli/opengist/internal/db"
|
|
webtest "github.com/thomiceli/opengist/internal/web/test"
|
|
)
|
|
|
|
func TestGistIndex(t *testing.T) {
|
|
s := webtest.Setup(t)
|
|
defer webtest.Teardown(t)
|
|
|
|
s.Register(t, "thomas")
|
|
s.Register(t, "alice")
|
|
|
|
t.Run("Public", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
s.Request(t, "GET", "/"+username+"/"+identifier, nil, 200)
|
|
})
|
|
|
|
t.Run("NonExistentRevision", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+"/rev/nonexistent", nil, 404)
|
|
})
|
|
|
|
t.Run("NonExistentGist", func(t *testing.T) {
|
|
s.Request(t, "GET", "/thomas/nonexistent", nil, 404)
|
|
})
|
|
|
|
t.Run("Unlisted", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "1")
|
|
|
|
s.Login(t, "thomas")
|
|
s.Request(t, "GET", "/"+username+"/"+identifier, nil, 200)
|
|
|
|
s.Login(t, "alice")
|
|
s.Request(t, "GET", "/"+username+"/"+identifier, nil, 200)
|
|
|
|
s.Logout()
|
|
s.Request(t, "GET", "/"+username+"/"+identifier, nil, 200)
|
|
})
|
|
|
|
t.Run("Private", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "2")
|
|
|
|
s.Login(t, "thomas")
|
|
s.Request(t, "GET", "/"+username+"/"+identifier, nil, 200)
|
|
|
|
s.Login(t, "alice")
|
|
s.Request(t, "GET", "/"+username+"/"+identifier, nil, 404)
|
|
|
|
s.Logout()
|
|
s.Request(t, "GET", "/"+username+"/"+identifier, nil, 404)
|
|
})
|
|
|
|
t.Run("SpecificRevision", func(t *testing.T) {
|
|
_, gist, username, identifier := s.CreateGist(t, "0")
|
|
|
|
s.Login(t, "thomas")
|
|
s.Request(t, "POST", "/"+username+"/"+identifier+"/edit", url.Values{
|
|
"title": {"Test"},
|
|
"name": {"file.txt"},
|
|
"content": {"updated content"},
|
|
}, 302)
|
|
|
|
files, _, err := gist.Files("HEAD", false)
|
|
require.NoError(t, err)
|
|
found := false
|
|
for _, f := range files {
|
|
if f.Filename == "file.txt" {
|
|
require.Equal(t, "updated content", f.Content)
|
|
found = true
|
|
}
|
|
}
|
|
require.True(t, found)
|
|
|
|
commits, err := gist.Log("HEAD", 0, 11)
|
|
require.NoError(t, err)
|
|
require.Len(t, commits, 2)
|
|
|
|
filesOld, _, err := gist.Files(commits[1].Hash, false)
|
|
require.NoError(t, err)
|
|
for _, f := range filesOld {
|
|
if f.Filename == "file.txt" {
|
|
require.Equal(t, "hello world", f.Content)
|
|
}
|
|
}
|
|
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+"/rev/HEAD", nil, 200)
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+"/rev/"+commits[1].Hash, nil, 200)
|
|
})
|
|
}
|
|
|
|
func TestGistSocialMetaTags(t *testing.T) {
|
|
s := webtest.Setup(t)
|
|
defer webtest.Teardown(t)
|
|
|
|
s.Register(t, "thomas")
|
|
|
|
t.Run("RendersOpenGraphAndTwitterCards", func(t *testing.T) {
|
|
_, gist, username, identifier := s.CreateGist(t, "0")
|
|
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier, nil, 200)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
html := string(body)
|
|
|
|
// Open Graph tags
|
|
assert.Contains(t, html, `property="og:title"`)
|
|
assert.Contains(t, html, `property="og:description"`)
|
|
assert.Contains(t, html, `property="og:type" content="article"`)
|
|
assert.Contains(t, html, `property="og:url"`)
|
|
assert.Contains(t, html, `property="og:site_name" content="Opengist"`)
|
|
assert.Contains(t, html, `property="og:image"`)
|
|
|
|
// Twitter Card tags
|
|
assert.Contains(t, html, `name="twitter:card" content="summary"`)
|
|
assert.Contains(t, html, `name="twitter:title"`)
|
|
assert.Contains(t, html, `name="twitter:description"`)
|
|
assert.Contains(t, html, `name="twitter:image"`)
|
|
|
|
// The card reflects the gist owner/title and points at the gist URL
|
|
assert.Contains(t, html, gist.Title)
|
|
assert.Contains(t, html, username+"/"+identifier)
|
|
})
|
|
|
|
t.Run("UnlistedGistAlsoHasMetaTags", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "1")
|
|
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier, nil, 200)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(body), `property="og:title"`)
|
|
})
|
|
}
|
|
|
|
func TestPreview(t *testing.T) {
|
|
s := webtest.Setup(t)
|
|
defer webtest.Teardown(t)
|
|
|
|
s.Register(t, "thomas")
|
|
|
|
t.Run("Markdown", func(t *testing.T) {
|
|
s.Login(t, "thomas")
|
|
|
|
resp := s.Request(t, "POST", "/preview", url.Values{
|
|
"content": {"# Hello\n\nThis is **bold** and *italic*."},
|
|
}, 200)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
html := string(body)
|
|
require.Contains(t, html, "<h1>")
|
|
require.Contains(t, html, "Hello")
|
|
require.Contains(t, html, "<strong>bold</strong>")
|
|
require.Contains(t, html, "<em>italic</em>")
|
|
})
|
|
|
|
t.Run("NoAuth", func(t *testing.T) {
|
|
s.Logout()
|
|
s.Request(t, "POST", "/preview", url.Values{
|
|
"content": {"# Hello"},
|
|
}, 302)
|
|
})
|
|
}
|
|
|
|
func TestGistJson(t *testing.T) {
|
|
s := webtest.Setup(t)
|
|
defer webtest.Teardown(t)
|
|
|
|
s.Register(t, "thomas")
|
|
s.Register(t, "alice")
|
|
|
|
t.Run("Public", func(t *testing.T) {
|
|
_, gist, username, identifier := s.CreateGist(t, "0")
|
|
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier+".json", nil, 200)
|
|
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]interface{}
|
|
err = json.Unmarshal(body, &result)
|
|
require.NoError(t, err)
|
|
t.Helper()
|
|
|
|
require.Equal(t, username, result["owner"])
|
|
require.Equal(t, identifier, result["id"])
|
|
require.Equal(t, gist.Uuid, result["uuid"])
|
|
require.Equal(t, gist.Title, result["title"])
|
|
require.Equal(t, "public", result["visibility"])
|
|
require.Equal(t, []interface{}{"hello", "opengist"}, result["topics"])
|
|
require.Equal(t, []interface{}{
|
|
map[string]interface{}{
|
|
"content": "hello world",
|
|
"filename": "file.txt",
|
|
"human_size": "11 B",
|
|
"size": float64(11),
|
|
"truncated": false,
|
|
"type": "Text",
|
|
},
|
|
map[string]interface{}{
|
|
"content": "other content",
|
|
"filename": "otherfile.txt",
|
|
"human_size": "13 B",
|
|
"size": float64(13),
|
|
"truncated": false,
|
|
"type": "Text",
|
|
},
|
|
}, result["files"])
|
|
|
|
embed, ok := result["embed"].(map[string]interface{})
|
|
require.True(t, ok)
|
|
require.Contains(t, embed["js"], identifier+".js")
|
|
require.Contains(t, embed["js_dark"], identifier+".js?dark")
|
|
require.Contains(t, embed["js_light"], identifier+".js?light")
|
|
require.Contains(t, embed["js_auto"], identifier+".js?auto")
|
|
require.NotEmpty(t, embed["css"])
|
|
require.NotEmpty(t, embed["html"])
|
|
})
|
|
|
|
t.Run("Unlisted", func(t *testing.T) {
|
|
s.Logout()
|
|
_, _, username, identifier := s.CreateGist(t, "1")
|
|
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+".json", nil, 200)
|
|
})
|
|
|
|
t.Run("Private", func(t *testing.T) {
|
|
s.Logout()
|
|
_, _, username, identifier := s.CreateGist(t, "2")
|
|
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+".json", nil, 404)
|
|
})
|
|
|
|
t.Run("NonExistentGist", func(t *testing.T) {
|
|
s.Request(t, "GET", "/thomas/nonexistent.json", nil, 404)
|
|
})
|
|
}
|
|
|
|
func TestGistAccess(t *testing.T) {
|
|
s := webtest.Setup(t)
|
|
defer webtest.Teardown(t)
|
|
|
|
s.Register(t, "thomas")
|
|
s.Register(t, "alice")
|
|
|
|
_, _, user, publicId := s.CreateGist(t, "0")
|
|
_, _, _, unlistedId := s.CreateGist(t, "1")
|
|
_, _, _, privateId := s.CreateGist(t, "2")
|
|
|
|
tests := []struct {
|
|
name string
|
|
settings map[string]string
|
|
// expected codes: [owner, otherUser, anonymous] x [public, unlisted, private]
|
|
owner, otherUser, anonymous []int
|
|
}{
|
|
{
|
|
name: "Default",
|
|
owner: []int{200, 200, 200},
|
|
otherUser: []int{200, 200, 404},
|
|
anonymous: []int{200, 200, 404},
|
|
},
|
|
{
|
|
name: "RequireLogin",
|
|
settings: map[string]string{db.SettingRequireLogin: "1"},
|
|
owner: []int{200, 200, 200},
|
|
otherUser: []int{200, 200, 404},
|
|
anonymous: []int{302, 302, 302},
|
|
},
|
|
{
|
|
name: "AllowGistsWithoutLogin",
|
|
settings: map[string]string{db.SettingRequireLogin: "1", db.SettingAllowGistsWithoutLogin: "1"},
|
|
owner: []int{200, 200, 200},
|
|
otherUser: []int{200, 200, 404},
|
|
anonymous: []int{200, 200, 404},
|
|
},
|
|
}
|
|
|
|
gists := []string{publicId, unlistedId, privateId}
|
|
labels := []string{"Public", "Unlisted", "Private"}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
s.Login(t, "thomas")
|
|
for k, v := range tt.settings {
|
|
s.Request(t, "PUT", "/-/admin-panel/set-config", url.Values{"key": {k}, "value": {v}}, 200)
|
|
}
|
|
|
|
t.Run("Owner", func(t *testing.T) {
|
|
s.Login(t, "thomas")
|
|
for i, id := range gists {
|
|
s.Request(t, "GET", "/"+user+"/"+id, nil, tt.owner[i])
|
|
}
|
|
})
|
|
|
|
t.Run("OtherUser", func(t *testing.T) {
|
|
s.Login(t, "alice")
|
|
for i, id := range gists {
|
|
s.Request(t, "GET", "/"+user+"/"+id, nil, tt.otherUser[i])
|
|
}
|
|
})
|
|
|
|
t.Run("Anonymous", func(t *testing.T) {
|
|
s.Logout()
|
|
for i, id := range gists {
|
|
t.Run(labels[i], func(t *testing.T) {
|
|
s.Request(t, "GET", "/"+user+"/"+id, nil, tt.anonymous[i])
|
|
})
|
|
}
|
|
})
|
|
|
|
s.Login(t, "thomas")
|
|
for k := range tt.settings {
|
|
s.Request(t, "PUT", "/-/admin-panel/set-config", url.Values{"key": {k}, "value": {"0"}}, 200)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestGetGistCaseInsensitive(t *testing.T) {
|
|
s := webtest.Setup(t)
|
|
defer webtest.Teardown(t)
|
|
|
|
s.Register(t, "THOmas")
|
|
s.Login(t, "THOmas")
|
|
|
|
s.Request(t, "POST", "/", url.Values{
|
|
"title": {"Test"},
|
|
"name": {"file.txt"},
|
|
"content": {"hello world"},
|
|
"url": {"my-GIST"},
|
|
"private": {"0"},
|
|
}, 302)
|
|
|
|
gist, err := db.GetGistByID("1")
|
|
require.NoError(t, err)
|
|
|
|
s.Logout()
|
|
|
|
t.Run("URL", func(t *testing.T) {
|
|
s.Request(t, "GET", "/thomas/my-gist", nil, 200)
|
|
s.Request(t, "GET", "/THOMAS/MY-GIST", nil, 200)
|
|
s.Request(t, "GET", "/thomas/MY-GIST", nil, 200)
|
|
s.Request(t, "GET", "/THOMAS/my-gist", nil, 200)
|
|
})
|
|
|
|
t.Run("UUID", func(t *testing.T) {
|
|
s.Request(t, "GET", "/thomas/"+strings.ToLower(gist.Uuid), nil, 200)
|
|
s.Request(t, "GET", "/THOMAS/"+strings.ToUpper(gist.Uuid), nil, 200)
|
|
})
|
|
}
|
|
|
|
func TestGistJsSingleFile(t *testing.T) {
|
|
s := webtest.Setup(t)
|
|
defer webtest.Teardown(t)
|
|
|
|
s.Register(t, "thomas")
|
|
s.Register(t, "alice")
|
|
|
|
t.Run("RendersOnlyRequestedFile", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier+".js?file=file.txt", nil, 200)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
js := string(body)
|
|
|
|
assert.Contains(t, js, "opengist-embed")
|
|
assert.Contains(t, js, "hello world")
|
|
assert.NotContains(t, js, "other content")
|
|
})
|
|
|
|
t.Run("NonExistentFile", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+".js?file=nonexistent.txt", nil, 404)
|
|
})
|
|
|
|
t.Run("DarkTheme", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier+".js?dark", nil, 200)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(body), "dark.css")
|
|
assert.Contains(t, string(body), ", false)")
|
|
})
|
|
|
|
t.Run("LightTheme", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier+".js?light", nil, 200)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(body), "light.css")
|
|
assert.Contains(t, string(body), ", false)")
|
|
})
|
|
|
|
t.Run("AutoTheme", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
// explicit ?auto
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier+".js?auto", nil, 200)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(body), "auto.css")
|
|
assert.Contains(t, string(body), "prefers-color-scheme")
|
|
})
|
|
|
|
t.Run("DefaultThemeIsAuto", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
// no param → auto
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier+".js", nil, 200)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(body), "auto.css")
|
|
assert.Contains(t, string(body), "prefers-color-scheme")
|
|
})
|
|
|
|
t.Run("NoCacheHeader", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier+".js", nil, 200)
|
|
assert.Equal(t, "no-store", resp.Header.Get("Cache-Control"))
|
|
})
|
|
|
|
t.Run("PrivateGist", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "2")
|
|
|
|
// Anonymous — no token
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+".js?file=file.txt", nil, 404)
|
|
|
|
// Invalid token
|
|
s.RequestWithHeaders(t, "GET", "/"+username+"/"+identifier+".js?file=file.txt", nil, 404,
|
|
map[string]string{"Authorization": "Token invalidtoken"})
|
|
|
|
// Other user's valid token
|
|
s.Login(t, "alice")
|
|
aliceTok := s.CreateAccessToken(t, "alice-tok", db.ReadPermission, db.ReadPermission)
|
|
s.Logout()
|
|
s.RequestWithHeaders(t, "GET", "/"+username+"/"+identifier+".js?file=file.txt", nil, 404,
|
|
map[string]string{"Authorization": "Token " + aliceTok})
|
|
|
|
// Owner's valid token
|
|
s.Login(t, "thomas")
|
|
ownerTok := s.CreateAccessToken(t, "owner-tok", db.ReadPermission, db.ReadPermission)
|
|
s.Logout()
|
|
resp := s.RequestWithHeaders(t, "GET", "/"+username+"/"+identifier+".js?file=file.txt", nil, 200,
|
|
map[string]string{"Authorization": "Token " + ownerTok})
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
assert.Contains(t, string(body), "hello world")
|
|
})
|
|
}
|
|
|
|
func TestGistJsonSingleFile(t *testing.T) {
|
|
s := webtest.Setup(t)
|
|
defer webtest.Teardown(t)
|
|
|
|
s.Register(t, "thomas")
|
|
|
|
t.Run("RendersOnlyRequestedFile", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
|
|
resp := s.Request(t, "GET", "/"+username+"/"+identifier+".json?file=file.txt", nil, 200)
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]interface{}
|
|
require.NoError(t, json.Unmarshal(body, &result))
|
|
|
|
files, ok := result["files"].([]interface{})
|
|
require.True(t, ok)
|
|
require.Len(t, files, 1)
|
|
assert.Equal(t, "file.txt", files[0].(map[string]interface{})["filename"])
|
|
|
|
embed, ok := result["embed"].(map[string]interface{})
|
|
require.True(t, ok)
|
|
assert.Contains(t, embed["js"], identifier+".js?file=file.txt")
|
|
assert.Contains(t, embed["js_dark"], identifier+".js?file=file.txt&dark")
|
|
assert.Contains(t, embed["js_light"], identifier+".js?file=file.txt&light")
|
|
assert.Contains(t, embed["js_auto"], identifier+".js?file=file.txt&auto")
|
|
assert.NotEmpty(t, embed["html"])
|
|
})
|
|
|
|
t.Run("NonExistentFile", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "0")
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+".json?file=nonexistent.txt", nil, 404)
|
|
})
|
|
|
|
t.Run("PrivateGist", func(t *testing.T) {
|
|
_, _, username, identifier := s.CreateGist(t, "2")
|
|
|
|
// Anonymous — no token
|
|
s.Request(t, "GET", "/"+username+"/"+identifier+".json?file=file.txt", nil, 404)
|
|
|
|
// Invalid token
|
|
s.RequestWithHeaders(t, "GET", "/"+username+"/"+identifier+".json?file=file.txt", nil, 404,
|
|
map[string]string{"Authorization": "Token invalidtoken"})
|
|
|
|
// Owner's valid token
|
|
s.Login(t, "thomas")
|
|
ownerTok := s.CreateAccessToken(t, "owner-tok", db.ReadPermission, db.ReadPermission)
|
|
s.Logout()
|
|
|
|
resp := s.RequestWithHeaders(t, "GET", "/"+username+"/"+identifier+".json?file=file.txt", nil, 200,
|
|
map[string]string{"Authorization": "Token " + ownerTok})
|
|
body, err := io.ReadAll(resp.Body)
|
|
require.NoError(t, err)
|
|
|
|
var result map[string]interface{}
|
|
require.NoError(t, json.Unmarshal(body, &result))
|
|
files, ok := result["files"].([]interface{})
|
|
require.True(t, ok)
|
|
require.Len(t, files, 1)
|
|
})
|
|
}
|