Files
upstream-mirror 240db4e2a7
Some checks failed
Server CI / Postgres (shard 0) (push) Has been skipped
Server CI / Postgres (shard 3) (push) Has been skipped
BuildEnv Docker Image / build-image (push) Failing after 4s
BuildEnv Docker Image / build-image-fips (push) Failing after 4s
Migration Review & Release Notes / Generate migration review & release notes (push) Failing after 3s
Server CI / Build Environment (push) Failing after 3s
Server CI / Postgres (shard 1) (push) Has been skipped
Tools CI / check-style (mattermost-govet) (push) Failing after 4s
Server CI / Postgres (shard 2) (push) Has been skipped
Tools CI / check-style (mmgotool) (push) Failing after 3s
Server CI / Elasticsearch v8 Compatibility (push) Has been skipped
Server CI / OpenSearch v2 Compatibility (push) Has been skipped
Server CI / Postgres FIPS (shard 0) (push) Has been skipped
Tools CI / Test (mattermost-govet) (push) Failing after 3s
Server CI / Postgres FIPS (shard 1) (push) Has been skipped
Server CI / Postgres FIPS (shard 2) (push) Has been skipped
Tools CI / Test (mmgotool) (push) Failing after 3s
Server CI / Postgres FIPS (shard 3) (push) Has been skipped
Server CI / Run mmctl tests (push) Has been skipped
Server CI / Run mmctl tests (FIPS) (push) Has been skipped
Server CI / Merge Postgres Test Results (push) Has been skipped
Server CI / Merge Postgres FIPS Test Results (push) Has been skipped
Server CI / Check backport migrations (push) Has been skipped
Server CI / Check generated files (push) Has been skipped
Server CI / check-style (push) Has been skipped
Server CI / Vet API (push) Has been skipped
Server CI / Build mattermost server app (push) Has been skipped
Server CI / Server CI Complete (push) Failing after 2s
Server CI / Server CI Artifacts (push) Has been skipped
Server CI / Sentry Upload (push) Has been skipped
Server CI / Server CI Report (push) Failing after 5m1s
Server CI Nightly Race / Race Detector (push) Has been skipped
CodeQL / Analyze (go) (push) Has been cancelled
CodeQL / Analyze (javascript) (push) Has been cancelled
Webapp CI Cache Warm / Warm Webapp node_modules Cache (push) Has been cancelled
E2E CI Cache Warm / Warm E2E npm Registry Cache (push) Has been cancelled
API / build (push) Has been cancelled
Web App CI / check-types (push) Has been cancelled
Web App CI / check-lint (push) Has been cancelled
Web App CI / check-i18n (push) Has been cancelled
Web App CI / check-external-links (push) Has been cancelled
Web App CI / test (platform) (push) Has been cancelled
Web App CI / test (mattermost-redux) (push) Has been cancelled
Web App CI / test (channels shard 1/4) (push) Has been cancelled
Web App CI / test (channels shard 2/4) (push) Has been cancelled
Web App CI / test (channels shard 3/4) (push) Has been cancelled
Web App CI / test (channels shard 4/4) (push) Has been cancelled
Web App CI / upload-coverage (push) Has been cancelled
Web App CI / build (push) Has been cancelled
YAML Lint / yamllint (push) Has been cancelled
Push mirrored docker images / cd/Push mirrored docker images (push) Has been cancelled
docs-cd / Build and deploy docs (push) Has been cancelled
Docs CI / build (push) Has been cancelled
E2E Cache Warm: Cypress deps / Warm Cypress deps cache (push) Has been cancelled
Scorecards supply-chain security / Scorecard analysis (push) Has been cancelled
Server CI Weekly / Postgres FIPS (push) Has been skipped
Server CI Weekly / Run mmctl tests (FIPS) (push) Has been skipped
Server CI Weekly / Postgres with binary parameters (push) Has been skipped
Server CI Weekly / Build Environment (push) Failing after 1s
Server CI Cache Warm / Warm Go Module and Build Cache (push) Failing after 2s
Server CI Nightly Race / Build Environment (push) Failing after 1s
snapshot of https://github.com/mattermost/mattermost.git (master, 2026-09-16; blobs > 131072B omitted)
2026-09-16 00:29:55 +05:00

1183 lines
37 KiB
Go
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package markdown
import (
"fmt"
"strings"
"testing"
"github.com/stretchr/testify/assert"
)
func TestParseURLAutolink(t *testing.T) {
testCases := []struct {
Description string
Input string
Position int
Expected string
}{
{
Description: "no link",
Input: "This is an :emoji:",
Position: 11,
Expected: "",
},
{
Description: "no link 2",
Input: "These are two things: apple and orange",
Position: 20,
Expected: "",
},
{
Description: "link with http",
Input: "http://example.com and some text",
Position: 4,
Expected: "http://example.com",
},
{
Description: "link with https",
Input: "https://example.com and some text",
Position: 5,
Expected: "https://example.com",
},
{
Description: "link with ftp",
Input: "ftp://example.com and some text",
Position: 3,
Expected: "ftp://example.com",
},
{
Description: "link with a path",
Input: "https://example.com/abcd and some text",
Position: 5,
Expected: "https://example.com/abcd",
},
{
Description: "link with parameters",
Input: "ftp://example.com/abcd?foo=bar and some text",
Position: 3,
Expected: "ftp://example.com/abcd?foo=bar",
},
{
Description: "link, not at start",
Input: "This is https://example.com and some text",
Position: 13,
Expected: "https://example.com",
},
{
Description: "link with a path, not at start",
Input: "This is also http://www.example.com/abcd and some text",
Position: 17,
Expected: "http://www.example.com/abcd",
},
{
Description: "link with parameters, not at start",
Input: "These are https://www.example.com/abcd?foo=bar and some text",
Position: 15,
Expected: "https://www.example.com/abcd?foo=bar",
},
{
Description: "link with trailing characters",
Input: "This is ftp://www.example.com??",
Position: 11,
Expected: "ftp://www.example.com",
},
{
Description: "multiple links",
Input: "This is https://example.com/abcd and ftp://www.example.com/1234",
Position: 13,
Expected: "https://example.com/abcd",
},
{
Description: "second of multiple links",
Input: "This is https://example.com/abcd and ftp://www.example.com/1234",
Position: 40,
Expected: "ftp://www.example.com/1234",
},
{
Description: "link with brackets",
Input: "Go to ftp://www.example.com/my/page_(disambiguation) and some text",
Position: 9,
Expected: "ftp://www.example.com/my/page_(disambiguation)",
},
{
Description: "link in brackets",
Input: "(https://www.example.com/foo/bar)",
Position: 6,
Expected: "https://www.example.com/foo/bar",
},
{
Description: "link in underscores",
Input: "_http://www.example.com_",
Position: 5,
Expected: "http://www.example.com",
},
{
Description: "link in asterisks",
Input: "This is **ftp://example.com**",
Position: 13,
Expected: "ftp://example.com",
},
{
Description: "link in strikethrough",
Input: "Those were ~~https://example.com~~",
Position: 18,
Expected: "https://example.com",
},
{
Description: "link in html tags",
Input: "<b>We use http://example.com</b>",
Position: 14,
Expected: "http://example.com",
},
{
Description: "bad link protocol",
Input: "://///",
Position: 0,
Expected: "",
},
{
Description: "position greater than input length",
Input: "there is no colon",
Position: 1000,
Expected: "",
},
{
Description: "no link with angle brackets",
Input: "This is an <:emoji:>",
Position: 15,
Expected: "",
},
{
Description: "link with http in angle brackets",
Input: "<http://example.com> and some text",
Position: 5,
Expected: "http://example.com",
},
{
Description: "link with https in angle brackets",
Input: "<https://example.com> and some text",
Position: 6,
Expected: "https://example.com",
},
{
Description: "link with ftp in angle brackets",
Input: "<ftp://example.com> and some text",
Position: 4,
Expected: "ftp://example.com",
},
{
Description: "link with a path in angle brackets",
Input: "<https://example.com/abcd> and some text",
Position: 6,
Expected: "https://example.com/abcd",
},
{
Description: "link with parameters in angle brackets",
Input: "<ftp://example.com/abcd?foo=bar> and some text",
Position: 4,
Expected: "ftp://example.com/abcd?foo=bar",
},
{
Description: "link, not at start in angle brackets",
Input: "This is <https://example.com> and some text",
Position: 14,
Expected: "https://example.com",
},
{
Description: "link with a path, not at start in angle brackets",
Input: "This is also <http://www.example.com/abcd> and some text",
Position: 18,
Expected: "http://www.example.com/abcd",
},
{
Description: "link with parameters, not at start in angle brackets",
Input: "These are <https://www.example.com/abcd?foo=bar> and some text",
Position: 16,
Expected: "https://www.example.com/abcd?foo=bar",
},
{
Description: "link with trailing characters in angle brackets",
Input: "This is <ftp://www.example.com??>",
Position: 12,
Expected: "ftp://www.example.com",
},
{
Description: "multiple links in angle brackets",
Input: "This is <https://example.com/abcd> and <ftp://www.example.com/1234>",
Position: 14,
Expected: "https://example.com/abcd",
},
{
Description: "second of multiple links in angle brackets",
Input: "This is <https://example.com/abcd> and <ftp://www.example.com/1234>",
Position: 43,
Expected: "ftp://www.example.com/1234",
},
{
Description: "multiple links with first one in angle brackets",
Input: "This is <https://example.com/abcd> and ftp://www.example.com/1234",
Position: 14,
Expected: "https://example.com/abcd",
},
{
Description: "second of multiple links with first one in angle brackets",
Input: "This is <https://example.com/abcd> and ftp://www.example.com/1234",
Position: 42,
Expected: "ftp://www.example.com/1234",
},
{
Description: "multiple links with second one in angle brackets",
Input: "This is https://example.com/abcd and <ftp://www.example.com/1234>",
Position: 13,
Expected: "https://example.com/abcd",
},
{
Description: "second of multiple links with second one in angle brackets",
Input: "This is https://example.com/abcd and <ftp://www.example.com/1234>",
Position: 41,
Expected: "ftp://www.example.com/1234",
},
{
Description: "link with brackets wrapped in angle brackets",
Input: "Go to <ftp://www.example.com/my/page_(disambiguation)> and some text",
Position: 10,
Expected: "ftp://www.example.com/my/page_(disambiguation)",
},
{
Description: "link with angle brackets in parenthesis",
Input: "(<https://www.example.com/foo/bar>)",
Position: 7,
Expected: "https://www.example.com/foo/bar",
},
{
Description: "link with angle brackets in underscores",
Input: "_<http://www.example.com>_",
Position: 6,
Expected: "http://www.example.com",
},
{
Description: "link with angle brackets in asterisks",
Input: "This is **<ftp://example.com>**",
Position: 14,
Expected: "ftp://example.com",
},
{
Description: "link with angle brackets in strikethrough",
Input: "Those were ~~<https://example.com>~~",
Position: 19,
Expected: "https://example.com",
},
{
Description: "link with angle brackets in html tags",
Input: "<b>We use <http://example.com></b>",
Position: 15,
Expected: "http://example.com",
},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
rawRange, ok := parseURLAutolink(testCase.Input, testCase.Position)
if testCase.Expected == "" {
assert.False(t, ok)
assert.Equal(t, Range{0, 0}, rawRange)
} else {
assert.True(t, ok)
assert.Equal(t, testCase.Expected, testCase.Input[rawRange.Position:rawRange.End])
}
})
}
}
func TestParseWWWAutolink(t *testing.T) {
testCases := []struct {
Description string
Input string
Position int
Expected string
}{
{
Description: "no link",
Input: "This is some text",
Position: 0,
Expected: "",
},
{
Description: "link",
Input: "www.example.com and some text",
Position: 0,
Expected: "www.example.com",
},
{
Description: "link with a path",
Input: "www.example.com/abcd and some text",
Position: 0,
Expected: "www.example.com/abcd",
},
{
Description: "link with parameters",
Input: "www.example.com/abcd?foo=bar and some text",
Position: 0,
Expected: "www.example.com/abcd?foo=bar",
},
{
Description: "link, not at start",
Input: "This is www.example.com and some text",
Position: 8,
Expected: "www.example.com",
},
{
Description: "link with a path, not at start",
Input: "This is also www.example.com/abcd and some text",
Position: 13,
Expected: "www.example.com/abcd",
},
{
Description: "link with parameters, not at start",
Input: "These are www.example.com/abcd?foo=bar and some text",
Position: 10,
Expected: "www.example.com/abcd?foo=bar",
},
{
Description: "link with trailing characters",
Input: "This is www.example.com??",
Position: 8,
Expected: "www.example.com",
},
{
Description: "link after current position",
Input: "This is some text and www.example.com",
Position: 0,
Expected: "",
},
{
Description: "multiple links",
Input: "This is www.example.com/abcd and www.example.com/1234",
Position: 8,
Expected: "www.example.com/abcd",
},
{
Description: "multiple links 2",
Input: "This is www.example.com/abcd and www.example.com/1234",
Position: 33,
Expected: "www.example.com/1234",
},
{
Description: "link with brackets",
Input: "Go to www.example.com/my/page_(disambiguation) and some text",
Position: 6,
Expected: "www.example.com/my/page_(disambiguation)",
},
{
Description: "link following other letters",
Input: "aaawww.example.com and some text",
Position: 3,
Expected: "",
},
{
Description: "link in brackets",
Input: "(www.example.com)",
Position: 1,
Expected: "www.example.com",
},
{
Description: "link in underscores",
Input: "_www.example.com_",
Position: 1,
Expected: "www.example.com",
},
{
Description: "link in asterisks",
Input: "This is **www.example.com**",
Position: 10,
Expected: "www.example.com",
},
{
Description: "link in strikethrough",
Input: "Those were ~~www.example.com~~",
Position: 13,
Expected: "www.example.com",
},
{
Description: "using www1",
Input: "Our backup site is at www1.example.com/foo",
Position: 22,
Expected: "www1.example.com/foo",
},
{
Description: "link in html tags",
Input: "<b>We use www2.example.com</b>",
Position: 10,
Expected: "www2.example.com",
},
{
Description: "no link, text with angle brackets",
Input: "This is some <> text",
Position: 0,
Expected: "",
},
{
Description: "link with angle brackets",
Input: "<www.example.com> and some text",
Position: 1,
Expected: "www.example.com",
},
{
Description: "link with a path in angle brackets",
Input: "<www.example.com/abcd> and some text",
Position: 1,
Expected: "www.example.com/abcd",
},
{
Description: "link with parameters in angle brackets",
Input: "<www.example.com/abcd?foo=bar> and some text",
Position: 1,
Expected: "www.example.com/abcd?foo=bar",
},
{
Description: "link in angle brackets, not at start",
Input: "This is <www.example.com> and some text",
Position: len("This is <"),
Expected: "www.example.com",
},
{
Description: "link with a path in angle brackets, not at start",
Input: "This is also <www.example.com/abcd> and some text",
Position: len("This is also <"),
Expected: "www.example.com/abcd",
},
{
Description: "link with parameters in angle brackets, not at start",
Input: "These are <www.example.com/abcd?foo=bar> and some text",
Position: len("These are <"),
Expected: "www.example.com/abcd?foo=bar",
},
{
Description: "link with trailing characters in angle brackets",
Input: "This is <www.example.com??>",
Position: len("This is <"),
Expected: "www.example.com",
},
{
Description: "link in angle brackets after current position",
Input: "This is some text and <www.example.com>",
Position: 0,
Expected: "",
},
{
Description: "multiple links with angle brackets",
Input: "This is <www.example.com/abcd> and <www.example.com/1234>",
Position: len("This is <"),
Expected: "www.example.com/abcd",
},
{
Description: "multiple links 2 with angle brackets",
Input: "This is <www.example.com/abcd> and <www.example.com/1234>",
Position: len("This is <www.example.com/abcd> and <"),
Expected: "www.example.com/1234",
},
{
Description: "multiple links, last one with angle brackets",
Input: "This is www.example.com/abcd and <www.example.com/1234>",
Position: len("This is "),
Expected: "www.example.com/abcd",
},
{
Description: "multiple links, last one with angle brackets 2",
Input: "This is www.example.com/abcd and <www.example.com/1234>",
Position: len("This is www.example.com/abcd and <"),
Expected: "www.example.com/1234",
},
{
Description: "multiple links, first one with angle brackets",
Input: "This is <www.example.com/abcd> and www.example.com/1234",
Position: len("This is <"),
Expected: "www.example.com/abcd",
},
{
Description: "multiple links, first one with angle brackets 2",
Input: "This is <www.example.com/abcd> and www.example.com/1234",
Position: len("This is <www.example.com/abcd> and "),
Expected: "www.example.com/1234",
},
{
Description: "link with brackets in angle brackets",
Input: "Go to <www.example.com/my/page_(disambiguation)> and some text",
Position: 7,
Expected: "www.example.com/my/page_(disambiguation)",
},
{
Description: "link in angle brackets following other letters",
Input: "aaa<www.example.com> and some text",
Position: 4,
Expected: "www.example.com",
},
{
Description: "link with angle brackets in parentheses",
Input: "(<www.example.com>)",
Position: 2,
Expected: "www.example.com",
},
{
Description: "link with angle brackets in underscores",
Input: "_<www.example.com>_",
Position: 2,
Expected: "www.example.com",
},
{
Description: "link with angle brackets in asterisks",
Input: "This is **<www.example.com>**",
Position: len("This is **<"),
Expected: "www.example.com",
},
{
Description: "link with angle brackets in strikethrough",
Input: "Those were ~~<www.example.com>~~",
Position: len("Those were ~~<"),
Expected: "www.example.com",
},
{
Description: "link using www1 in angle brackets",
Input: "Our backup site is at <www1.example.com/foo>",
Position: len("Our backup site is at <"),
Expected: "www1.example.com/foo",
},
{
Description: "link with angle brackets in html tags",
Input: "<b>We use <www2.example.com></b>",
Position: len("<b>We use <"),
Expected: "www2.example.com",
},
{
Description: "link with only opening angle bracket",
Input: "<www.example.com and some text",
Position: 1,
Expected: "www.example.com",
},
{
Description: "link with only closing angle bracket",
Input: "www.example.com> and some text",
Position: 0,
Expected: "www.example.com",
},
{
Description: "link with only opening bracket",
Input: "(www.example.com",
Position: 1,
Expected: "www.example.com",
},
{
Description: "link with only closing bracket",
Input: "www.example.com)",
Position: 0,
Expected: "www.example.com",
},
{
Description: "link with underscore at only start",
Input: "_www.example.com",
Position: 1,
Expected: "www.example.com",
},
{
Description: "link with underscore at only end",
Input: "www.example.com_",
Position: 0,
Expected: "www.example.com",
},
{
Description: "link with asterisks at only start",
Input: "This is **www.example.com",
Position: 10,
Expected: "www.example.com",
},
{
Description: "link with asterisks at only end",
Input: "This is www.example.com**",
Position: 8,
Expected: "www.example.com",
},
{
Description: "link with tildes at only start",
Input: "Those were ~~www.example.com",
Position: 13,
Expected: "www.example.com",
},
{
Description: "link with tildes at only end",
Input: "Those were www.example.com~~",
Position: 11,
Expected: "www.example.com",
},
{
Description: "link with only opening html tag",
Input: "<b>We use www2.example.com",
Position: 10,
Expected: "www2.example.com",
},
{
Description: "link with only closing html tag",
Input: "We use www2.example.com</b>",
Position: 7,
Expected: "www2.example.com",
},
}
for _, testCase := range testCases {
t.Run(testCase.Description, func(t *testing.T) {
rawRange, ok := parseWWWAutolink(testCase.Input, testCase.Position)
if testCase.Expected == "" {
assert.False(t, ok)
assert.Equal(t, Range{0, 0}, rawRange)
} else {
assert.True(t, ok)
assert.Equal(t, testCase.Expected, testCase.Input[rawRange.Position:rawRange.End])
}
})
}
}
func TestTrimTrailingCharactersFromLink(t *testing.T) {
testCases := []struct {
Input string
Start int
End int
ExpectedEnd int
}{
{
Input: "http://www.example.com",
ExpectedEnd: 22,
},
{
Input: "http://www.example.com/abcd",
ExpectedEnd: 27,
},
{
Input: "http://www.example.com/abcd/",
ExpectedEnd: 28,
},
{
Input: "http://www.example.com/1234",
ExpectedEnd: 27,
},
{
Input: "http://www.example.com/abcd?foo=bar",
ExpectedEnd: 35,
},
{
Input: "http://www.example.com/abcd#heading",
ExpectedEnd: 35,
},
{
Input: "http://www.example.com.",
ExpectedEnd: 22,
},
{
Input: "http://www.example.com,",
ExpectedEnd: 22,
},
{
Input: "http://www.example.com?",
ExpectedEnd: 22,
},
{
Input: "http://www.example.com)",
ExpectedEnd: 22,
},
{
Input: "http://www.example.com",
ExpectedEnd: 22,
},
{
Input: "https://en.wikipedia.org/wiki/Dolphin_(disambiguation)",
ExpectedEnd: 54,
},
{
Input: "https://en.wikipedia.org/wiki/Dolphin_(disambiguation",
ExpectedEnd: 53,
},
{
Input: "https://en.wikipedia.org/wiki/Dolphin_(disambiguation))",
ExpectedEnd: 54,
},
{
Input: "https://example.com/" + strings.Repeat(")", 4096),
ExpectedEnd: len("https://example.com/"),
},
{
Input: "https://example.com/path_(with_" + strings.Repeat("nested_", 10) + "parens)",
ExpectedEnd: len("https://example.com/path_(with_" + strings.Repeat("nested_", 10) + "parens)"),
},
{
Input: "https://example.com/path_((opening-heavy)",
ExpectedEnd: len("https://example.com/path_((opening-heavy)"),
},
{
Input: "https://en.wikipedia.org/wiki/Dolphin_(disambiguation)_(disambiguation)",
ExpectedEnd: 71,
},
{
Input: "https://en.wikipedia.org/wiki/Dolphin_(disambiguation_(disambiguation))",
ExpectedEnd: 71,
},
{
Input: "http://www.example.com&quot;",
ExpectedEnd: 22,
},
{
Input: "this is a sentence containing http://www.example.com in it",
Start: 30,
End: 52,
ExpectedEnd: 52,
},
{
Input: "this is a sentence containing http://www.example.com???",
Start: 30,
End: 55,
ExpectedEnd: 52,
},
{
Input: "http://google.com/å",
ExpectedEnd: len("http://google.com/å"),
},
{
Input: "http://google.com/å...",
ExpectedEnd: len("http://google.com/å"),
},
{
Input: "This is http://google.com/å, a link, and http://google.com/å",
Start: 8,
End: len("This is http://google.com/å,"),
ExpectedEnd: len("This is http://google.com/å"),
},
{
Input: "This is http://google.com/å, a link, and http://google.com/å",
Start: 41,
End: len("This is http://google.com/å, a link, and http://google.com/å"),
ExpectedEnd: len("This is http://google.com/å, a link, and http://google.com/å"),
},
{
Input: "This is http://google.com/å, a link, and http://google.com/å.",
Start: 41,
End: len("This is http://google.com/å, a link, and http://google.com/å."),
ExpectedEnd: len("This is http://google.com/å, a link, and http://google.com/å"),
},
{
Input: "http://🍄.ga/ http://x🍄.ga/",
Start: 0,
End: len("http://🍄.ga/"),
ExpectedEnd: len("http://🍄.ga/"),
},
{
Input: "http://🍄.ga/ http://x🍄.ga/",
Start: len("http://🍄.ga/ "),
End: len("http://🍄.ga/ http://x🍄.ga/"),
ExpectedEnd: len("http://🍄.ga/ http://x🍄.ga/"),
},
{
Input: "<http://www.example.com>",
Start: 1,
ExpectedEnd: 23,
},
{
Input: "<http://www.example.com&quot;>",
Start: 1,
ExpectedEnd: 23,
},
{
Input: "this is a sentence containing <http://www.example.com> in it",
Start: len("this is a sentence containing <"),
End: len("this is a sentence containing <http://www.example.com>"),
ExpectedEnd: 53,
},
{
Input: "this is a sentence containing <http://www.example.com???>",
Start: len("this is a sentence containing <"),
End: len("this is a sentence containing <http://www.example.com???>"),
ExpectedEnd: 53,
},
}
for _, testCase := range testCases {
t.Run(testCase.Input, func(t *testing.T) {
if testCase.End == 0 {
testCase.End = len(testCase.Input) - testCase.Start
}
assert.Equal(t, testCase.ExpectedEnd, trimTrailingCharactersFromLink(testCase.Input, testCase.Start, testCase.End))
})
}
}
func BenchmarkTrimTrailingCharactersFromLink_UnmatchedClosingParens(b *testing.B) {
for _, size := range []int{128, 1024, 8192, 65536} {
b.Run(fmt.Sprintf("closing-%d", size), func(b *testing.B) {
input := "https://example.com/" + strings.Repeat(")", size)
expectedEnd := len("https://example.com/")
b.ReportAllocs()
for range b.N {
if end := trimTrailingCharactersFromLink(input, 0, len(input)); end != expectedEnd {
b.Fatalf("expected end %d, got %d", expectedEnd, end)
}
}
})
}
}
func TestAutolinking(t *testing.T) {
// These tests are adapted from https://github.com/mattermost/commonmark.js/test/mattermost.txt.
// It is missing tests for:
// 1. Links surrounded by emphasis (emphasis not implemented on the server)
// 2. IPv6 addresses (not implemented on the server or by GitHub)
// 3. Custom URL schemes (not implemented)
for name, tc := range map[string]struct {
Markdown string
ExpectedHTML string
}{
"valid-link-1": {
Markdown: `http://example.com`,
ExpectedHTML: `<p><a href="http://example.com">http://example.com</a></p>`,
},
"valid-link-2": {
Markdown: `https://example.com`,
ExpectedHTML: `<p><a href="https://example.com">https://example.com</a></p>`,
},
"valid-link-3": {
Markdown: `ftp://example.com`,
ExpectedHTML: `<p><a href="ftp://example.com">ftp://example.com</a></p>`,
},
// "valid-link-4": {
// Markdown: `ts3server://example.com?port=9000`,
// ExpectedHTML: `<p><a href="ts3server://example.com?port=9000">ts3server://example.com?port=9000</a></p>`,
// },
"valid-link-5": {
Markdown: `www.example.com`,
ExpectedHTML: `<p><a href="http://www.example.com">www.example.com</a></p>`,
},
"valid-link-6": {
Markdown: `www.example.com/index`,
ExpectedHTML: `<p><a href="http://www.example.com/index">www.example.com/index</a></p>`,
},
"valid-link-7": {
Markdown: `www.example.com/index.html`,
ExpectedHTML: `<p><a href="http://www.example.com/index.html">www.example.com/index.html</a></p>`,
},
"valid-link-8": {
Markdown: `http://example.com/index/sub`,
ExpectedHTML: `<p><a href="http://example.com/index/sub">http://example.com/index/sub</a></p>`,
},
"valid-link-9": {
Markdown: `www1.example.com`,
ExpectedHTML: `<p><a href="http://www1.example.com">www1.example.com</a></p>`,
},
"valid-link-10": {
Markdown: `https://en.wikipedia.org/wiki/URLs#Syntax`,
ExpectedHTML: `<p><a href="https://en.wikipedia.org/wiki/URLs#Syntax">https://en.wikipedia.org/wiki/URLs#Syntax</a></p>`,
},
"valid-link-11": {
Markdown: `https://groups.google.com/forum/#!msg`,
ExpectedHTML: `<p><a href="https://groups.google.com/forum/#!msg">https://groups.google.com/forum/#!msg</a></p>`,
},
"valid-link-12": {
Markdown: `www.example.com/index?params=1`,
ExpectedHTML: `<p><a href="http://www.example.com/index?params=1">www.example.com/index?params=1</a></p>`,
},
"valid-link-13": {
Markdown: `www.example.com/index?params=1&other=2`,
ExpectedHTML: `<p><a href="http://www.example.com/index?params=1&amp;other=2">www.example.com/index?params=1&amp;other=2</a></p>`,
},
"valid-link-14": {
Markdown: `www.example.com/index?params=1;other=2`,
ExpectedHTML: `<p><a href="http://www.example.com/index?params=1;other=2">www.example.com/index?params=1;other=2</a></p>`,
},
"valid-link-15": {
Markdown: `http://www.example.com/_/page`,
ExpectedHTML: `<p><a href="http://www.example.com/_/page">http://www.example.com/_/page</a></p>`,
},
"valid-link-16": {
Markdown: `https://en.wikipedia.org/wiki/🐬`,
ExpectedHTML: `<p><a href="https://en.wikipedia.org/wiki/%F0%9F%90%AC">https://en.wikipedia.org/wiki/🐬</a></p>`,
},
"valid-link-17": {
Markdown: `http://✪df.ws/1234`,
ExpectedHTML: `<p><a href="http://%E2%9C%AAdf.ws/1234">http://✪df.ws/1234</a></p>`,
},
"valid-link-18": {
Markdown: `https://groups.google.com/forum/#!msg`,
ExpectedHTML: `<p><a href="https://groups.google.com/forum/#!msg">https://groups.google.com/forum/#!msg</a></p>`,
},
"valid-link-19": {
Markdown: `https://пример.срб/пример-26/`,
ExpectedHTML: `<p><a href="https://%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80.%D1%81%D1%80%D0%B1/%D0%BF%D1%80%D0%B8%D0%BC%D0%B5%D1%80-26/">https://пример.срб/пример-26/</a></p>`,
},
"valid-link-20": {
Markdown: `mailto://test@example.com`,
ExpectedHTML: `<p><a href="mailto://test@example.com">mailto://test@example.com</a></p>`,
},
"valid-link-21": {
Markdown: `tel://555-123-4567`,
ExpectedHTML: `<p><a href="tel://555-123-4567">tel://555-123-4567</a></p>`,
},
"ip-address-1": {
Markdown: `http://127.0.0.1`,
ExpectedHTML: `<p><a href="http://127.0.0.1">http://127.0.0.1</a></p>`,
},
"ip-address-2": {
Markdown: `http://192.168.1.1:4040`,
ExpectedHTML: `<p><a href="http://192.168.1.1:4040">http://192.168.1.1:4040</a></p>`,
},
"ip-address-3": {
Markdown: `http://username:password@127.0.0.1`,
ExpectedHTML: `<p><a href="http://username:password@127.0.0.1">http://username:password@127.0.0.1</a></p>`,
},
"ip-address-4": {
Markdown: `http://username:password@[2001:0:5ef5:79fb:303a:62d5:3312:ff42]:80`,
ExpectedHTML: `<p><a href="http://username:password@%5B2001:0:5ef5:79fb:303a:62d5:3312:ff42%5D:80">http://username:password@[2001:0:5ef5:79fb:303a:62d5:3312:ff42]:80</a></p>`,
},
"link-with-brackets-1": {
Markdown: `https://en.wikipedia.org/wiki/Rendering_(computer_graphics)`,
ExpectedHTML: `<p><a href="https://en.wikipedia.org/wiki/Rendering_(computer_graphics)">https://en.wikipedia.org/wiki/Rendering_(computer_graphics)</a></p>`,
},
"link-with-brackets-2": {
Markdown: `http://example.com/more_(than)_one_(parens)`,
ExpectedHTML: `<p><a href="http://example.com/more_(than)_one_(parens)">http://example.com/more_(than)_one_(parens)</a></p>`,
},
"link-with-brackets-3": {
Markdown: `http://example.com/(something)?after=parens`,
ExpectedHTML: `<p><a href="http://example.com/(something)?after=parens">http://example.com/(something)?after=parens</a></p>`,
},
"link-with-brackets-4": {
Markdown: `http://foo.com/unicode_(✪)_in_parens`,
ExpectedHTML: `<p><a href="http://foo.com/unicode_(%E2%9C%AA)_in_parens">http://foo.com/unicode_(✪)_in_parens</a></p>`,
},
"inside-another-link-1": {
Markdown: `[www.example.com](https://example.com)`,
ExpectedHTML: `<p><a href="https://example.com">www.example.com</a></p>`,
},
"inside-another-link-2": {
Markdown: `[http://www.example.com](https://example.com)`,
ExpectedHTML: `<p><a href="https://example.com">http://www.example.com</a></p>`,
},
"link-in-sentence-1": {
Markdown: `(http://example.com)`,
ExpectedHTML: `<p>(<a href="http://example.com">http://example.com</a>)</p>`,
},
"link-in-sentence-2": {
Markdown: `(see http://example.com)`,
ExpectedHTML: `<p>(see <a href="http://example.com">http://example.com</a>)</p>`,
},
"link-in-sentence-3": {
Markdown: `(http://example.com watch this)`,
ExpectedHTML: `<p>(<a href="http://example.com">http://example.com</a> watch this)</p>`,
},
"link-in-sentence-4": {
Markdown: `This is a sentence with a http://example.com in it.`,
ExpectedHTML: `<p>This is a sentence with a <a href="http://example.com">http://example.com</a> in it.</p>`,
},
"link-in-sentence-5": {
Markdown: `This is a sentence with a [link](http://example.com) in it.`,
ExpectedHTML: `<p>This is a sentence with a <a href="http://example.com">link</a> in it.</p>`,
},
"link-in-sentence-6": {
Markdown: `This is a sentence with a http://example.com/_/underscore in it.`,
ExpectedHTML: `<p>This is a sentence with a <a href="http://example.com/_/underscore">http://example.com/_/underscore</a> in it.</p>`,
},
"link-in-sentence-7": {
Markdown: `This is a sentence with a link (http://example.com) in it.`,
ExpectedHTML: `<p>This is a sentence with a link (<a href="http://example.com">http://example.com</a>) in it.</p>`,
},
"link-in-sentence-8": {
Markdown: `This is a sentence with a (https://en.wikipedia.org/wiki/Rendering_(computer_graphics)) in it.`,
ExpectedHTML: `<p>This is a sentence with a (<a href="https://en.wikipedia.org/wiki/Rendering_(computer_graphics)">https://en.wikipedia.org/wiki/Rendering_(computer_graphics)</a>) in it.</p>`,
},
"link-in-sentence-9": {
Markdown: `This is a sentence with a http://192.168.1.1:4040 in it.`,
ExpectedHTML: `<p>This is a sentence with a <a href="http://192.168.1.1:4040">http://192.168.1.1:4040</a> in it.</p>`,
},
"link-in-sentence-10": {
Markdown: `This is a link to http://example.com.`,
ExpectedHTML: `<p>This is a link to <a href="http://example.com">http://example.com</a>.</p>`,
},
"link-in-sentence-11": {
Markdown: `This is a link to http://example.com*`,
ExpectedHTML: `<p>This is a link to <a href="http://example.com">http://example.com</a>*</p>`,
},
"link-in-sentence-12": {
Markdown: `This is a link to http://example.com_`,
ExpectedHTML: `<p>This is a link to <a href="http://example.com">http://example.com</a>_</p>`,
},
"link-in-sentence-13": {
Markdown: `This is a link containing http://example.com/something?with,commas,in,url, but not at the end`,
ExpectedHTML: `<p>This is a link containing <a href="http://example.com/something?with,commas,in,url">http://example.com/something?with,commas,in,url</a>, but not at the end</p>`,
},
"link-in-sentence-14": {
Markdown: `This is a question about a link http://example.com?`,
ExpectedHTML: `<p>This is a question about a link <a href="http://example.com">http://example.com</a>?</p>`,
},
"plt-7250-link-with-trailing-periods-1": {
Markdown: `http://example.com.`,
ExpectedHTML: `<p><a href="http://example.com">http://example.com</a>.</p>`,
},
"plt-7250-link-with-trailing-periods-2": {
Markdown: `http://example.com...`,
ExpectedHTML: `<p><a href="http://example.com">http://example.com</a>...</p>`,
},
"plt-7250-link-with-trailing-periods-3": {
Markdown: `http://example.com/foo.`,
ExpectedHTML: `<p><a href="http://example.com/foo">http://example.com/foo</a>.</p>`,
},
"plt-7250-link-with-trailing-periods-4": {
Markdown: `http://example.com/foo...`,
ExpectedHTML: `<p><a href="http://example.com/foo">http://example.com/foo</a>...</p>`,
},
"plt-7250-link-with-trailing-periods-5": {
Markdown: `http://example.com/foo.bar`,
ExpectedHTML: `<p><a href="http://example.com/foo.bar">http://example.com/foo.bar</a></p>`,
},
"plt-7250-link-with-trailing-periods-6": {
Markdown: `http://example.com/foo...bar`,
ExpectedHTML: `<p><a href="http://example.com/foo...bar">http://example.com/foo...bar</a></p>`,
},
"rn-319-www-link-as-part-of-word-1": {
Markdown: `testwww.example.com`,
ExpectedHTML: `<p>testwww.example.com</p>`,
},
"mm-10180-link-containing-period-followed-by-non-letter-1": {
Markdown: `https://example.com/123.+Pagetitle`,
ExpectedHTML: `<p><a href="https://example.com/123.+Pagetitle">https://example.com/123.+Pagetitle</a></p>`,
},
"mm-10180-link-containing-period-followed-by-non-letter-2": {
Markdown: `https://example.com/123.?Pagetitle`,
ExpectedHTML: `<p><a href="https://example.com/123.?Pagetitle">https://example.com/123.?Pagetitle</a></p>`,
},
"mm-10180-link-containing-period-followed-by-non-letter-3": {
Markdown: `https://example.com/123.-Pagetitle`,
ExpectedHTML: `<p><a href="https://example.com/123.-Pagetitle">https://example.com/123.-Pagetitle</a></p>`,
},
"mm-10180-link-containing-period-followed-by-non-letter-4": {
Markdown: `https://example.com/123._Pagetitle`,
ExpectedHTML: `<p><a href="https://example.com/123._Pagetitle">https://example.com/123._Pagetitle</a></p>`,
},
"mm-10180-link-containing-period-followed-by-non-letter-5": {
Markdown: `https://example.com/123.+`,
ExpectedHTML: `<p><a href="https://example.com/123.+">https://example.com/123.+</a></p>`,
},
"mm-10180-link-containing-period-followed-by-non-letter-6": {
Markdown: `https://example.com/123.?`,
ExpectedHTML: `<p><a href="https://example.com/123">https://example.com/123</a>.?</p>`,
},
"mm-10180-link-containing-period-followed-by-non-letter-7": {
Markdown: `https://example.com/123.-`,
ExpectedHTML: `<p><a href="https://example.com/123.-">https://example.com/123.-</a></p>`,
},
"mm-10180-link-containing-period-followed-by-non-letter-8": {
Markdown: `https://example.com/123._`,
ExpectedHTML: `<p><a href="https://example.com/123">https://example.com/123</a>._</p>`,
},
} {
t.Run(name, func(t *testing.T) {
assert.Equal(t, tc.ExpectedHTML, RenderHTML(tc.Markdown))
})
}
}
func Test_isAllowedBeforeWWWLink(t *testing.T) {
type args struct {
c byte
}
tests := []struct {
name string
args args
want bool
}{
{
name: "empty space",
args: args{
c: byte(' '),
},
want: false,
},
{
name: "asterisk",
args: args{
c: byte('*'),
},
want: true,
},
{
name: "underscore",
args: args{
c: byte('_'),
},
want: true,
},
{
name: "open parenthesis",
args: args{
c: byte('('),
},
want: true,
},
{
name: "close parenthesis",
args: args{
c: byte(')'),
},
want: true,
},
{
name: "tilde",
args: args{
c: byte('~'),
},
want: true,
},
{
name: "open angle bracket",
args: args{
c: byte('<'),
},
want: true,
},
{
name: "close angle bracket",
args: args{
c: byte('>'),
},
want: true,
},
{
name: "alphabet",
args: args{
c: byte('c'),
},
want: false,
},
{
name: "number",
args: args{
c: byte('4'),
},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
assert.Equalf(t, tt.want, isAllowedBeforeWWWLink(tt.args.c), "isAllowedBeforeWWWLink(%v)", tt.args.c)
})
}
}