增加密码解锁与游客签名 cookie;发帖页对齐 1440 并默认双栏预览;修复 locked 解析、按钮对比度与回复聚焦。 Co-authored-by: Cursor <cursoragent@cursor.com>
173 lines
4.4 KiB
Go
173 lines
4.4 KiB
Go
package markdown
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestParseIgnoreCodeFence(t *testing.T) {
|
|
src := "公开\n\n```\n:::hide login\n假的\n:::\n```\n\n结尾\n"
|
|
blocks, err := ParseHideBlocks(src)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(blocks) != 0 {
|
|
t.Fatalf("expected 0 blocks, got %d", len(blocks))
|
|
}
|
|
}
|
|
|
|
func TestParseMixedAndDerive(t *testing.T) {
|
|
src := "公开段\n\n:::hide login\n登录内容\n:::\n\n:::hide points 10\n积分A\n:::\n\n:::hide points 5\n积分B\n:::\n"
|
|
blocks, err := ParseHideBlocks(src)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(blocks) != 3 {
|
|
t.Fatalf("expected 3 blocks, got %d", len(blocks))
|
|
}
|
|
d := DeriveAccess(blocks)
|
|
if d.Access != "mixed" {
|
|
t.Fatalf("access=%s", d.Access)
|
|
}
|
|
if d.Points != 15 {
|
|
t.Fatalf("points=%d", d.Points)
|
|
}
|
|
}
|
|
|
|
func TestParseNestedRejected(t *testing.T) {
|
|
src := ":::hide login\n外\n:::hide reply\n内\n:::\n:::\n"
|
|
_, err := ParseHideBlocks(src)
|
|
if err != ErrHideNested {
|
|
t.Fatalf("want ErrHideNested, got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestSanitizeLocksBody(t *testing.T) {
|
|
src := "公开\n\n:::hide login\n秘密内容\n:::\n\n尾\n"
|
|
out, fully := SanitizeForViewer(src, ViewerCaps{})
|
|
if fully {
|
|
t.Fatal("should not be fully locked")
|
|
}
|
|
if strings.Contains(out, "秘密内容") {
|
|
t.Fatalf("leaked: %q", out)
|
|
}
|
|
if !strings.Contains(out, ":::hide login locked") {
|
|
t.Fatalf("missing locked marker: %q", out)
|
|
}
|
|
if !strings.Contains(out, "公开") || !strings.Contains(out, "尾") {
|
|
t.Fatalf("public parts lost: %q", out)
|
|
}
|
|
}
|
|
|
|
func TestSanitizeUnlockLogin(t *testing.T) {
|
|
src := ":::hide login\n秘密\n:::\n"
|
|
out, fully := SanitizeForViewer(src, ViewerCaps{LoggedIn: true})
|
|
if fully {
|
|
t.Fatal("should see content")
|
|
}
|
|
if !strings.Contains(out, "秘密") {
|
|
t.Fatalf("missing body: %q", out)
|
|
}
|
|
if strings.Contains(out, "locked") {
|
|
t.Fatalf("should not lock: %q", out)
|
|
}
|
|
}
|
|
|
|
func TestSanitizeBypass(t *testing.T) {
|
|
src := ":::hide points 9\n付费\n:::\n"
|
|
out, _ := SanitizeForViewer(src, ViewerCaps{Bypass: true})
|
|
if !strings.Contains(out, "付费") {
|
|
t.Fatalf("bypass failed: %q", out)
|
|
}
|
|
}
|
|
|
|
func TestSanitizePointsPaid(t *testing.T) {
|
|
src := ":::hide points 3\n付费文\n:::\n"
|
|
out, _ := SanitizeForViewer(src, ViewerCaps{LoggedIn: true, PointsPaid: true})
|
|
if !strings.Contains(out, "付费文") {
|
|
t.Fatalf("paid unlock failed: %q", out)
|
|
}
|
|
}
|
|
|
|
func TestFullyLocked(t *testing.T) {
|
|
src := ":::hide reply\n仅回复\n:::\n"
|
|
_, fully := SanitizeForViewer(src, ViewerCaps{LoggedIn: true})
|
|
if !fully {
|
|
t.Fatal("expected fully locked")
|
|
}
|
|
}
|
|
|
|
func TestCodeInsideHide(t *testing.T) {
|
|
src := ":::hide login\n```\n:::hide reply\n伪\n:::\n```\n真\n:::\n"
|
|
blocks, err := ParseHideBlocks(src)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(blocks) != 1 {
|
|
t.Fatalf("got %d", len(blocks))
|
|
}
|
|
if !strings.Contains(blocks[0].Body, "真") {
|
|
t.Fatalf("body=%q", blocks[0].Body)
|
|
}
|
|
}
|
|
|
|
func TestParsePasswordAndSanitize(t *testing.T) {
|
|
src := "公开\n\n:::hide password secret1\n密码内容\n:::\n"
|
|
blocks, err := ParseHideBlocks(src)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(blocks) != 1 || blocks[0].Kind != HideKindPassword || blocks[0].Password != "secret1" {
|
|
t.Fatalf("%+v", blocks)
|
|
}
|
|
d := DeriveAccess(blocks)
|
|
if d.Access != "password" {
|
|
t.Fatalf("access=%s", d.Access)
|
|
}
|
|
out, fully := SanitizeForViewer(src, ViewerCaps{})
|
|
if fully {
|
|
t.Fatal("public visible")
|
|
}
|
|
if strings.Contains(out, "secret1") || strings.Contains(out, "密码内容") {
|
|
t.Fatalf("leaked: %q", out)
|
|
}
|
|
if !strings.Contains(out, ":::hide password locked") {
|
|
t.Fatalf("%q", out)
|
|
}
|
|
out2, _ := SanitizeForViewer(src, ViewerCaps{PasswordUnlocked: map[int]bool{0: true}})
|
|
if !strings.Contains(out2, "密码内容") {
|
|
t.Fatalf("unlock failed: %q", out2)
|
|
}
|
|
if strings.Contains(out2, "secret1") {
|
|
t.Fatalf("password leaked after unlock: %q", out2)
|
|
}
|
|
}
|
|
|
|
func TestMatchPasswordBlocks(t *testing.T) {
|
|
src := ":::hide password aaa\nA\n:::\n\n:::hide password bbb\nB\n:::\n"
|
|
hit, err := MatchPasswordBlocks(src, "bbb")
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(hit) != 1 || hit[0] != 1 {
|
|
t.Fatalf("%v", hit)
|
|
}
|
|
}
|
|
|
|
func TestPasswordNeed(t *testing.T) {
|
|
_, err := ParseHideBlocks(":::hide password\n无密码\n:::\n")
|
|
if err != ErrHidePasswordNeed {
|
|
t.Fatalf("got %v", err)
|
|
}
|
|
}
|
|
|
|
func TestWrapContentAsHide(t *testing.T) {
|
|
got := WrapContentAsHide("points", 20, "旧正文")
|
|
if !strings.HasPrefix(got, ":::hide points 20\n") {
|
|
t.Fatalf("%q", got)
|
|
}
|
|
if !strings.Contains(got, "旧正文") {
|
|
t.Fatal(got)
|
|
}
|
|
}
|