Skip to content
Snippets Groups Projects
Commit 74f1ce70 authored by Semyon Krestyaninov's avatar Semyon Krestyaninov :dog2: Committed by Pavel Antonov
Browse files

chore(core): Рефакторинг `template.Builder`

Issue: #3360
parent 4613dc12
No related branches found
No related tags found
No related merge requests found
package template package template
import ( import (
"bytes"
"context" "context"
html "html/template"
"io"
"maps"
text "text/template"
"git.perx.ru/perxis/perxis-go/pkg/collections" "git.perx.ru/perxis/perxis-go/pkg/collections"
"git.perx.ru/perxis/perxis-go/pkg/content" "git.perx.ru/perxis/perxis-go/pkg/content"
...@@ -15,198 +10,138 @@ import ( ...@@ -15,198 +10,138 @@ import (
) )
type Template interface { type Template interface {
Execute(w io.Writer, data any) error // Execute выполняет заданный шаблон pattern с предоставленными данными data и возвращает результат в виде строки.
Execute(pattern string, data ...any) (string, error)
// ExecuteList выполняет каждый шаблон из списка patterns с предоставленными данными data и возвращает результаты в виде среза строк.
// Каждый шаблон обрабатывается индивидуально.
ExecuteList(patterns []string, data ...any) ([]string, error)
// ExecuteMap выполняет шаблоны из мапы patternMap с предоставленными данными data и возвращает результаты в виде мапы.
// Шаблоны применяются к значениям мапы, если они являются строками или срезами строк; в противном случае, значения остаются без изменений.
ExecuteMap(patternMap map[string]any, data ...any) (map[string]any, error)
} }
type Builder struct { type Builder struct {
conf *BuilderConfig
ctx context.Context ctx context.Context
cnt *content.Content
SpaceID string
EnvID string
CollID string
data map[string]any data map[string]any
funcMap map[string]any
// Для кеширования запросов // Для кеширования запросов
space *spaces.Space space *spaces.Space
environment *environments.Environment environment *environments.Environment
collection *collections.Collection collection *collections.Collection
template func(templ string) (Template, error)
}
func NewBuilder(cnt *content.Content, space, env, coll string) *Builder {
b := &Builder{
ctx: context.Background(),
cnt: cnt,
SpaceID: space,
EnvID: env,
CollID: coll,
}
b.template = func(templ string) (Template, error) {
t, err := text.New("main").
Funcs(b.getFuncs()).
Parse(templ)
if err != nil {
return nil, err
}
return t, nil
}
return b
} }
func NewHTMLBuilder(cnt *content.Content, space, env, coll string) *Builder { type BuilderConfig struct {
b := &Builder{ // DefaultHTML если true, по умолчанию используется шаблонизатор для HTML данных,
ctx: context.Background(), // иначе для текстовых.
cnt: cnt, DefaultHTML bool
SpaceID: space,
EnvID: env,
CollID: coll,
}
b.template = func(templ string) (Template, error) {
t, err := html.New("main").
Funcs(b.getFuncs()).
Parse(templ)
if err != nil {
return nil, err
}
return t, nil
}
return b
}
func (b *Builder) getFuncs() map[string]any { Content *content.Content
return map[string]any{ SpaceID string
"lookup": getLookup(b), EnvironmentID string
"system": getSystem(b), CollectionID string
}
} }
func (b *Builder) WithData(data map[string]any) *Builder { func NewBuilder(conf *BuilderConfig) *Builder {
bld := *b if conf == nil {
bld.data = data conf = &BuilderConfig{}
return &bld
} }
func (b *Builder) WithKV(kv ...any) *Builder { builder := &Builder{
bld := *b conf: conf,
if bld.data == nil { ctx: context.Background(),
//nolint:mnd // Количество аргументов делится на 2, так как они представлены в виде пар ключ-значение. data: make(map[string]any),
bld.data = make(map[string]any, len(kv)/2)
}
for i := 0; i < len(kv)-1; i += 2 {
k, _ := kv[i].(string)
v := kv[i+1]
if k != "" && v != nil {
bld.data[k] = v
}
} }
return &bld builder.funcMap = map[string]any{
"lookup": getLookup(builder),
"system": getSystem(builder),
} }
func (b *Builder) GetData() map[string]any { return builder
return b.data
} }
func (b *Builder) WithSpace(space, env string) *Builder { // TextTemplate возвращает новый шаблонизатор для работы с текстовыми данными.
bld := *b // При выполнении шаблонов доступны данные и функции из Builder.
bld.SpaceID = space func (b *Builder) TextTemplate() Template {
bld.EnvID = env return NewCommonTemplate(false, b.data, b.funcMap)
return &bld
} }
func (b *Builder) WithContext(ctx context.Context) *Builder { // HTMLTemplate возвращает новый шаблонизатор для работы с HTML данными.
bld := *b // При выполнении шаблонов доступны данные и функции из Builder.
bld.ctx = ctx func (b *Builder) HTMLTemplate() Template {
return &bld return NewCommonTemplate(true, b.data, b.funcMap)
} }
func (b *Builder) Context() context.Context { func (b *Builder) Context() context.Context {
return b.ctx return b.ctx
} }
func (b *Builder) Template(text string) (Template, error) { func (b *Builder) WithContext(ctx context.Context) *Builder {
return b.template(text) clone := *b
clone.ctx = ctx
return &clone
} }
func (b *Builder) Execute(templ string, data ...any) (string, error) { func (b *Builder) Content() *content.Content {
t, err := b.Template(templ) return b.conf.Content
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
if err = t.Execute(buf, b.getData(data...)); err != nil {
return "", err
} }
return buf.String(), nil
}
func (b *Builder) ExecuteList(templs []string, data ...any) ([]string, error) {
result := make([]string, len(templs))
buf := new(bytes.Buffer)
d := b.getData(data...)
for i, templ := range templs { func (b *Builder) SpaceID() string {
t, err := b.Template(templ) return b.conf.SpaceID
if err != nil {
return nil, err
} }
buf.Reset() func (b *Builder) EnvironmentID() string {
err = t.Execute(buf, d) return b.conf.EnvironmentID
if err != nil {
return nil, err
} }
result[i] = buf.String() func (b *Builder) CollectionID() string {
return b.conf.CollectionID
} }
return result, nil // WithData возвращает копию Builder с новыми переданными данными.
func (b *Builder) WithData(data map[string]any) *Builder {
clone := *b
clone.data = data
return &clone
} }
func (b *Builder) ExecuteMap(templMap map[string]any, data ...any) (map[string]any, error) { func (b *Builder) WithKV(kv ...any) *Builder {
result := make(map[string]any, len(templMap)) clone := *b
d := b.getData(data...) if clone.data == nil {
//nolint:mnd // Количество аргументов делится на 2, так как они представлены в виде пар ключ-значение.
for k, v := range templMap { clone.data = make(map[string]any, len(kv)/2)
switch t := v.(type) {
case string:
value, err := b.Execute(t, d)
if err != nil {
return nil, err
} }
result[k] = value for i := 0; i < len(kv)-1; i += 2 {
case []string: k, _ := kv[i].(string)
var err error v := kv[i+1]
result[k], err = b.ExecuteList(t, d) if k != "" && v != nil {
if err != nil { clone.data[k] = v
return nil, err
} }
default:
result[k] = v
} }
return &clone
} }
return result, nil func (b *Builder) Execute(pattern string, data ...any) (string, error) {
if b.conf.DefaultHTML {
return b.HTMLTemplate().Execute(pattern, data...)
} }
return b.TextTemplate().Execute(pattern, data...)
func (b *Builder) getData(data ...any) any {
if len(data) == 0 {
return b.data
} }
res := maps.Clone(b.data) func (b *Builder) ExecuteList(patterns []string, data ...any) ([]string, error) {
if res == nil { if b.conf.DefaultHTML {
res = make(map[string]any) return b.HTMLTemplate().ExecuteList(patterns, data...)
}
for _, v := range data {
if m, ok := v.(map[string]any); ok {
maps.Copy(res, m)
} }
return b.TextTemplate().ExecuteList(patterns, data...)
} }
if len(res) == 0 { func (b *Builder) ExecuteMap(patternMap map[string]any, data ...any) (map[string]any, error) {
return data[0] if b.conf.DefaultHTML {
return b.HTMLTemplate().ExecuteMap(patternMap, data...)
} }
return b.TextTemplate().ExecuteMap(patternMap, data...)
return res
} }
package template package template
import ( import (
"context"
"testing" "testing"
"git.perx.ru/perxis/perxis-go/pkg/collections"
collectionsmocks "git.perx.ru/perxis/perxis-go/pkg/collections/mocks"
"git.perx.ru/perxis/perxis-go/pkg/content" "git.perx.ru/perxis/perxis-go/pkg/content"
"git.perx.ru/perxis/perxis-go/pkg/errors" "git.perx.ru/perxis/perxis-go/pkg/environments"
environmentsmocks "git.perx.ru/perxis/perxis-go/pkg/environments/mocks"
"git.perx.ru/perxis/perxis-go/pkg/items" "git.perx.ru/perxis/perxis-go/pkg/items"
mocksitems "git.perx.ru/perxis/perxis-go/pkg/items/mocks" itemsmocks "git.perx.ru/perxis/perxis-go/pkg/items/mocks"
"git.perx.ru/perxis/perxis-go/pkg/spaces"
spacesmocks "git.perx.ru/perxis/perxis-go/pkg/spaces/mocks"
"github.com/stretchr/testify/assert" "github.com/stretchr/testify/assert"
"github.com/stretchr/testify/mock"
"github.com/stretchr/testify/require"
) )
func TestBuilder_Execute(t *testing.T) { func TestBuilder(t *testing.T) {
tests := []struct { t.Run("using default HTML template", func(t *testing.T) {
name string builder := NewBuilder(&BuilderConfig{
SpaceID string DefaultHTML: true,
EnvID string }).WithData(map[string]any{
CollID string "foo": "<b>bar</b>",
template string })
data any
want any got, err := builder.Execute("{{ .foo }}")
htmlBuilder bool require.NoError(t, err)
wantErr bool assert.Equal(t, "&lt;b&gt;bar&lt;/b&gt;", got)
})
}
getCnt func(t *testing.T) *content.Content func TestBuilder_Funcs(t *testing.T) {
for _, tc := range []struct {
name string
pattern string
setupContent func(t *testing.T) *content.Content
spaceID string
environmentID string
collectionID string
want string
assertError assert.ErrorAssertionFunc
}{ }{
{ {
name: "invalid data", name: "basic lookup",
template: "hello {{ .a }}", pattern: "{{ lookup \"coll_id.item_id.field\" }}",
data: "world", setupContent: func(t *testing.T) *content.Content {
want: "", itemsService := itemsmocks.NewItems(t)
wantErr: true, itemsService.On("Get", mock.Anything, "space_id", "env_id", "coll_id", "item_id").
},
{
name: "invalid template",
template: "hello {{ . ",
data: "world",
want: "",
wantErr: true,
},
{
name: "empty template",
template: "",
data: "world",
want: "",
wantErr: false,
},
{
name: "empty data",
template: "{{ . }}",
data: "",
want: "",
wantErr: false,
},
{
name: "success",
template: "hello {{ . }}",
data: "world",
want: "hello world",
wantErr: false,
},
{
name: "success with html builder",
template: "<span class=\"comment\">{{ . }}</span>",
data: "<script>alert(localStorage.getItem('secret'))</script>",
want: "<span class=\"comment\">" +
"&lt;script&gt;alert(localStorage.getItem(&#39;secret&#39;))&lt;/script&gt;" +
"</span>",
htmlBuilder: true,
wantErr: false,
},
{
name: "template as plain text",
template: "hello",
data: "world",
want: "hello",
wantErr: false,
},
{
name: "lookup function",
SpaceID: "space",
EnvID: "env",
template: "hello, {{ lookup \"secrets.dev.key\" }}",
data: "",
want: "hello, Luk",
wantErr: false,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
Return(&items.Item{ Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]any{ Data: map[string]any{
"id": "dev", "field": "value",
"key": "Luk",
}, },
}, nil).Once() }, nil).
return &content.Content{Items: itemsSvc} Once()
return &content.Content{
Items: itemsService,
}
}, },
spaceID: "space_id",
environmentID: "env_id",
want: "value",
assertError: assert.NoError,
}, },
{ {
name: "lookup function with slice", name: "lookup with unknown field",
SpaceID: "space", pattern: "{{ lookup \"coll_id.item_id.unknown\" }}",
EnvID: "env", setupContent: func(t *testing.T) *content.Content {
template: "numbers {{ lookup \"secrets.dev.slice\" }}", itemsService := itemsmocks.NewItems(t)
data: "", itemsService.On("Get", mock.Anything, "space_id", "env_id", "coll_id", "item_id").
want: "numbers [1 2 3]",
wantErr: false,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
Return(&items.Item{ Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]any{ Data: map[string]any{
"id": "dev", "field": "value",
"slice": []int{1, 2, 3},
},
}, nil).Once()
return &content.Content{Items: itemsSvc}
},
}, },
{ }, nil).
name: "lookup function with unknown field", Once()
SpaceID: "space", return &content.Content{
EnvID: "env", Items: itemsService,
template: "numbers {{ lookup \"secrets.dev.slice\" }}", }
data: "",
want: "numbers <no value>",
wantErr: false,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").
Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]any{},
}, nil).Once()
return &content.Content{Items: itemsSvc}
},
},
{
name: "lookup function not found item",
SpaceID: "space",
EnvID: "env",
template: "hello {{ lookup \"secrets.prod.pass\" }}",
data: "",
want: "",
wantErr: true,
getCnt: func(t *testing.T) *content.Content {
itemsSvc := mocksitems.NewItems(t)
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "prod").
Return(nil, errors.New("not found")).Once()
return &content.Content{Items: itemsSvc}
}, },
spaceID: "space_id",
environmentID: "env_id",
want: "<no value>",
assertError: assert.NoError,
}, },
{ {
name: "system function", name: "invalid lookup",
SpaceID: "space", pattern: "{{ lookup \"coll_id.item_id\" }}",
template: "hello {{ system.SpaceID }}", setupContent: func(t *testing.T) *content.Content {
data: "", itemsService := itemsmocks.NewItems(t)
want: "hello space", return &content.Content{
wantErr: false, Items: itemsService,
},
} }
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var cnt *content.Content
if tt.getCnt != nil {
cnt = tt.getCnt(t)
}
var b *Builder
if tt.htmlBuilder {
b = NewHTMLBuilder(cnt, tt.SpaceID, tt.EnvID, tt.CollID)
} else {
b = NewBuilder(cnt, tt.SpaceID, tt.EnvID, tt.CollID)
}
got, err := b.Execute(tt.template, tt.data)
if tt.wantErr {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, got)
})
}
}
func TestBuilder_ExecuteList(t *testing.T) {
tests := []struct {
name string
templs []string
data any
want []string
htmlBuilder bool
assertError assert.ErrorAssertionFunc
getCnt func(t *testing.T) *content.Content
}{
{
name: "invalid data",
templs: []string{
"hello {{ .a }}",
}, },
data: "world", spaceID: "space_id",
want: nil, environmentID: "env_id",
want: "",
assertError: assert.Error, assertError: assert.Error,
}, },
{ {
name: "invalid template", name: "system space id",
templs: []string{ pattern: "{{ system.SpaceID }}",
"hello {{ . ", spaceID: "space_id",
}, want: "space_id",
data: "world", assertError: assert.NoError,
want: nil,
assertError: assert.Error,
}, },
{ {
name: "empty template", name: "field from system space",
templs: []string{ pattern: "{{ system.Space.Name }}",
"", setupContent: func(t *testing.T) *content.Content {
}, spacesService := spacesmocks.NewSpaces(t)
data: "world", spacesService.On("Get", mock.Anything, "space_id").
want: []string{ Return(&spaces.Space{
"", Name: "name",
}, nil).
Once()
return &content.Content{
Spaces: spacesService,
}
}, },
spaceID: "space_id",
environmentID: "env_id",
want: "name",
assertError: assert.NoError, assertError: assert.NoError,
}, },
{ {
name: "empty data", name: "system environment id",
templs: []string{ pattern: "{{ system.EnvID }}",
"{{ . }}", environmentID: "env_id",
}, want: "env_id",
data: "",
want: []string{
"",
},
assertError: assert.NoError, assertError: assert.NoError,
}, },
{ {
name: "success", name: "field from system environment",
templs: []string{ pattern: "{{ system.Environment.ID }}",
"hello {{ . }}", setupContent: func(t *testing.T) *content.Content {
"world {{ . }}", environmentsService := environmentsmocks.NewEnvironments(t)
}, environmentsService.On("Get", mock.Anything, "space_id", "env_id").
data: "world", Return(&environments.Environment{
want: []string{ ID: "env_id",
"hello world", }, nil).
"world world", Once()
return &content.Content{
Environments: environmentsService,
}
}, },
spaceID: "space_id",
environmentID: "env_id",
want: "env_id",
assertError: assert.NoError, assertError: assert.NoError,
}, },
{ {
name: "success with html builder", name: "system collection id",
templs: []string{ pattern: "{{ system.CollectionID }}",
"<span class=\"comment\">{{ . }}</span>", collectionID: "coll_id",
}, want: "coll_id",
data: "<script>alert(localStorage.getItem('secret'))</script>",
want: []string{
"<span class=\"comment\">" +
"&lt;script&gt;alert(localStorage.getItem(&#39;secret&#39;))&lt;/script&gt;" +
"</span>",
},
htmlBuilder: true,
assertError: assert.NoError, assertError: assert.NoError,
}, },
{ {
name: "template as plain text", name: "field from system collection",
templs: []string{ pattern: "{{ system.Collection.ID }}",
"hello", setupContent: func(t *testing.T) *content.Content {
}, collectionsService := collectionsmocks.NewCollections(t)
data: "world", collectionsService.On("Get", mock.Anything, "space_id", "env_id", "coll_id").
want: []string{ Return(&collections.Collection{
"hello", ID: "coll_id",
}, nil).
Once()
return &content.Content{
Collections: collectionsService,
}
}, },
spaceID: "space_id",
environmentID: "env_id",
collectionID: "coll_id",
want: "coll_id",
assertError: assert.NoError, assertError: assert.NoError,
}, },
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
var cnt *content.Content
if tt.getCnt != nil {
cnt = tt.getCnt(t)
}
var b *Builder
if tt.htmlBuilder {
b = NewHTMLBuilder(cnt, "space_id", "env_id", "coll_id")
} else {
b = NewBuilder(cnt, "space_id", "env_id", "coll_id")
}
got, err := b.ExecuteList(tt.templs, tt.data)
tt.assertError(t, err)
assert.Equal(t, tt.want, got)
})
}
}
func TestBuilder_ExecuteMap(t *testing.T) {
tests := []struct {
name string
SpaceID string
EnvID string
templateMap map[string]any
data any
want map[string]any
wantErr bool
itemsCall func(itemsSvc *mocksitems.Items)
} { } {
{ t.Run(tc.name, func(t *testing.T) {
name: "invalid template", conf := &BuilderConfig{
SpaceID: "space", SpaceID: tc.spaceID,
EnvID: "env", EnvironmentID: tc.environmentID,
templateMap: map[string]any{"hello": "{{ . }"}, CollectionID: tc.collectionID,
data: "world", }
want: nil, if tc.setupContent != nil {
wantErr: true, conf.Content = tc.setupContent(t)
}, }
{ builder := NewBuilder(conf)
name: "empty data", got, err := builder.TextTemplate().Execute(tc.pattern)
SpaceID: "space", tc.assertError(t, err)
EnvID: "env", assert.Equal(t, tc.want, got)
templateMap: map[string]any{},
data: "",
want: map[string]any{},
wantErr: false,
},
{
name: "one template",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"hello": "{{ . }}",
},
data: "world",
want: map[string]any{
"hello": "world",
},
wantErr: false,
},
{
name: "one template with many value templates",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"hello": []string{
"{{ .foo }}",
"{{ .bar }}",
},
},
data: map[string]any{
"foo": "hello",
"bar": "world",
},
want: map[string]any{
"hello": []string{
"hello",
"world",
},
},
wantErr: false,
},
{
name: "many template",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"hello": "{{ . }}",
"go": "{{ . }}",
},
data: "world",
want: map[string]any{
"hello": "world",
"go": "world",
},
wantErr: false,
},
{
name: "template in key with one value",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"{{ .key }}": "value",
},
data: map[string]any{
"key": "foo",
},
want: map[string]any{
"{{ .key }}": "value",
},
wantErr: false,
},
{
name: "template in key with many values",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"{{ .key }}": []string{
"value",
},
},
data: map[string]any{
"key": "foo",
},
want: map[string]any{
"{{ .key }}": []string{
"value",
},
},
wantErr: false,
},
{
name: "template as plain text",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"a": "b",
},
data: "world",
want: map[string]any{
"a": "b",
},
wantErr: false,
},
{
name: "template is not template",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"a": "b",
"b": []int{1, 2, 3},
},
data: "world",
want: map[string]any{
"a": "b",
"b": []int{1, 2, 3},
},
wantErr: false,
},
{
name: "lookup function",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"hello": "{{ lookup \"secrets.dev.key\" }}",
},
want: map[string]any{
"hello": "1234",
},
wantErr: false,
itemsCall: func(itemsSvc *mocksitems.Items) {
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]any{
"id": "dev",
"key": "1234",
},
}, nil).Once()
},
},
{
name: "lookup function with unknown field",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{"hello": "{{ lookup \"secrets.dev.incorrect\" }}"},
want: map[string]any{"hello": "<no value>"},
wantErr: false,
itemsCall: func(itemsSvc *mocksitems.Items) {
itemsSvc.On("Get", context.Background(), "space", "env", "secrets", "dev").Return(&items.Item{
ID: "dev",
SpaceID: "space",
EnvID: "env",
CollectionID: "secrets",
Data: map[string]any{
"id": "dev",
"key": "1234",
},
}, nil).Once()
},
},
{
name: "system function",
SpaceID: "space",
EnvID: "env",
templateMap: map[string]any{
"hello": "{{ system.SpaceID }}",
},
want: map[string]any{
"hello": "space",
},
wantErr: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
itemsSvc := &mocksitems.Items{}
if tt.itemsCall != nil {
tt.itemsCall(itemsSvc)
}
b := NewBuilder(&content.Content{Items: itemsSvc}, tt.SpaceID, tt.EnvID, "")
got, err := b.ExecuteMap(tt.templateMap, tt.data)
if tt.wantErr == true {
assert.Error(t, err)
} else {
assert.NoError(t, err)
}
assert.Equal(t, tt.want, got)
itemsSvc.AssertExpectations(t)
}) })
} }
} }
func TestBuilder_getData(t *testing.T) {
t.Run("empty data", func(t *testing.T) {
builder := NewBuilder(nil, "space_id", "env_id", "coll_id")
assert.Equal(t,
map[string]any(nil),
builder.getData(),
)
})
t.Run("with data", func(t *testing.T) {
builder := NewBuilder(nil, "space_id", "env_id", "coll_id").
WithData(map[string]any{
"foo": "bar",
})
assert.Equal(t,
map[string]any{
"foo": "bar",
},
builder.getData(),
)
})
t.Run("merge data", func(t *testing.T) {
builder := NewBuilder(nil, "space_id", "env_id", "coll_id")
assert.Equal(t,
map[string]any{
"a": 1,
"b": 2,
},
builder.getData(
map[string]any{},
map[string]any{"a": 1},
map[string]any{"b": 2},
[]string{"a", "b"},
"string",
),
)
})
t.Run("merge with original data", func(t *testing.T) {
builder := NewBuilder(nil, "space_id", "env_id", "coll_id").
WithData(map[string]any{
"foo": "bar",
"baz": "qux",
})
assert.Equal(t,
map[string]any{
"a": 1,
"b": 2,
"foo": "bar",
"baz": "qux",
},
builder.getData(
map[string]any{"a": 1},
map[string]any{"b": 2},
),
)
})
}
...@@ -19,7 +19,7 @@ func getLookup(b *Builder) func(string) (any, error) { ...@@ -19,7 +19,7 @@ func getLookup(b *Builder) func(string) (any, error) {
collectionID := parsedName[0] collectionID := parsedName[0]
itemID := parsedName[1] itemID := parsedName[1]
field := parsedName[2] field := parsedName[2]
item, err := b.cnt.Items.Get(b.Context(), b.SpaceID, b.EnvID, collectionID, itemID) item, err := b.Content().Items.Get(b.Context(), b.SpaceID(), b.EnvironmentID(), collectionID, itemID)
if err != nil { if err != nil {
return nil, errors.Wrapf(err, "failed to get \"%s\"") return nil, errors.Wrapf(err, "failed to get \"%s\"")
} }
......
...@@ -85,7 +85,12 @@ func Test_getLookup(t *testing.T) { ...@@ -85,7 +85,12 @@ func Test_getLookup(t *testing.T) {
if tc.setup != nil { if tc.setup != nil {
tc.setup(svc) tc.setup(svc)
} }
b := NewBuilder(&content.Content{Items: svc}, "space_id", "env_id", "coll_id") b := NewBuilder(&BuilderConfig{
Content: &content.Content{Items: svc},
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
lookup := getLookup(b) lookup := getLookup(b)
got, err := lookup(tc.input) got, err := lookup(tc.input)
...@@ -100,11 +105,16 @@ func Test_getSystem(t *testing.T) { ...@@ -100,11 +105,16 @@ func Test_getSystem(t *testing.T) {
spacesSvc := mocksspaces.NewSpaces(t) spacesSvc := mocksspaces.NewSpaces(t)
envsSvc := mocksenvs.NewEnvironments(t) envsSvc := mocksenvs.NewEnvironments(t)
collsSvc := mockscolls.NewCollections(t) collsSvc := mockscolls.NewCollections(t)
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
assert.Equal(t, "space_id", system().SpaceID()) assert.Equal(t, "space_id", system().SpaceID())
}) })
...@@ -112,11 +122,16 @@ func Test_getSystem(t *testing.T) { ...@@ -112,11 +122,16 @@ func Test_getSystem(t *testing.T) {
spacesSvc := mocksspaces.NewSpaces(t) spacesSvc := mocksspaces.NewSpaces(t)
envsSvc := mocksenvs.NewEnvironments(t) envsSvc := mocksenvs.NewEnvironments(t)
collsSvc := mockscolls.NewCollections(t) collsSvc := mockscolls.NewCollections(t)
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
assert.Equal(t, "env_id", system().EnvID()) assert.Equal(t, "env_id", system().EnvID())
}) })
...@@ -124,11 +139,16 @@ func Test_getSystem(t *testing.T) { ...@@ -124,11 +139,16 @@ func Test_getSystem(t *testing.T) {
spacesSvc := mocksspaces.NewSpaces(t) spacesSvc := mocksspaces.NewSpaces(t)
envsSvc := mocksenvs.NewEnvironments(t) envsSvc := mocksenvs.NewEnvironments(t)
collsSvc := mockscolls.NewCollections(t) collsSvc := mockscolls.NewCollections(t)
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
assert.Equal(t, "coll_id", system().CollectionID()) assert.Equal(t, "coll_id", system().CollectionID())
}) })
...@@ -144,11 +164,16 @@ func Test_getSystem(t *testing.T) { ...@@ -144,11 +164,16 @@ func Test_getSystem(t *testing.T) {
Once() Once()
envsSvc := mocksenvs.NewEnvironments(t) envsSvc := mocksenvs.NewEnvironments(t)
collsSvc := mockscolls.NewCollections(t) collsSvc := mockscolls.NewCollections(t)
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
got, err := system().Space() got, err := system().Space()
require.NoError(t, err) require.NoError(t, err)
...@@ -165,11 +190,16 @@ func Test_getSystem(t *testing.T) { ...@@ -165,11 +190,16 @@ func Test_getSystem(t *testing.T) {
Once() Once()
envsSvc := mocksenvs.NewEnvironments(t) envsSvc := mocksenvs.NewEnvironments(t)
collsSvc := mockscolls.NewCollections(t) collsSvc := mockscolls.NewCollections(t)
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
got, err := system().Space() got, err := system().Space()
require.Error(t, err) require.Error(t, err)
...@@ -187,11 +217,16 @@ func Test_getSystem(t *testing.T) { ...@@ -187,11 +217,16 @@ func Test_getSystem(t *testing.T) {
}, nil). }, nil).
Once() Once()
collsSvc := mockscolls.NewCollections(t) collsSvc := mockscolls.NewCollections(t)
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
got, err := system().Environment() got, err := system().Environment()
require.NoError(t, err) require.NoError(t, err)
...@@ -207,11 +242,16 @@ func Test_getSystem(t *testing.T) { ...@@ -207,11 +242,16 @@ func Test_getSystem(t *testing.T) {
Return(nil, errors.New("some error")). Return(nil, errors.New("some error")).
Once() Once()
collsSvc := mockscolls.NewCollections(t) collsSvc := mockscolls.NewCollections(t)
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
got, err := system().Environment() got, err := system().Environment()
require.Error(t, err) require.Error(t, err)
...@@ -231,11 +271,16 @@ func Test_getSystem(t *testing.T) { ...@@ -231,11 +271,16 @@ func Test_getSystem(t *testing.T) {
Name: "Collection", Name: "Collection",
}, nil). }, nil).
Once() Once()
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
got, err := system().Collection() got, err := system().Collection()
require.NoError(t, err) require.NoError(t, err)
...@@ -253,11 +298,16 @@ func Test_getSystem(t *testing.T) { ...@@ -253,11 +298,16 @@ func Test_getSystem(t *testing.T) {
collsSvc.On("Get", mock.Anything, "space_id", "env_id", "coll_id"). collsSvc.On("Get", mock.Anything, "space_id", "env_id", "coll_id").
Return(nil, errors.New("some error")). Return(nil, errors.New("some error")).
Once() Once()
b := NewBuilder(&content.Content{ b := NewBuilder(&BuilderConfig{
Content: &content.Content{
Spaces: spacesSvc, Spaces: spacesSvc,
Environments: envsSvc, Environments: envsSvc,
Collections: collsSvc, Collections: collsSvc,
}, "space_id", "env_id", "coll_id") },
SpaceID: "space_id",
EnvironmentID: "env_id",
CollectionID: "coll_id",
})
system := getSystem(b) system := getSystem(b)
got, err := system().Collection() got, err := system().Collection()
require.Error(t, err) require.Error(t, err)
......
...@@ -10,44 +10,45 @@ type System struct { ...@@ -10,44 +10,45 @@ type System struct {
builder *Builder builder *Builder
} }
func (s *System) SpaceID() string {
return s.builder.SpaceID
}
func (s *System) EnvID() string {
return s.builder.EnvID
}
func (s *System) CollectionID() string {
return s.builder.CollID
}
func (s *System) Space() (*spaces.Space, error) { func (s *System) Space() (*spaces.Space, error) {
if s.builder.space != nil { if s.builder.space != nil {
return s.builder.space, nil return s.builder.space, nil
} }
space, err := s.builder.cnt.Spaces.Get(s.builder.ctx, s.builder.SpaceID) space, err := s.builder.Content().Spaces.Get(s.builder.Context(), s.builder.SpaceID())
s.builder.space = space s.builder.space = space
return s.builder.space, err return s.builder.space, err
} }
func (s *System) SpaceID() string {
return s.builder.SpaceID()
}
func (s *System) Environment() (*environments.Environment, error) { func (s *System) Environment() (*environments.Environment, error) {
if s.builder.environment != nil { if s.builder.environment != nil {
return s.builder.environment, nil return s.builder.environment, nil
} }
env, err := s.builder.cnt.Environments.Get(s.builder.ctx, s.builder.SpaceID, s.builder.EnvID) env, err := s.builder.Content().Environments.Get(s.builder.ctx, s.builder.SpaceID(), s.builder.EnvironmentID())
s.builder.environment = env s.builder.environment = env
return s.builder.environment, err return s.builder.environment, err
} }
func (s *System) EnvID() string {
return s.builder.EnvironmentID()
}
func (s *System) Collection() (*collections.Collection, error) { func (s *System) Collection() (*collections.Collection, error) {
if s.builder.collection != nil { if s.builder.collection != nil {
return s.builder.collection, nil return s.builder.collection, nil
} }
coll, err := s.builder.cnt.Collections.Get(s.builder.ctx, s.builder.SpaceID, s.builder.EnvID, s.builder.CollID) coll, err := s.builder.Content().Collections.Get(s.builder.ctx, s.builder.SpaceID(),
s.builder.EnvironmentID(), s.builder.CollectionID())
s.builder.collection = coll s.builder.collection = coll
return s.builder.collection, err return s.builder.collection, err
} }
func (s *System) CollectionID() string {
return s.builder.CollectionID()
}
package template
import (
"bytes"
html "html/template"
"io"
"maps"
text "text/template"
)
func NewCommonTemplate(html bool, data map[string]any, funcMap map[string]any) *CommonTemplate {
return &CommonTemplate{
html: html,
data: data,
funcMap: funcMap,
}
}
type CommonTemplate struct {
html bool
data map[string]any
funcMap map[string]any
}
func (t *CommonTemplate) Execute(pattern string, data ...any) (string, error) {
return t.execute(pattern, t.getData(data...))
}
func (t *CommonTemplate) ExecuteList(patterns []string, data ...any) ([]string, error) {
return t.executeList(patterns, t.getData(data...))
}
func (t *CommonTemplate) ExecuteMap(patternMap map[string]any, data ...any) (map[string]any, error) {
result := make(map[string]any, len(patternMap))
d := t.getData(data...)
for key, value := range patternMap {
var err error
switch v := value.(type) {
case string:
result[key], err = t.execute(v, d)
if err != nil {
return nil, err
}
case []string:
result[key], err = t.executeList(v, d)
if err != nil {
return nil, err
}
default:
result[key] = value
}
}
return result, nil
}
func (t *CommonTemplate) execute(pattern string, data any) (string, error) {
var (
exec interface {
Execute(w io.Writer, data any) error
}
err error
)
if t.html {
exec, err = html.New("main").Funcs(t.funcMap).Parse(pattern)
} else {
exec, err = text.New("main").Funcs(t.funcMap).Parse(pattern)
}
if err != nil {
return "", err
}
var buf bytes.Buffer
err = exec.Execute(&buf, data)
if err != nil {
return "", err
}
return buf.String(), nil
}
func (t *CommonTemplate) executeList(patterns []string, data any) ([]string, error) {
result := make([]string, len(patterns))
var err error
for i, pattern := range patterns {
result[i], err = t.execute(pattern, data)
if err != nil {
return nil, err
}
}
return result, nil
}
func (t *CommonTemplate) getData(data ...any) any {
if len(data) == 0 {
return t.data
}
m, ok := data[0].(map[string]any)
if !ok {
return data[0]
}
result := maps.Clone(t.data)
if result == nil {
result = make(map[string]any)
}
maps.Copy(result, m)
return result
}
package template
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestCommonTemplate_Execute(t *testing.T) {
for _, tc := range []struct {
name string
pattern string
data map[string]any
additionalData []any
funcMap map[string]any
wantText string
wantHTML string
assertError assert.ErrorAssertionFunc
}{
{
name: "basic",
pattern: "{{ .foo }}",
data: map[string]any{
"foo": "bar",
},
wantText: "bar",
wantHTML: "bar",
assertError: assert.NoError,
},
{
name: "additional data",
pattern: "{{ . }}",
additionalData: []any{
"bar",
},
wantText: "bar",
wantHTML: "bar",
assertError: assert.NoError,
},
{
name: "map in additional data",
pattern: "{{ .foo }}",
additionalData: []any{
map[string]any{
"foo": "bar",
},
},
wantText: "bar",
wantHTML: "bar",
assertError: assert.NoError,
},
{
name: "html in additional data",
pattern: "{{ . }}",
additionalData: []any{
"<a href=\"https://example.com/\">Link</a>",
},
wantText: "<a href=\"https://example.com/\">Link</a>",
wantHTML: "&lt;a href=&#34;https://example.com/&#34;&gt;Link&lt;/a&gt;",
assertError: assert.NoError,
},
{
name: "empty pattern",
pattern: "",
wantText: "",
wantHTML: "",
assertError: assert.NoError,
},
{
name: "plain text",
pattern: "text",
wantText: "text",
wantHTML: "text",
assertError: assert.NoError,
},
{
name: "func map",
pattern: "{{ foo }}",
funcMap: map[string]any{
"foo": func() string {
return "bar"
},
},
wantText: "bar",
wantHTML: "bar",
assertError: assert.NoError,
},
{
name: "data and additional data",
pattern: "{{ .foo }},{{ .bar }}",
data: map[string]any{
"foo": "foo",
},
additionalData: []any{
map[string]any{
"bar": "bar",
},
},
wantText: "foo,bar",
wantHTML: "foo,bar",
assertError: assert.NoError,
},
{
name: "invalid pattern",
pattern: "{{ .",
wantText: "",
wantHTML: "",
assertError: assert.Error,
},
{
name: "no value",
pattern: "{{ .foo }}",
wantText: "<no value>",
wantHTML: "",
assertError: assert.NoError,
},
} {
t.Run(tc.name, func(t *testing.T) {
for _, enc := range []struct {
name string
template *CommonTemplate
want string
}{
{
name: "text",
template: NewCommonTemplate(false, tc.data, tc.funcMap),
want: tc.wantText,
},
{
name: "html",
template: NewCommonTemplate(true, tc.data, tc.funcMap),
want: tc.wantHTML,
},
} {
t.Run(enc.name, func(t *testing.T) {
got, err := enc.template.Execute(tc.pattern, tc.additionalData...)
tc.assertError(t, err)
assert.Equal(t, enc.want, got)
})
}
})
}
}
func TestCommonTemplate_ExecuteList(t *testing.T) {
for _, tc := range []struct {
name string
patterns []string
data map[string]any
additionalData []any
funcMap map[string]any
wantText []string
wantHTML []string
assertError assert.ErrorAssertionFunc
}{
{
name: "basic",
patterns: []string{"{{ .foo }}", "{{ .bar }}"},
data: map[string]any{
"foo": "hello",
"bar": "world",
},
wantText: []string{"hello", "world"},
wantHTML: []string{"hello", "world"},
assertError: assert.NoError,
},
{
name: "additional data",
patterns: []string{"{{ . }}"},
additionalData: []any{
"test",
},
wantText: []string{"test"},
wantHTML: []string{"test"},
assertError: assert.NoError,
},
{
name: "map in additional data",
patterns: []string{"{{ .foo }}", "{{ .bar }}"},
additionalData: []any{
map[string]any{
"foo": "value1",
"bar": "value2",
},
},
wantText: []string{"value1", "value2"},
wantHTML: []string{"value1", "value2"},
assertError: assert.NoError,
},
{
name: "html escaping",
patterns: []string{"{{ . }}"},
additionalData: []any{
"<script>alert('secret')</script>",
},
wantText: []string{"<script>alert('secret')</script>"},
wantHTML: []string{"&lt;script&gt;alert(&#39;secret&#39;)&lt;/script&gt;"},
assertError: assert.NoError,
},
{
name: "empty list",
patterns: []string{},
wantText: []string{},
wantHTML: []string{},
assertError: assert.NoError,
},
{
name: "single plain text",
patterns: []string{"just text"},
wantText: []string{"just text"},
wantHTML: []string{"just text"},
assertError: assert.NoError,
},
{
name: "func map",
patterns: []string{"{{ greet }}", "{{ 42 | add }}"},
funcMap: map[string]any{
"greet": func() string { return "hello" },
"add": func(i int) int { return i + 1 },
},
wantText: []string{"hello", "43"},
wantHTML: []string{"hello", "43"},
assertError: assert.NoError,
},
{
name: "data merging",
patterns: []string{"{{ .foo }}", "{{ .bar }}"},
data: map[string]any{
"foo": "base",
},
additionalData: []any{
map[string]any{
"bar": "extra",
},
},
wantText: []string{"base", "extra"},
wantHTML: []string{"base", "extra"},
assertError: assert.NoError,
},
{
name: "invalid pattern",
patterns: []string{"valid", "{{ .invalid "},
wantText: []string(nil),
wantHTML: []string(nil),
assertError: assert.Error,
},
{
name: "unknown value",
patterns: []string{"{{ .unknown }}"},
wantText: []string{"<no value>"},
wantHTML: []string{""},
assertError: assert.NoError,
},
} {
t.Run(tc.name, func(t *testing.T) {
for _, enc := range []struct {
name string
template *CommonTemplate
want []string
}{
{
name: "text",
template: NewCommonTemplate(false, tc.data, tc.funcMap),
want: tc.wantText,
},
{
name: "html",
template: NewCommonTemplate(true, tc.data, tc.funcMap),
want: tc.wantHTML,
},
} {
t.Run(enc.name, func(t *testing.T) {
got, err := enc.template.ExecuteList(tc.patterns, tc.additionalData...)
tc.assertError(t, err)
assert.Equal(t, enc.want, got)
})
}
})
}
}
func TestCommonTemplate_ExecuteMap(t *testing.T) {
for _, tc := range []struct {
name string
patternMap map[string]any
data map[string]any
additionalData []any
funcMap map[string]any
wantText map[string]any
wantHTML map[string]any
assertError assert.ErrorAssertionFunc
}{
{
name: "mixed types",
patternMap: map[string]any{
"key1": "{{ .foo }}",
"key2": []string{"{{ .bar }}", "static"},
"key3": 42,
},
data: map[string]any{
"foo": "value1",
"bar": "value2",
},
wantText: map[string]any{
"key1": "value1",
"key2": []string{"value2", "static"},
"key3": 42,
},
wantHTML: map[string]any{
"key1": "value1",
"key2": []string{"value2", "static"},
"key3": 42,
},
assertError: assert.NoError,
},
{
name: "html escaping",
patternMap: map[string]any{
"html": "{{ .text }}",
},
data: map[string]any{
"text": "<b>content</b>",
},
wantText: map[string]any{
"html": "<b>content</b>",
},
wantHTML: map[string]any{
"html": "&lt;b&gt;content&lt;/b&gt;",
},
assertError: assert.NoError,
},
{
name: "func map",
patternMap: map[string]any{
"func": "{{ add 1 2 }}",
"slice": []string{"{{ greet }}"},
},
funcMap: map[string]any{
"add": func(a, b int) int { return a + b },
"greet": func() string { return "hello" },
},
wantText: map[string]any{
"func": "3",
"slice": []string{"hello"},
},
wantHTML: map[string]any{
"func": "3",
"slice": []string{"hello"},
},
assertError: assert.NoError,
},
{
name: "data merging",
patternMap: map[string]any{
"merged": "{{ .base }}, {{ .extra }}",
},
data: map[string]any{
"base": "foo",
},
additionalData: []any{
map[string]any{
"extra": "bar",
},
},
wantText: map[string]any{
"merged": "foo, bar",
},
wantHTML: map[string]any{
"merged": "foo, bar",
},
assertError: assert.NoError,
},
{
name: "invalid pattern",
patternMap: map[string]any{
"ok": "valid",
"error": "{{ .invalid",
},
wantText: map[string]any(nil),
wantHTML: map[string]any(nil),
assertError: assert.Error,
},
{
name: "missing value",
patternMap: map[string]any{
"missing": "{{ .unknown }}",
},
wantText: map[string]any{
"missing": "<no value>",
},
wantHTML: map[string]any{
"missing": "",
},
assertError: assert.NoError,
},
{
name: "nested slices",
patternMap: map[string]any{
"slice": []string{
"{{ .item1 }}",
"{{ .item2 }}",
"plain text",
},
},
additionalData: []any{
map[string]any{
"item1": "value1",
"item2": "value2",
},
},
wantText: map[string]any{
"slice": []string{"value1", "value2", "plain text"},
},
wantHTML: map[string]any{
"slice": []string{"value1", "value2", "plain text"},
},
assertError: assert.NoError,
},
{
name: "empty map",
patternMap: map[string]any{},
wantText: map[string]any{},
wantHTML: map[string]any{},
assertError: assert.NoError,
},
} {
t.Run(tc.name, func(t *testing.T) {
for _, enc := range []struct {
name string
template *CommonTemplate
want map[string]any
}{
{
name: "text",
template: NewCommonTemplate(false, tc.data, tc.funcMap),
want: tc.wantText,
},
{
name: "html",
template: NewCommonTemplate(true, tc.data, tc.funcMap),
want: tc.wantHTML,
},
} {
t.Run(enc.name, func(t *testing.T) {
got, err := enc.template.ExecuteMap(tc.patternMap, tc.additionalData...)
tc.assertError(t, err)
assert.Equal(t, enc.want, got)
})
}
})
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment