dati

A Go library/binary to parse & execute data against template langauges.
git clone git://src.gearsix.net/dati
Log | Files | Refs | Atom | README | LICENSE

template_test.go (10075B)


      1 package dati
      2 
      3 /*
      4 Copyright (C) 2023 gearsix <gearsix@tuta.io>
      5 
      6 This program is free software: you can redistribute it and/or modify
      7 it under the terms of the GNU General Public License as published by
      8 the Free Software Foundation, either version 3 of the License, or
      9 at your option) any later version.
     10 
     11 This program is distributed in the hope that it will be useful,
     12 but WITHOUT ANY WARRANTY; without even the implied warranty of
     13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     14 GNU General Public License for more details.
     15 
     16 You should have received a copy of the GNU General Public License
     17 along with this program.  If not, see <https://www.gnu.org/licenses/>.
     18 */
     19 
     20 import (
     21 	"bytes"
     22 	"io/ioutil"
     23 	"os"
     24 	"path/filepath"
     25 	"reflect"
     26 	"strings"
     27 	"testing"
     28 )
     29 
     30 const tmplRootGood = `{{.eg}} {{ template "tmplPartialGood" . }}`
     31 const tmplPartialGood = `{{.eg}}`
     32 const tmplResult = `0 0`
     33 const tmplRootBad = `{{ example }}} {{{ template \"tmplPartialBad\" . }}`
     34 const tmplPartialBad = `{{{ .example }}`
     35 
     36 const htmplRootGood = `<!DOCTYPE html><html><p>{{.eg}} {{ template "htmplPartialGood" . }}</p></html>`
     37 const htmplPartialGood = `<b>{{.eg}}</b>`
     38 const htmplResult = `<!DOCTYPE html><html><p>0 <b>0</b></p></html>`
     39 const htmplRootBad = `{{ example }} {{{ template "htmplPartialBad" . }}`
     40 const htmplPartialBad = `<b>{{{ .example2 }}</b>`
     41 
     42 const mstRootGood = `{{eg}} {{> mstPartialGood}}`
     43 const mstPartialGood = `{{eg}}`
     44 const mstResult = `0 0`
     45 const mstRootBad = `{{> badPartial.mst}}{{#doesnt-exist}}{{/exit}}`
     46 const mstPartialBad = `p{{$}}{{ > noexist}`
     47 
     48 var templateExts = []string{
     49 	".tmpl", "tmpl", "TMPL", ".TMPL",
     50 	".htmpl", "htmpl", "HTMPL", ".HTMPL",
     51 	".mst", "mst", "MST", ".MST",
     52 	".NONE", "-", ".", "",
     53 }
     54 
     55 func TestIsTemplateLanguage(t *testing.T) {
     56 	for i, ext := range templateExts {
     57 		var target bool
     58 
     59 		if i < 12 {
     60 			target = true
     61 		}
     62 
     63 		is := IsTemplateLanguage(ext)
     64 		if is != target {
     65 			t.Fatalf("%t did not return %t", is, target)
     66 		}
     67 	}
     68 }
     69 
     70 func TestReadTemplateLanguage(t *testing.T) {
     71 	for i, ext := range templateExts {
     72 		var target TemplateLanguage
     73 
     74 		if i < 4 {
     75 			target = TMPL
     76 		} else if i < 8 {
     77 			target = HTMPL
     78 		} else if i < 12 {
     79 			target = MST
     80 		} else {
     81 			target = ""
     82 		}
     83 
     84 		if ReadTemplateLangauge(ext) != target {
     85 			if target == "" {
     86 				t.Fatalf("%s is not a supported data language", ext)
     87 			} else {
     88 				t.Fatalf("%s did not return %s", ext, target)
     89 			}
     90 		}
     91 	}
     92 }
     93 
     94 func validateTemplate(t *testing.T, template Template, templateType string, rootName string, partialNames ...string) {
     95 	types := map[string]string{
     96 		"tmpl":  "*template.Template",
     97 		"htmpl": "*template.Template",
     98 		"mst":   "*mustache.Template",
     99 	}
    100 
    101 	rt := reflect.TypeOf(template.T).String()
    102 	if rt != types[templateType] {
    103 		t.Fatalf("invalid template type '%s' loaded, should be '%s' (%s)", rt, types[templateType], templateType)
    104 	}
    105 
    106 	if types[templateType] == "*template.T" {
    107 		var rv []reflect.Value
    108 		for _, p := range partialNames {
    109 			rv := reflect.ValueOf(template.T).MethodByName("Lookup").Call([]reflect.Value{reflect.ValueOf(p)})
    110 			if rv[0].IsNil() {
    111 				t.Fatalf("missing defined template '%s'", p)
    112 				rv = reflect.ValueOf(template.T).MethodByName("DefinedTemplates").Call([]reflect.Value{})
    113 				t.Log(rv)
    114 			}
    115 		}
    116 		rv = reflect.ValueOf(template.T).MethodByName("Name").Call([]reflect.Value{})
    117 		if rv[0].String() != rootName {
    118 			t.Fatalf("invalid template name: %s does not match %s", rv[0].String(), rootName)
    119 		}
    120 	}
    121 }
    122 
    123 func validateTemplateFile(t *testing.T, template Template, rootPath string, partialPaths ...string) {
    124 	rType := ReadTemplateLangauge(rootPath)
    125 	rName := filepath.Base(rootPath)
    126 	if rType == "mst" {
    127 		rName = strings.TrimSuffix(rName, filepath.Ext(rName))
    128 	}
    129 	var pNames []string
    130 	for _, path := range partialPaths {
    131 		name := filepath.Base(path)
    132 		if rType == "mst" {
    133 			name = strings.TrimSuffix(name, filepath.Ext(name))
    134 		}
    135 		pNames = append(pNames, name)
    136 	}
    137 
    138 	validateTemplate(t, template, rootPath, rName, pNames...)
    139 }
    140 
    141 func TestLoadTemplateFile(t *testing.T) {
    142 	t.Parallel()
    143 
    144 	tdir := os.TempDir()
    145 	var goodRoots, goodPartials, badRoots, badPartials []string
    146 
    147 	createFile := func(path string, data string) {
    148 		if err := ioutil.WriteFile(path, []byte(data), 0666); err != nil {
    149 			t.Error(err)
    150 		}
    151 	}
    152 
    153 	goodRoots = append(goodRoots, tdir+"/goodRoot.tmpl")
    154 	createFile(goodRoots[len(goodRoots)-1], tmplRootGood)
    155 	goodPartials = append(goodPartials, tdir+"/goodPartial.tmpl")
    156 	createFile(goodPartials[len(goodPartials)-1], tmplPartialGood)
    157 	badRoots = append(badRoots, tdir+"/badRoot.tmpl")
    158 	createFile(badRoots[len(badRoots)-1], tmplRootBad)
    159 	badPartials = append(badRoots, tdir+"/badPartials.tmpl")
    160 	createFile(badPartials[len(badPartials)-1], tmplPartialBad)
    161 
    162 	goodRoots = append(goodRoots, tdir+"/goodRoot.htmpl")
    163 	createFile(goodRoots[len(goodRoots)-1], htmplRootGood)
    164 	goodPartials = append(goodPartials, tdir+"/goodPartial.htmpl")
    165 	createFile(goodPartials[len(goodPartials)-1], htmplPartialGood)
    166 	badRoots = append(badRoots, tdir+"/badRoot.htmpl")
    167 	createFile(badRoots[len(badRoots)-1], htmplRootBad)
    168 	badPartials = append(badRoots, tdir+"/badPartials.htmpl")
    169 	createFile(badPartials[len(badPartials)-1], htmplPartialBad)
    170 
    171 	goodRoots = append(goodRoots, tdir+"/goodRoot.mst")
    172 	createFile(goodRoots[len(goodRoots)-1], mstRootGood)
    173 	goodPartials = append(goodPartials, tdir+"/goodPartial.mst")
    174 	createFile(goodPartials[len(goodPartials)-1], mstPartialGood)
    175 	badRoots = append(badRoots, tdir+"/badRoot.mst")
    176 	createFile(badRoots[len(badRoots)-1], mstRootBad)
    177 	badPartials = append(badRoots, tdir+"/badPartials.mst")
    178 	createFile(badPartials[len(badPartials)-1], mstPartialBad)
    179 
    180 	for i, root := range goodRoots { // good root, good partials
    181 		if template, e := LoadTemplateFile(root, goodPartials[i]); e != nil {
    182 			t.Fatal(e)
    183 		} else {
    184 			validateTemplateFile(t, template, root, goodPartials[i])
    185 		}
    186 	}
    187 	for i, root := range badRoots { // bad root, good partials
    188 		if _, e := LoadTemplateFile(root, goodPartials[i]); e == nil {
    189 			t.Fatalf("no error for bad template with good partials\n")
    190 		}
    191 	}
    192 	for i, root := range badRoots { // bad root, bad partials
    193 		if _, e := LoadTemplateFile(root, badPartials[i]); e == nil {
    194 			t.Fatalf("no error for bad template with bad partials\n")
    195 		}
    196 	}
    197 }
    198 
    199 func TestLoadTemplateString(t *testing.T) {
    200 	var err error
    201 	var template Template
    202 	var templateType TemplateLanguage
    203 
    204 	testInvalid := func(templateType TemplateLanguage, template Template) {
    205 		t.Logf("invalid '%s' template managed to load", templateType)
    206 		if buf, err := template.Execute(""); err == nil {
    207 			t.Fatalf("invalid '%s' template managed to execute: %s", templateType, buf.String())
    208 		}
    209 	}
    210 
    211 	name := "test"
    212 	templateType = TMPL
    213 	if _, err = LoadTemplateString(templateType, name, tmplRootGood,
    214 		map[string]string{"tmplPartialGood": tmplPartialGood}); err != nil {
    215 		t.Fatalf("'%s' template failed to load", templateType)
    216 	}
    217 	if _, err = LoadTemplateString(templateType, name, tmplRootBad,
    218 		map[string]string{"tmplPartialGood": tmplPartialGood}); err == nil {
    219 		testInvalid(templateType, template)
    220 	}
    221 	if _, err = LoadTemplateString(templateType, name, tmplRootGood,
    222 		map[string]string{"tmplPartialGood": tmplPartialBad}); err == nil {
    223 		testInvalid(templateType, template)
    224 	}
    225 
    226 	templateType = "htmpl"
    227 	if template, err = LoadTemplateString(templateType, name, htmplRootGood,
    228 		map[string]string{"htmplPartialGood": htmplPartialGood}); err != nil {
    229 		t.Fatalf("'%s' template failed to load", templateType)
    230 	}
    231 	if template, err = LoadTemplateString(templateType, name, htmplRootBad,
    232 		map[string]string{"htmplPartialGood": htmplPartialGood}); err == nil {
    233 		testInvalid(templateType, template)
    234 	}
    235 	if template, err = LoadTemplateString(templateType, name, htmplRootGood,
    236 		map[string]string{"htmplPartialGood": htmplPartialBad}); err == nil {
    237 		testInvalid(templateType, template)
    238 	}
    239 
    240 	templateType = "mst"
    241 	if template, err = LoadTemplateString(templateType, name, mstRootGood,
    242 		map[string]string{"mstPartialGood": mstPartialGood}); err != nil {
    243 		t.Fatalf("'%s' template failed to load", templateType)
    244 	}
    245 	if template, err = LoadTemplateString(templateType, name, mstRootBad,
    246 		map[string]string{"mstPartialGood": mstPartialGood}); err == nil {
    247 		testInvalid(templateType, template)
    248 	}
    249 	if template, err = LoadTemplateString(templateType, name, mstRootGood,
    250 		map[string]string{"mstPartialGood": mstPartialBad}); err == nil {
    251 		testInvalid(templateType, template)
    252 	}
    253 }
    254 
    255 // func TestLoadTemplateString(t *testing.T) {} // This is tested by TestLoadTemplateFile and TestLoadTemplateString
    256 
    257 func validateExecute(t *testing.T, results string, expect string, e error) {
    258 	if e != nil {
    259 		t.Fatal(e)
    260 	} else if results != expect {
    261 		t.Fatalf("invalid results: '%s' should match '%s'", results, expect)
    262 	}
    263 }
    264 
    265 func TestExecute(t *testing.T) {
    266 	t.Parallel()
    267 
    268 	var err error
    269 	var tmpl Template
    270 	var data map[string]interface{}
    271 	var results bytes.Buffer
    272 
    273 	if tmpl, err = LoadTemplateString("tmpl", "tmplRootGood", tmplRootGood,
    274 		map[string]string{"tmplPartialGood": tmplPartialGood}); err != nil {
    275 		t.Skip("setup failure:", err)
    276 	}
    277 	if err = LoadData("json", strings.NewReader(good["json"]), &data); err != nil {
    278 		t.Skip("setup failure:", err)
    279 	}
    280 	results, err = tmpl.Execute(data)
    281 	validateExecute(t, results.String(), tmplResult, err)
    282 
    283 	if tmpl, err = LoadTemplateString("htmpl", "htmplRootGood", htmplRootGood,
    284 		map[string]string{"htmplPartialGood": htmplPartialGood}); err != nil {
    285 		t.Skip("setup failure:", err)
    286 	}
    287 	if err = LoadData("yaml", strings.NewReader(good["yaml"]), &data); err != nil {
    288 		t.Skip("setup failure:", err)
    289 	}
    290 	results, err = tmpl.Execute(data)
    291 	validateExecute(t, results.String(), htmplResult, err)
    292 
    293 	if tmpl, err = LoadTemplateString("mst", "mstRootGood", mstRootGood,
    294 		map[string]string{"mstPartialGood": mstPartialGood}); err != nil {
    295 		t.Skip("setup failure:", err)
    296 	}
    297 	if err = LoadData("toml", strings.NewReader(good["toml"]), &data); err != nil {
    298 		t.Skip("setup failure:", err)
    299 	}
    300 	results, err = tmpl.Execute(data)
    301 	validateExecute(t, results.String(), mstResult, err)
    302 }