package posting
import (
"html/template"
"regexp"
"testing"
"github.com/gochan-org/gochan/pkg/config"
"github.com/gochan-org/gochan/pkg/gcsql"
"github.com/gochan-org/gochan/pkg/gcutil/testutil"
"github.com/stretchr/testify/assert"
lua "github.com/yuin/gopher-lua"
)
const (
versionStr = "4.0.0"
bbcodeMsgPreRender = `[b]Bold[/b] [i]Italics[/i] [u]Underline[/u] [url=https://gochan.org]URL[/url] [?]Spoiler[/?]
[code]Code[/code]
[hide]Hidden[/hide]`
bbcodeMsgExpected = `Bold Italics Underline URL Spoiler
` +
`
Code
` +
`Hidden
`
linkTestPreRender = `gochan.org: https://gochan.org
gochan.org with path: https://gochan.org/a
gochan.org with bad link: https://gochan.org/a">:)`
linkTestExpected = `gochan.org: https://gochan.org
` +
`gochan.org with path: https://gochan.org/a
` +
`gochan.org with bad link: https://gochan.org/a">:)</a>`
doubleTagPreRender = `[url=https://gochan.org]Gochan[/url] [url]https://gochan.org[/url]`
doubleTagExpected = `Gochan https://gochan.org`
luaBBCodeTest = `local bbcode = require("bbcode")
local msg = "[lua]Lua test[/lua]"
bbcode.set_tag("lua", function(node)
return {name="span", attrs={class="lua"}}
end)`
luaBBCodeTestExpected = `Lua test`
)
var (
diceTestCases = []diceRollerTestCase{
{
desc: "[2d6]",
post: gcsql.Post{
MessageRaw: "[2d6]",
},
matcher: regexp.MustCompile(`2d6 = \d{1,2}`),
expectMin: 2,
expectMax: 12,
},
{
desc: "[2d6+1]",
post: gcsql.Post{
MessageRaw: "[2d6+1]",
},
matcher: regexp.MustCompile(`2d6\+1 = \d{1,2}`),
expectMin: 3,
expectMax: 13,
},
{
desc: "[2d6-1]",
post: gcsql.Post{
MessageRaw: "[2d6-1]",
},
matcher: regexp.MustCompile(`2d6-1 = \d{1,2}`),
expectMin: 1,
expectMax: 11,
},
{
desc: "[d8]",
post: gcsql.Post{
MessageRaw: "[d8]",
},
matcher: regexp.MustCompile(`1d8 = \d`),
expectMin: 1,
expectMax: 8,
},
{
desc: "before[1d6]after, no space",
post: gcsql.Post{
MessageRaw: "before[1d6]after",
},
matcher: regexp.MustCompile(`before1d6 = \dafter`),
expectMin: 1,
expectMax: 6,
},
{
desc: "before [1d6] after, no space (test for injection)",
post: gcsql.Post{
MessageRaw: `[1d6]`,
},
matcher: regexp.MustCompile(`<script>alert\("lol"\)</script>1d6 = \d<script>alert\("lmao"\)</script>`),
expectMin: 1,
expectMax: 6,
},
{
desc: "two dice rolls, no space",
post: gcsql.Post{
MessageRaw: "[d6][2d6]",
},
matcher: regexp.MustCompile(`1d6 = \d2d6 = \d{1,2}`),
expectMin: 0,
expectMax: 7,
},
{
desc: "multiple dice rolls, no space",
post: gcsql.Post{
MessageRaw: "[d6][2d20-2][3d8+1]",
},
matcher: regexp.MustCompile(`1d6 = \d2d20-2 = \d{1,2}3d8\+1 = \d{1,2}`),
expectMin: 0,
expectMax: 38,
},
{
desc: "invalid number of dice",
post: gcsql.Post{
MessageRaw: "[0d6]",
},
expectError: true,
},
{
desc: "invalid die size",
post: gcsql.Post{
MessageRaw: "[1d0]",
},
expectError: true,
},
{
desc: "invalid modifier",
post: gcsql.Post{
MessageRaw: "[1d6+]",
},
matcher: regexp.MustCompile(`\[1d6\+\]`),
},
}
)
type diceRollerTestCase struct {
desc string
post gcsql.Post
expectError bool
matcher *regexp.Regexp
expectMin int
expectMax int
}
func TestBBCode(t *testing.T) {
config.SetVersion(versionStr)
var testFmtr MessageFormatter
testFmtr.Init()
rendered := testFmtr.Compile(bbcodeMsgPreRender, "")
assert.Equal(t, bbcodeMsgExpected, rendered, "Testing BBcode rendering")
}
func TestLinks(t *testing.T) {
config.SetVersion(versionStr)
var testFmtr MessageFormatter
testFmtr.Init()
rendered := urlRE.ReplaceAllStringFunc(linkTestPreRender, wrapLinksInURL)
rendered = testFmtr.Compile(rendered, "")
assert.Equal(t, linkTestExpected, rendered)
}
func TestNoDoubleTags(t *testing.T) {
config.SetVersion(versionStr)
msgfmtr.Init()
_, warnEv, errEv := testutil.GetTestLogs(t)
rendered, err := FormatMessage(doubleTagPreRender, "", warnEv, errEv)
assert.NoError(t, err)
assert.EqualValues(t, doubleTagExpected, rendered)
}
func TestLuaBBCode(t *testing.T) {
config.SetVersion(versionStr)
msgfmtr.Init()
l := lua.NewState()
defer l.Close()
l.PreloadModule("bbcode", PreloadBBCodeModule)
assert.NoError(t, l.DoString(luaBBCodeTest))
compiled := msgfmtr.bbCompiler.Compile("[lua]Lua test[/lua]")
assert.Equal(t, luaBBCodeTestExpected, compiled)
assert.NoError(t, l.DoString(`require("bbcode").set_tag("b", nil)`))
assert.Equal(t, "[b]Lua test[/b]", msgfmtr.bbCompiler.Compile("[b]Lua test[/b]"))
assert.Error(t, l.DoString(`bbcode.set_tag("lua", 1)`))
}
func diceRollRunner(t *testing.T, tC *diceRollerTestCase) {
var err error
_, warnEv, errEv := testutil.GetTestLogs(t)
tC.post.Message, err = FormatMessage(tC.post.MessageRaw, "", warnEv, errEv)
assert.NoError(t, err)
err = ApplyDiceRoll(&tC.post)
if tC.expectError {
assert.Error(t, err)
} else {
assert.NoError(t, err)
assert.Regexp(t, tC.matcher, tC.post.Message)
}
if t.Failed() {
t.FailNow()
}
}
func TestDiceRoll(t *testing.T) {
config.SetVersion(versionStr)
msgfmtr.Init()
for _, tC := range diceTestCases {
t.Run(tC.desc, func(t *testing.T) {
for range 100 {
// Run the test case multiple times to account for randomness
diceRollRunner(t, &tC)
}
})
}
}
func TestHashTags(t *testing.T) {
config.SetVersion(versionStr)
msgfmtr.Init()
_, warnEv, errEv := testutil.GetTestLogs(t)
msg := `[#tag]
[#t a g]
[ #tag]
[#tag ]
[# tag]
>greentext [#tag]
[#line
test]
[#single] [#line] [#tags]
[#jsinjection]`
msgHTML, err := FormatMessage(msg, "test", warnEv, errEv)
if !assert.NoError(t, err) {
t.FailNow()
}
assert.Equal(t, template.HTML(
`#tag
`+
`#t a g
`+
`[ #tag]
`+
`#tag
`+
`# tag
`+
`>greentext #tag
`+
`[#line
`+
`test]
`+
`#single #line #tags
`+
`#js<script>alert("lol")</script>injection`,
), msgHTML)
}
func TestWorksafe(t *testing.T) {
config.SetVersion(versionStr)
msgfmtr.Init()
_, warnEv, errEv := testutil.GetTestLogs(t)
boardConfig := config.GetBoardConfig("test")
boardConfig.Worksafe = true
config.SetBoardConfig("test", boardConfig)
_, err := FormatMessage("[#nsfw] [#tag2]", "test", warnEv, errEv)
if !assert.ErrorIs(t, err, ErrWorksafeBoard) {
t.FailNow()
}
boardConfig.Worksafe = false
config.SetBoardConfig("test", boardConfig)
msgHTML, err := FormatMessage("[#nsfw]", "test", warnEv, errEv)
assert.NoError(t, err)
assert.Equal(t, template.HTML(`#nsfw`), msgHTML)
}