Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 10 additions & 9 deletions pkg/genlib/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,16 @@ type Config struct {
}

type ConfigField struct {
Name string `config:"name"`
Fuzziness float64 `config:"fuzziness"`
Range Range `config:"range"`
Cardinality int `config:"cardinality"`
Period time.Duration `config:"period"`
Enum []string `config:"enum"`
ObjectKeys []string `config:"object_keys"`
Value any `config:"value"`
Counter bool `config:"counter"`
Name string `config:"name"`
Fuzziness float64 `config:"fuzziness"`
Range Range `config:"range"`
Cardinality int `config:"cardinality"`
Period time.Duration `config:"period"`
Enum []string `config:"enum"`
ObjectKeys []string `config:"object_keys"`
Value any `config:"value"`
Counter bool `config:"counter"`
FormattingPattern string `config:"formatting_pattern"`
}

func (cf ConfigField) ValidForDateField() error {
Expand Down
45 changes: 45 additions & 0 deletions pkg/genlib/generator_interface.go
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,40 @@ func newGenState() *genState {
}
}

type patternFn func() string

var patternGenerators = func() map[string]patternFn {
return map[string]patternFn{
"string": func() string {
return randomdata.Noun()
},
"ipv4": func() string {
return randomdata.IpV4Address()
},
"ipv6": func() string {
return randomdata.IpV6Address()
},
"port": func() string {
return fmt.Sprintf("%d", randomdata.Number(1024, 65535))
},
}
}

func replacePattern(pattern string) string {
// Regular expression to find {generator} patterns
re := regexp.MustCompile(`\{(.*?)}`)
matches := re.FindAllStringSubmatch(pattern, -1)

for _, match := range matches {
key := match[1]
if generator, exists := patternGenerators()[key]; exists {
pattern = strings.Replace(pattern, match[0], generator(), 1)
}
}

return pattern
}

func bindField(cfg Config, field Field, fieldMap map[string]any, withReturn bool) error {

// Check for hardcoded field value
Expand Down Expand Up @@ -844,6 +878,17 @@ func bindConstantKeywordWithReturn(field Field, fieldMap map[string]any) error {
}

func bindKeywordWithReturn(fieldCfg ConfigField, field Field, fieldMap map[string]any) error {
if fieldCfg.FormattingPattern != "" {
var emitF emitF
emitF = func(state *genState) any {
res := replacePattern(fieldCfg.FormattingPattern)
return res
}

fieldMap[field.Name] = emitF
return nil
}

if len(fieldCfg.Enum) > 0 {
var emitF emitF
emitF = func(state *genState) any {
Expand Down
99 changes: 99 additions & 0 deletions pkg/genlib/generator_with_text_template_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import (
"fmt"
"math/rand"
"net"
"regexp"
"strconv"
"strings"
"testing"
Expand Down Expand Up @@ -755,6 +756,104 @@ func Test_FieldIPWithTextTemplate(t *testing.T) {
}
}

func Test_FieldKeywordFormattingPatternPathWithTextTemplate(t *testing.T) {
fld := Field{
Name: "path",
Type: FieldTypeKeyword,
}

template := []byte(`{{$path := generate "path"}}{"path":"{{$path}}"}`)
configYaml := []byte(`fields:
- name: path
cardinality: 25
formatting_pattern: "/home/{string}/{string}/{string}"`)
t.Logf("with template: %s", string(template))

cfg, err := config.LoadConfigFromYaml(configYaml)
if err != nil {
t.Fatal(err)
}

g := makeGeneratorWithTextTemplate(t, cfg, []Field{fld}, template, 10)

var buf bytes.Buffer

pathRegex := regexp.MustCompile(`^/home/[^/]+/[^/]+/[^/]+$`)

nSpins := int64(10)

for i := int64(0); i < nSpins; i++ {
if err := g.Emit(&buf); err != nil {
t.Fatal(err)
}

m := unmarshalJSONT[string](t, buf.Bytes())
buf.Reset()

if len(m) != 1 {
t.Errorf("Expected map size 1, got %d", len(m))
}

v, ok := m[fld.Name]
if !ok {
t.Errorf("Missing key %v", fld.Name)
}

if !pathRegex.MatchString(v) {
t.Errorf("Generated path %v does not match expected format", v)
}
}
}

func Test_FieldKeywordFormattingPatternHostIPWithTextTemplate(t *testing.T) {
fld := Field{
Name: "hostIP",
Type: FieldTypeKeyword,
}

template := []byte(`{{$hostIP := generate "hostIP"}}{"hostIP":"{{$hostIP}}"}`)
configYaml := []byte(`fields:
- name: hostIP
cardinality: 25
formatting_pattern: "{ipv4}:{port}"`)
t.Logf("with template: %s", string(template))

cfg, err := config.LoadConfigFromYaml(configYaml)
if err != nil {
t.Fatal(err)
}

g := makeGeneratorWithTextTemplate(t, cfg, []Field{fld}, template, 10)

var buf bytes.Buffer

ipv4PortRegex := regexp.MustCompile(`^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}:\d{1,5}$`)

nSpins := int64(10)

for i := int64(0); i < nSpins; i++ {
if err := g.Emit(&buf); err != nil {
t.Fatal(err)
}

m := unmarshalJSONT[string](t, buf.Bytes())
buf.Reset()

if len(m) != 1 {
t.Errorf("Expected map size 1, got %d", len(m))
}

v, ok := m[fld.Name]
if !ok {
t.Errorf("Missing key %v", fld.Name)
}

if !ipv4PortRegex.MatchString(v) {
t.Errorf("Generated pattern %v does not match expected format", v)
}
}
}

func Test_FieldFloatsWithTextTemplate(t *testing.T) {
_testNumericWithTextTemplate[float64](t, FieldTypeDouble)
_testNumericWithTextTemplate[float32](t, FieldTypeFloat)
Expand Down