2017-04-21 12:31:08 +05:30
// Copyright 2017 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.
2016-04-23 03:58:08 +05:30
package markdown_test
import (
2017-02-14 06:43:59 +05:30
"strings"
2017-09-16 22:47:57 +05:30
"testing"
2017-02-14 06:43:59 +05:30
2021-10-11 17:42:06 +05:30
"code.gitea.io/gitea/modules/log"
2021-04-20 03:55:08 +05:30
"code.gitea.io/gitea/modules/markup"
2017-09-21 10:50:14 +05:30
. "code.gitea.io/gitea/modules/markup/markdown"
2016-11-10 21:54:48 +05:30
"code.gitea.io/gitea/modules/setting"
2018-02-20 18:20:42 +05:30
"code.gitea.io/gitea/modules/util"
2017-09-16 22:47:57 +05:30
2017-02-08 11:59:07 +05:30
"github.com/stretchr/testify/assert"
2016-04-23 03:58:08 +05:30
)
2017-02-14 06:43:59 +05:30
const AppURL = "http://localhost:3000/"
const Repo = "gogits/gogs"
const AppSubURL = AppURL + Repo + "/"
2016-04-23 03:58:08 +05:30
2019-04-12 11:23:34 +05:30
// these values should match the Repo const above
var localMetas = map [ string ] string {
2019-08-14 13:34:55 +05:30
"user" : "gogits" ,
"repo" : "gogs" ,
"repoPath" : "../../../integrations/gitea-repositories-meta/user13/repo11.git/" ,
2019-04-12 11:23:34 +05:30
}
2017-02-24 20:29:56 +05:30
func TestRender_StandardLinks ( t * testing . T ) {
2017-02-14 06:43:59 +05:30
setting . AppURL = AppURL
setting . AppSubURL = AppSubURL
2017-02-24 20:29:56 +05:30
test := func ( input , expected , expectedWiki string ) {
2021-04-20 03:55:08 +05:30
buffer , err := RenderString ( & markup . RenderContext {
URLPrefix : setting . AppSubURL ,
} , input )
assert . NoError ( t , err )
2019-07-24 00:20:39 +05:30
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( buffer ) )
2021-04-20 03:55:08 +05:30
buffer , err = RenderString ( & markup . RenderContext {
URLPrefix : setting . AppSubURL ,
IsWiki : true ,
} , input )
2021-07-18 17:39:34 +05:30
assert . NoError ( t , err )
2021-04-20 03:55:08 +05:30
assert . Equal ( t , strings . TrimSpace ( expectedWiki ) , strings . TrimSpace ( buffer ) )
2017-02-24 20:29:56 +05:30
}
googleRendered := ` <p><a href="https://google.com/" rel="nofollow">https://google.com/</a></p> `
test ( "<https://google.com/>" , googleRendered , googleRendered )
2018-02-20 18:20:42 +05:30
lnk := util . URLJoin ( AppSubURL , "WikiPage" )
lnkWiki := util . URLJoin ( AppSubURL , "wiki" , "WikiPage" )
2017-02-24 20:29:56 +05:30
test ( "[WikiPage](WikiPage)" ,
` <p><a href=" ` + lnk + ` " rel="nofollow">WikiPage</a></p> ` ,
` <p><a href=" ` + lnkWiki + ` " rel="nofollow">WikiPage</a></p> ` )
}
2017-09-16 22:47:57 +05:30
func TestMisc_IsMarkdownFile ( t * testing . T ) {
setting . Markdown . FileExtensions = [ ] string { ".md" , ".markdown" , ".mdown" , ".mkd" }
trueTestCases := [ ] string {
"test.md" ,
"wow.MARKDOWN" ,
"LOL.mDoWn" ,
}
falseTestCases := [ ] string {
"test" ,
"abcdefg" ,
"abcdefghijklmnopqrstuvwxyz" ,
"test.md.test" ,
2017-02-14 06:43:59 +05:30
}
2017-09-16 22:47:57 +05:30
for _ , testCase := range trueTestCases {
assert . True ( t , IsMarkdownFile ( testCase ) )
}
for _ , testCase := range falseTestCases {
assert . False ( t , IsMarkdownFile ( testCase ) )
}
2017-02-24 20:29:56 +05:30
}
func TestRender_Images ( t * testing . T ) {
setting . AppURL = AppURL
setting . AppSubURL = AppSubURL
test := func ( input , expected string ) {
2021-04-20 03:55:08 +05:30
buffer , err := RenderString ( & markup . RenderContext {
URLPrefix : setting . AppSubURL ,
} , input )
assert . NoError ( t , err )
2019-07-24 00:20:39 +05:30
assert . Equal ( t , strings . TrimSpace ( expected ) , strings . TrimSpace ( buffer ) )
2017-02-24 20:29:56 +05:30
}
url := "../../.images/src/02/train.jpg"
title := "Train"
2018-10-31 03:56:28 +05:30
href := "https://gitea.io"
2018-02-20 18:20:42 +05:30
result := util . URLJoin ( AppSubURL , url )
2021-10-11 17:42:06 +05:30
// hint: With Markdown v2.5.2, there is a new syntax: [link](URL){:target="_blank"} , but we do not support it now
2017-02-24 20:29:56 +05:30
test (
"![" + title + "](" + url + ")" ,
2021-10-11 17:42:06 +05:30
` <p><a href=" ` + result + ` " target="_blank" rel="nofollow noopener"><img src=" ` + result + ` " alt=" ` + title + ` "/></a></p> ` )
2017-02-24 20:29:56 +05:30
test (
"[[" + title + "|" + url + "]]" ,
2018-02-27 12:39:18 +05:30
` <p><a href=" ` + result + ` " rel="nofollow"><img src=" ` + result + ` " title=" ` + title + ` " alt=" ` + title + ` "/></a></p> ` )
2018-10-31 03:56:28 +05:30
test (
"[![" + title + "](" + url + ")](" + href + ")" ,
` <p><a href=" ` + href + ` " rel="nofollow"><img src=" ` + result + ` " alt=" ` + title + ` "/></a></p> ` )
2021-04-10 21:56:28 +05:30
url = "/../../.images/src/02/train.jpg"
test (
"![" + title + "](" + url + ")" ,
2021-10-11 17:42:06 +05:30
` <p><a href=" ` + result + ` " target="_blank" rel="nofollow noopener"><img src=" ` + result + ` " alt=" ` + title + ` "/></a></p> ` )
2021-04-10 21:56:28 +05:30
test (
"[[" + title + "|" + url + "]]" ,
` <p><a href=" ` + result + ` " rel="nofollow"><img src=" ` + result + ` " title=" ` + title + ` " alt=" ` + title + ` "/></a></p> ` )
test (
"[![" + title + "](" + url + ")](" + href + ")" ,
` <p><a href=" ` + href + ` " rel="nofollow"><img src=" ` + result + ` " alt=" ` + title + ` "/></a></p> ` )
2017-02-14 06:43:59 +05:30
}
2017-02-24 20:29:56 +05:30
func testAnswers ( baseURLContent , baseURLImages string ) [ ] string {
return [ ] string {
` < p > Wiki ! Enjoy : ) < / p >
< ul >
2017-07-06 21:35:35 +05:30
< li > < a href = "` + baseURLContent + `/Links" rel = "nofollow" > Links , Language bindings , Engine bindings < / a > < / li >
< li > < a href = "` + baseURLContent + `/Tips" rel = "nofollow" > Tips < / a > < / li >
2017-02-24 20:29:56 +05:30
< / ul >
2019-08-14 13:34:55 +05:30
< p > See commit < a href = "http://localhost:3000/gogits/gogs/commit/65f1bf27bc" rel = "nofollow" > < code > 65 f1bf27bc < / code > < / a > < / p >
2017-02-24 20:29:56 +05:30
< p > Ideas and codes < / p >
< ul >
2020-01-20 10:09:21 +05:30
< li > Bezier widget ( by < a href = "` + AppURL + `r-lyeh" rel = "nofollow" > @ r - lyeh < / a > ) < a href = "http://localhost:3000/ocornut/imgui/issues/786" class = "ref-issue" rel = "nofollow" > ocornut / imgui # 786 < / a > < / li >
< li > Bezier widget ( by < a href = "` + AppURL + `r-lyeh" rel = "nofollow" > @ r - lyeh < / a > ) < a href = "http://localhost:3000/gogits/gogs/issues/786" class = "ref-issue" rel = "nofollow" > # 786 < / a > < / li >
2018-02-27 12:39:18 +05:30
< li > Node graph editors < a href = "https://github.com/ocornut/imgui/issues/306" rel = "nofollow" > https : //github.com/ocornut/imgui/issues/306</a></li>
2017-07-06 21:35:35 +05:30
< li > < a href = "` + baseURLContent + `/memory_editor_example" rel = "nofollow" > Memory Editor < / a > < / li >
< li > < a href = "` + baseURLContent + `/plot_var_example" rel = "nofollow" > Plot var helper < / a > < / li >
2017-02-24 20:29:56 +05:30
< / ul >
` ,
2019-12-24 04:08:50 +05:30
` < h2 id = "user-content-what-is-wine-staging" > What is Wine Staging ? < / h2 >
2017-02-14 06:43:59 +05:30
< p > < strong > Wine Staging < / strong > on website < a href = "http://wine-staging.com" rel = "nofollow" > wine - staging . com < / a > . < / p >
2019-12-24 04:08:50 +05:30
< h2 id = "user-content-quick-links" > Quick Links < / h2 >
2017-02-14 06:43:59 +05:30
< p > Here are some links to the most important topics . You can find the full list of pages at the sidebar . < / p >
< table >
< thead >
< tr >
2018-02-27 12:39:18 +05:30
< th > < a href = "` + baseURLImages + `/images/icon-install.png" rel = "nofollow" > < img src = "` + baseURLImages + `/images/icon-install.png" title = "icon-install.png" alt = "images/icon-install.png" / > < / a > < / th >
2017-07-06 21:35:35 +05:30
< th > < a href = "` + baseURLContent + `/Installation" rel = "nofollow" > Installation < / a > < / th >
2017-02-14 06:43:59 +05:30
< / tr >
< / thead >
< tbody >
< tr >
2018-02-27 12:39:18 +05:30
< td > < a href = "` + baseURLImages + `/images/icon-usage.png" rel = "nofollow" > < img src = "` + baseURLImages + `/images/icon-usage.png" title = "icon-usage.png" alt = "images/icon-usage.png" / > < / a > < / td >
2017-07-06 21:35:35 +05:30
< td > < a href = "` + baseURLContent + `/Usage" rel = "nofollow" > Usage < / a > < / td >
2017-02-14 06:43:59 +05:30
< / tr >
< / tbody >
< / table >
` ,
2017-02-24 20:29:56 +05:30
` < p > < a href = "http://www.excelsiorjet.com/" rel = "nofollow" > Excelsior JET < / a > allows you to create native executables for Windows , Linux and Mac OS X . < / p >
2017-02-14 06:43:59 +05:30
< ol >
2020-04-22 03:43:56 +05:30
< li > < a href = "https://github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop" rel = "nofollow" > Package your libGDX application < / a > < br / >
2018-02-27 12:39:18 +05:30
< a href = "` + baseURLImages + `/images/1.png" rel = "nofollow" > < img src = "` + baseURLImages + `/images/1.png" title = "1.png" alt = "images/1.png" / > < / a > < / li >
2020-04-22 03:43:56 +05:30
< li > Perform a test run by hitting the Run ! button . < br / >
2018-02-27 12:39:18 +05:30
< a href = "` + baseURLImages + `/images/2.png" rel = "nofollow" > < img src = "` + baseURLImages + `/images/2.png" title = "2.png" alt = "images/2.png" / > < / a > < / li >
2017-02-14 06:43:59 +05:30
< / ol >
2019-12-24 04:08:50 +05:30
< h2 id = "user-content-custom-id" > More tests < / h2 >
2019-03-21 19:23:06 +05:30
< p > ( from < a href = "https://www.markdownguide.org/extended-syntax/" rel = "nofollow" > https : //www.markdownguide.org/extended-syntax/</a>)</p>
2020-05-04 01:47:24 +05:30
< h3 id = "user-content-checkboxes" > Checkboxes < / h3 >
2020-05-11 04:44:49 +05:30
< ul >
2021-05-23 19:44:03 +05:30
< li class = "task-list-item" > < input type = "checkbox" disabled = "" data - source - position = "434" / > unchecked < / li >
< li class = "task-list-item" > < input type = "checkbox" disabled = "" data - source - position = "450" checked = "" / > checked < / li >
< li class = "task-list-item" > < input type = "checkbox" disabled = "" data - source - position = "464" / > still unchecked < / li >
2020-05-04 01:47:24 +05:30
< / ul >
2019-12-24 04:08:50 +05:30
< h3 id = "user-content-definition-list" > Definition list < / h3 >
2019-03-21 19:23:06 +05:30
< dl >
< dt > First Term < / dt >
< dd > This is the definition of the first term . < / dd >
< dt > Second Term < / dt >
< dd > This is one definition of the second term . < / dd >
< dd > This is another definition of the second term . < / dd >
< / dl >
2019-12-24 04:08:50 +05:30
< h3 id = "user-content-footnotes" > Footnotes < / h3 >
< p > Here is a simple footnote , < sup id = "fnref:user-content-1" > < a href = "#fn:user-content-1" rel = "nofollow" > 1 < / a > < / sup > and here is a longer one . < sup id = "fnref:user-content-bignote" > < a href = "#fn:user-content-bignote" rel = "nofollow" > 2 < / a > < / sup > < / p >
2019-03-21 19:23:06 +05:30
< div >
< hr / >
< ol >
2019-12-31 07:23:28 +05:30
< li id = "fn:user-content-1" >
< p > This is the first footnote . < a href = "#fnref:user-content-1" rel = "nofollow" > ↩ ︎ < / a > < / p >
< / li >
< li id = "fn:user-content-bignote" >
< p > Here is one with multiple paragraphs and code . < / p >
2019-03-21 19:23:06 +05:30
< p > Indent paragraphs to include them in the footnote . < / p >
< p > < code > { my code } < / code > < / p >
2019-12-31 07:23:28 +05:30
< p > Add as many paragraphs as you like . < a href = "#fnref:user-content-bignote" rel = "nofollow" > ↩ ︎ < / a > < / p >
< / li >
2019-03-21 19:23:06 +05:30
< / ol >
< / div >
2017-02-14 06:43:59 +05:30
` ,
2017-02-24 20:29:56 +05:30
}
2017-02-14 06:43:59 +05:30
}
2017-09-16 22:47:57 +05:30
// Test cases without ambiguous links
var sameCases = [ ] string {
// dear imgui wiki markdown extract: special wiki syntax
` Wiki ! Enjoy : )
- [ [ Links , Language bindings , Engine bindings | Links ] ]
- [ [ Tips ] ]
2017-02-24 20:29:56 +05:30
2019-08-14 13:34:55 +05:30
See commit 65 f1bf27bc
2019-04-16 13:23:57 +05:30
2017-09-16 22:47:57 +05:30
Ideas and codes
2017-02-14 06:43:59 +05:30
2017-09-16 22:47:57 +05:30
- Bezier widget ( by @ r - lyeh ) ` + AppURL + ` ocornut / imgui / issues / 786
2019-04-12 11:23:34 +05:30
- Bezier widget ( by @ r - lyeh ) ` + AppURL + ` gogits / gogs / issues / 786
2017-09-16 22:47:57 +05:30
- Node graph editors https : //github.com/ocornut/imgui/issues/306
- [ [ Memory Editor | memory_editor_example ] ]
- [ [ Plot var helper | plot_var_example ] ] ` ,
// wine-staging wiki home extract: tables, special wiki syntax, images
` # # What is Wine Staging ?
* * Wine Staging * * on website [ wine - staging . com ] ( http : //wine-staging.com).
2017-02-14 06:43:59 +05:30
2017-09-16 22:47:57 +05:30
# # Quick Links
Here are some links to the most important topics . You can find the full list of pages at the sidebar .
| [ [ images / icon - install . png ] ] | [ [ Installation ] ] |
| -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- | -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- |
| [ [ images / icon - usage . png ] ] | [ [ Usage ] ] |
` ,
// libgdx wiki page: inline images with special syntax
` [ Excelsior JET ] ( http : //www.excelsiorjet.com/) allows you to create native executables for Windows, Linux and Mac OS X.
1. [ Package your libGDX application ] ( https : //github.com/libgdx/libgdx/wiki/Gradle-on-the-Commandline#packaging-for-the-desktop)
[ [ images / 1. png ] ]
2. Perform a test run by hitting the Run ! button .
2019-03-21 19:23:06 +05:30
[ [ images / 2. png ] ]
# # More tests { # custom - id }
( from https : //www.markdownguide.org/extended-syntax/)
2020-05-04 01:47:24 +05:30
# # # Checkboxes
- [ ] unchecked
- [ x ] checked
- [ ] still unchecked
2019-03-21 19:23:06 +05:30
# # # Definition list
First Term
: This is the definition of the first term .
Second Term
: This is one definition of the second term .
: This is another definition of the second term .
# # # Footnotes
Here is a simple footnote , [ ^ 1 ] and here is a longer one . [ ^ bignote ]
[ ^ 1 ] : This is the first footnote .
[ ^ bignote ] : Here is one with multiple paragraphs and code .
Indent paragraphs to include them in the footnote .
` + " ` { my code } ` " + `
Add as many paragraphs as you like .
` ,
2017-02-14 06:43:59 +05:30
}
func TestTotal_RenderWiki ( t * testing . T ) {
2021-05-09 18:59:49 +05:30
setting . AppURL = AppURL
setting . AppSubURL = AppSubURL
2018-02-20 18:20:42 +05:30
answers := testAnswers ( util . URLJoin ( AppSubURL , "wiki/" ) , util . URLJoin ( AppSubURL , "wiki" , "raw/" ) )
2017-02-24 20:29:56 +05:30
for i := 0 ; i < len ( sameCases ) ; i ++ {
2021-04-20 03:55:08 +05:30
line , err := RenderString ( & markup . RenderContext {
URLPrefix : AppSubURL ,
Metas : localMetas ,
IsWiki : true ,
} , sameCases [ i ] )
assert . NoError ( t , err )
2017-02-24 20:29:56 +05:30
assert . Equal ( t , answers [ i ] , line )
2017-02-14 06:43:59 +05:30
}
testCases := [ ] string {
// Guard wiki sidebar: special syntax
` [[Guardfile-DSL / Configuring-Guard|Guardfile-DSL---Configuring-Guard]] ` ,
// rendered
` <p><a href=" ` + AppSubURL + ` wiki / Guardfile - DSL -- - Configuring - Guard " rel=" nofollow " > Guardfile - DSL / Configuring - Guard < / a > < / p >
` ,
// special syntax
` [[Name|Link]] ` ,
// rendered
` <p><a href=" ` + AppSubURL + ` wiki / Link " rel=" nofollow " > Name < / a > < / p >
` ,
}
for i := 0 ; i < len ( testCases ) ; i += 2 {
2021-04-20 03:55:08 +05:30
line , err := RenderString ( & markup . RenderContext {
URLPrefix : AppSubURL ,
IsWiki : true ,
} , testCases [ i ] )
assert . NoError ( t , err )
2017-02-14 06:43:59 +05:30
assert . Equal ( t , testCases [ i + 1 ] , line )
}
2016-04-23 03:58:08 +05:30
}
2017-09-16 22:47:57 +05:30
func TestTotal_RenderString ( t * testing . T ) {
2021-05-09 18:59:49 +05:30
setting . AppURL = AppURL
setting . AppSubURL = AppSubURL
2018-02-20 18:20:42 +05:30
answers := testAnswers ( util . URLJoin ( AppSubURL , "src" , "master/" ) , util . URLJoin ( AppSubURL , "raw" , "master/" ) )
2017-09-16 22:47:57 +05:30
for i := 0 ; i < len ( sameCases ) ; i ++ {
2021-04-20 03:55:08 +05:30
line , err := RenderString ( & markup . RenderContext {
URLPrefix : util . URLJoin ( AppSubURL , "src" , "master/" ) ,
Metas : localMetas ,
} , sameCases [ i ] )
assert . NoError ( t , err )
2017-09-16 22:47:57 +05:30
assert . Equal ( t , answers [ i ] , line )
}
testCases := [ ] string { }
for i := 0 ; i < len ( testCases ) ; i += 2 {
2021-04-20 03:55:08 +05:30
line , err := RenderString ( & markup . RenderContext {
URLPrefix : AppSubURL ,
} , testCases [ i ] )
assert . NoError ( t , err )
2017-09-16 22:47:57 +05:30
assert . Equal ( t , testCases [ i + 1 ] , line )
}
}
2019-11-13 07:57:11 +05:30
func TestRender_RenderParagraphs ( t * testing . T ) {
test := func ( t * testing . T , str string , cnt int ) {
2021-04-20 03:55:08 +05:30
res , err := RenderRawString ( & markup . RenderContext { } , str )
assert . NoError ( t , err )
assert . Equal ( t , cnt , strings . Count ( res , "<p" ) , "Rendered result for unix should have %d paragraph(s) but has %d:\n%s\n" , cnt , strings . Count ( res , "<p" ) , res )
mac := strings . ReplaceAll ( str , "\n" , "\r" )
res , err = RenderRawString ( & markup . RenderContext { } , mac )
assert . NoError ( t , err )
assert . Equal ( t , cnt , strings . Count ( res , "<p" ) , "Rendered result for mac should have %d paragraph(s) but has %d:\n%s\n" , cnt , strings . Count ( res , "<p" ) , res )
dos := strings . ReplaceAll ( str , "\n" , "\r\n" )
res , err = RenderRawString ( & markup . RenderContext { } , dos )
assert . NoError ( t , err )
assert . Equal ( t , cnt , strings . Count ( res , "<p" ) , "Rendered result for windows should have %d paragraph(s) but has %d:\n%s\n" , cnt , strings . Count ( res , "<p" ) , res )
2019-11-13 07:57:11 +05:30
}
test ( t , "\nOne\nTwo\nThree" , 1 )
test ( t , "\n\nOne\nTwo\nThree" , 1 )
test ( t , "\n\nOne\nTwo\nThree\n\n\n" , 1 )
test ( t , "A\n\nB\nC\n" , 2 )
test ( t , "A\n\n\nB\nC\n" , 2 )
}
2021-03-14 22:06:51 +05:30
2021-03-16 04:50:05 +05:30
func TestMarkdownRenderRaw ( t * testing . T ) {
testcases := [ ] [ ] byte {
{ // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6267570554535936
0x2a , 0x20 , 0x2d , 0x0a , 0x09 , 0x20 , 0x60 , 0x5b , 0x0a , 0x09 , 0x20 , 0x60 ,
0x5b ,
} ,
{ // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6278827345051648
0x2d , 0x20 , 0x2d , 0x0d , 0x09 , 0x60 , 0x0d , 0x09 , 0x60 ,
} ,
{ // clusterfuzz_testcase_minimized_fuzz_markdown_render_raw_6016973788020736[] = {
0x7b , 0x63 , 0x6c , 0x61 , 0x73 , 0x73 , 0x3d , 0x35 , 0x7d , 0x0a , 0x3d ,
} ,
}
for _ , testcase := range testcases {
2021-10-11 17:42:06 +05:30
log . Info ( "Test markdown render error with fuzzy data: %x, the following errors can be recovered" , testcase )
2021-04-20 03:55:08 +05:30
_ , err := RenderRawString ( & markup . RenderContext { } , string ( testcase ) )
assert . NoError ( t , err )
2021-03-16 04:50:05 +05:30
}
}
2021-03-14 22:06:51 +05:30
func TestRenderSiblingImages_Issue12925 ( t * testing . T ) {
testcase := ` ! [ image1 ] ( / image1 )
! [ image2 ] ( / image2 )
`
2021-10-11 17:42:06 +05:30
expected := ` < p > < a href = "/image1" target = "_blank" rel = "nofollow noopener" > < img src = "/image1" alt = "image1" > < / a > < br >
< a href = "/image2" target = "_blank" rel = "nofollow noopener" > < img src = "/image2" alt = "image2" > < / a > < / p >
2021-03-14 22:06:51 +05:30
`
2021-04-20 03:55:08 +05:30
res , err := RenderRawString ( & markup . RenderContext { } , testcase )
assert . NoError ( t , err )
2021-03-14 22:06:51 +05:30
assert . Equal ( t , expected , res )
2021-03-16 04:50:05 +05:30
2021-03-14 22:06:51 +05:30
}