add tests in pkg/csconfig and improve pkg/exprhelpers tests (#150)

* add tests for csconfig & improve exprhelpers tests
This commit is contained in:
AlteredCoder 2020-07-28 15:38:48 +02:00 committed by GitHub
parent e6cb7f3a79
commit 794d3221d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
13 changed files with 752 additions and 144 deletions

View file

@ -37,7 +37,7 @@ func initConfig() {
}
csConfig := csconfig.NewCrowdSecConfig()
if err := csConfig.GetCliConfig(&config.ConfigFilePath); err != nil {
if err := csConfig.LoadConfigurationFile(&config.ConfigFilePath); err != nil {
log.Fatalf(err.Error())
}
config.configFolder = filepath.Clean(csConfig.CsCliFolder)

View file

@ -244,7 +244,7 @@ func main() {
cConfig = csconfig.NewCrowdSecConfig()
// Handle command line arguments
if err := cConfig.GetOPT(); err != nil {
if err := cConfig.LoadConfig(); err != nil {
log.Fatalf(err.Error())
}
// Configure logging

View file

@ -81,7 +81,7 @@ func (c *CrowdSec) LoadSimulation() error {
return nil
}
func (c *CrowdSec) GetCliConfig(configFile *string) error {
func (c *CrowdSec) LoadConfigurationFile(configFile *string) error {
/*overriden by cfg file*/
if *configFile != "" {
rcfg, err := ioutil.ReadFile(*configFile)
@ -101,9 +101,8 @@ func (c *CrowdSec) GetCliConfig(configFile *string) error {
return nil
}
// GetOPT return flags parsed from command line
func (c *CrowdSec) GetOPT() error {
// LoadConfig return configuration parsed from command line and configuration file
func (c *CrowdSec) LoadConfig() error {
AcquisitionFile := flag.String("acquis", "", "path to acquis.yaml")
configFile := flag.String("c", "", "configuration file")
printTrace := flag.Bool("trace", false, "VERY verbose")
@ -129,14 +128,14 @@ func (c *CrowdSec) GetOPT() error {
if *catFile != "" {
if *catFileType == "" {
log.Fatalf("-file requires -type")
return fmt.Errorf("-file requires -type")
}
c.SingleFile = *catFile
c.SingleFileLabel = *catFileType
}
if err := c.GetCliConfig(configFile); err != nil {
log.Fatalf("Error while loading configuration : %s", err)
if err := c.LoadConfigurationFile(configFile); err != nil {
return fmt.Errorf("Error while loading configuration : %s", err)
}
if *AcquisitionFile != "" {

411
pkg/csconfig/config_test.go Normal file
View file

@ -0,0 +1,411 @@
package csconfig
import (
"flag"
"os"
"testing"
"github.com/crowdsecurity/crowdsec/pkg/outputs"
log "github.com/sirupsen/logrus"
"github.com/stretchr/testify/assert"
)
func TestNewCrowdSecConfig(t *testing.T) {
tests := []struct {
name string
expectedResult *CrowdSec
err string
}{
{
name: "new configuration: basic",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
Daemonize: false,
Profiling: false,
WorkingFolder: "/tmp/",
DataFolder: "/var/lib/crowdsec/data/",
ConfigFolder: "/etc/crowdsec/config/",
PIDFolder: "/var/run/",
LogFolder: "/var/log/",
LogMode: "stdout",
APIMode: false,
NbParsers: 1,
Prometheus: false,
HTTPListen: "127.0.0.1:6060",
},
err: "",
},
}
for _, test := range tests {
result := NewCrowdSecConfig()
isOk := assert.Equal(t, test.expectedResult, result)
if !isOk {
t.Fatalf("test '%s' failed", test.name)
}
log.Infof("test '%s' : OK", test.name)
}
}
func TestLoadConfig(t *testing.T) {
tests := []struct {
name string
expectedResult *CrowdSec
Args []string
err string
}{
{
name: "load configuration: basic",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
Daemonize: true,
Profiling: true,
WorkingFolder: "./tests/",
DataFolder: "./tests/",
ConfigFolder: "./tests/",
PIDFolder: "./tests/",
LogFolder: "./tests/",
LogMode: "stdout",
APIMode: true,
NbParsers: 1,
Prometheus: true,
HTTPListen: "127.0.0.1:6060",
AcquisitionFile: "tests/acquis.yaml",
CsCliFolder: "./tests/cscli/",
SimulationCfg: &SimulationConfig{
Simulation: false,
Exclusions: nil,
},
SimulationCfgPath: "./tests/simulation.yaml",
OutputConfig: &outputs.OutputFactory{
BackendFolder: "./tests/plugins/backend",
MaxRecords: "",
MaxRecordsAge: "720h",
Flush: false,
Debug: false,
},
},
Args: []string{
"crowdsec",
"-c",
"./tests/config.yaml",
},
err: "",
},
{
name: "load configuration: with -file",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
SingleFile: "./tests/test.file",
SingleFileLabel: "test",
Daemonize: true,
Profiling: true,
WorkingFolder: "./tests/",
DataFolder: "./tests/",
ConfigFolder: "./tests/",
PIDFolder: "./tests/",
LogFolder: "./tests/",
LogMode: "stdout",
APIMode: true,
NbParsers: 1,
Prometheus: true,
HTTPListen: "127.0.0.1:6060",
AcquisitionFile: "tests/acquis.yaml",
CsCliFolder: "./tests/cscli/",
SimulationCfg: &SimulationConfig{
Simulation: false,
Exclusions: nil,
},
SimulationCfgPath: "./tests/simulation.yaml",
OutputConfig: &outputs.OutputFactory{
BackendFolder: "./tests/plugins/backend",
MaxRecords: "",
MaxRecordsAge: "720h",
Flush: false,
Debug: false,
},
},
Args: []string{
"crowdsec",
"-c",
"./tests/config.yaml",
"-file",
"./tests/test.file",
"-type",
"test",
},
err: "",
},
{
name: "load configuration: with -file without -type",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
Daemonize: false,
Profiling: false,
WorkingFolder: "/tmp/",
DataFolder: "/var/lib/crowdsec/data/",
ConfigFolder: "/etc/crowdsec/config/",
PIDFolder: "/var/run/",
LogFolder: "/var/log/",
LogMode: "stdout",
APIMode: false,
NbParsers: 1,
Prometheus: false,
HTTPListen: "127.0.0.1:6060",
},
Args: []string{
"crowdsec",
"-c",
"./tests/config.yaml",
"-file",
"./tests/test.file",
},
err: "-file requires -type",
},
{
name: "load configuration: all flags set",
expectedResult: &CrowdSec{
LogLevel: log.TraceLevel,
Daemonize: true,
Profiling: true,
WorkingFolder: "./tests/",
DataFolder: "./tests/",
ConfigFolder: "./tests/",
PIDFolder: "./tests/",
LogFolder: "./tests/",
LogMode: "stdout",
APIMode: true,
Linter: true,
NbParsers: 1,
Prometheus: true,
HTTPListen: "127.0.0.1:6060",
AcquisitionFile: "./tests/acquis.yaml",
CsCliFolder: "./tests/cscli/",
SimulationCfg: &SimulationConfig{
Simulation: false,
Exclusions: nil,
},
SimulationCfgPath: "./tests/simulation.yaml",
OutputConfig: &outputs.OutputFactory{
BackendFolder: "./tests/plugins/backend",
MaxRecords: "",
MaxRecordsAge: "720h",
Flush: false,
Debug: false,
},
RestoreMode: "./tests/states.json",
DumpBuckets: true,
},
Args: []string{
"crowdsec",
"-c",
"./tests/config.yaml",
"-acquis",
"./tests/acquis.yaml",
"-dump-state",
"-prometheus-metrics",
"-t",
"-daemon",
"-profile",
"-debug",
"-trace",
"-info",
"-restore-state",
"./tests/states.json",
"-api",
},
err: "",
},
{
name: "load configuration: bad config file",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
Daemonize: true,
Profiling: true,
WorkingFolder: "./tests/",
DataFolder: "./tests/",
ConfigFolder: "./tests/",
PIDFolder: "./tests/",
LogFolder: "./tests/",
LogMode: "stdout",
APIMode: true,
Linter: false,
NbParsers: 1,
Prometheus: true,
HTTPListen: "127.0.0.1:6060",
CsCliFolder: "./tests/cscli/",
SimulationCfgPath: "./tests/simulation.yaml",
OutputConfig: &outputs.OutputFactory{
BackendFolder: "./tests/plugins/backend",
MaxRecords: "",
MaxRecordsAge: "720h",
Flush: false,
Debug: false,
},
},
Args: []string{
"crowdsec",
"-c",
"./tests/bad_config.yaml",
},
err: "Error while loading configuration : parse './tests/bad_config.yaml' : yaml: unmarshal errors:\n line 1: field non_existing_field not found in type csconfig.CrowdSec",
},
{
name: "load configuration: bad simulation file",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
Daemonize: true,
Profiling: true,
WorkingFolder: "./tests/",
DataFolder: "./tests/",
ConfigFolder: "./tests/",
PIDFolder: "./tests/",
LogFolder: "./tests/",
LogMode: "stdout",
APIMode: true,
Linter: false,
NbParsers: 1,
Prometheus: true,
AcquisitionFile: "tests/acquis.yaml",
HTTPListen: "127.0.0.1:6060",
CsCliFolder: "./tests/cscli/",
SimulationCfgPath: "./tests/bad_simulation.yaml",
OutputConfig: &outputs.OutputFactory{
BackendFolder: "./tests/plugins/backend",
MaxRecords: "",
MaxRecordsAge: "720h",
Flush: false,
Debug: false,
},
},
Args: []string{
"crowdsec",
"-c",
"./tests/bad_config_simulation.yaml",
},
err: `Error while loading configuration : loading simulation config : while parsing './tests/bad_simulation.yaml' : yaml: unmarshal errors:
line 1: field test not found in type csconfig.SimulationConfig`,
},
{
name: "load configuration: bad config file",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
Daemonize: true,
Profiling: true,
WorkingFolder: "./tests/",
DataFolder: "./tests/",
ConfigFolder: "./tests/",
PIDFolder: "./tests/",
LogFolder: "./tests/",
LogMode: "stdout",
APIMode: true,
Linter: false,
NbParsers: 1,
Prometheus: true,
HTTPListen: "127.0.0.1:6060",
CsCliFolder: "./tests/cscli/",
SimulationCfgPath: "./tests/simulation.yaml",
OutputConfig: &outputs.OutputFactory{
BackendFolder: "./tests/plugins/backend",
MaxRecords: "",
MaxRecordsAge: "720h",
Flush: false,
Debug: false,
},
},
Args: []string{
"crowdsec",
"-c",
"./tests/bad_config.yaml",
},
err: "Error while loading configuration : parse './tests/bad_config.yaml' : yaml: unmarshal errors:\n line 1: field non_existing_field not found in type csconfig.CrowdSec",
},
{
name: "load configuration: non exist simulation file",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
Daemonize: true,
Profiling: true,
WorkingFolder: "./tests/",
DataFolder: "./tests/",
ConfigFolder: "./tests/",
PIDFolder: "./tests/",
LogFolder: "./tests/",
LogMode: "stdout",
APIMode: true,
Linter: false,
NbParsers: 1,
Prometheus: true,
AcquisitionFile: "tests/acquis.yaml",
HTTPListen: "127.0.0.1:6060",
CsCliFolder: "./tests/cscli/",
SimulationCfgPath: "./tests/non_exist.yaml",
OutputConfig: &outputs.OutputFactory{
BackendFolder: "./tests/plugins/backend",
MaxRecords: "",
MaxRecordsAge: "720h",
Flush: false,
Debug: false,
},
},
Args: []string{
"crowdsec",
"-c",
"./tests/bad_config_simulation_1.yaml",
},
err: "Error while loading configuration : loading simulation config : while reading './tests/non_exist.yaml' : open ./tests/non_exist.yaml: no such file or directory",
},
{
name: "load configuration: non existent configuration file",
expectedResult: &CrowdSec{
LogLevel: log.InfoLevel,
Daemonize: false,
Profiling: false,
WorkingFolder: "/tmp/",
DataFolder: "/var/lib/crowdsec/data/",
ConfigFolder: "/etc/crowdsec/config/",
PIDFolder: "/var/run/",
LogFolder: "/var/log/",
LogMode: "stdout",
APIMode: false,
NbParsers: 1,
Prometheus: false,
HTTPListen: "127.0.0.1:6060",
},
Args: []string{
"crowdsec",
"-c",
"./tests/non_exist.yaml",
},
err: "Error while loading configuration : read './tests/non_exist.yaml' : open ./tests/non_exist.yaml: no such file or directory",
},
}
oldArgs := os.Args
defer func() { os.Args = oldArgs }()
for _, test := range tests {
log.Printf("testing '%s'", test.name)
flag.CommandLine = flag.NewFlagSet(test.Args[0], flag.ExitOnError)
result := NewCrowdSecConfig()
os.Args = test.Args
err := result.LoadConfig()
if test.err != "" {
if err == nil {
t.Fatalf("test '%s' should returned an error", test.name)
}
isOk := assert.EqualErrorf(t, err, test.err, "")
if !isOk {
t.Fatalf("test '%s' failed", test.name)
}
}
if test.err == "" && err != nil {
t.Fatalf("test '%s' return an error : %s", test.name, err)
}
isOk := assert.Equal(t, test.expectedResult, result)
if !isOk {
t.Fatalf("test '%s' failed", test.name)
}
log.Infof("test '%s' : OK", test.name)
}
}

View file

@ -0,0 +1,19 @@
non_existing_field: ""
working_dir: ./tests/
data_dir: ./tests/
config_dir: ./tests/
pid_dir: ./tests/
log_dir: ./tests/
cscli_dir: ./tests/cscli/
simulation_path: ./tests/simulation.yaml
log_mode: stdout
log_level: info
profiling: true
apimode: true
daemon: true
prometheus: true
#for prometheus agent / golang debugging
http_listen: 127.0.0.1:6060
plugin:
backend: "./tests/plugins/backend"
max_records_age: 720h

View file

@ -0,0 +1,18 @@
working_dir: ./tests/
data_dir: ./tests/
config_dir: ./tests/
pid_dir: ./tests/
log_dir: ./tests/
cscli_dir: ./tests/cscli/
simulation_path: ./tests/bad_simulation.yaml
log_mode: stdout
log_level: info
profiling: true
apimode: true
daemon: true
prometheus: true
#for prometheus agent / golang debugging
http_listen: 127.0.0.1:6060
plugin:
backend: "./tests/plugins/backend"
max_records_age: 720h

View file

@ -0,0 +1,18 @@
working_dir: ./tests/
data_dir: ./tests/
config_dir: ./tests/
pid_dir: ./tests/
log_dir: ./tests/
cscli_dir: ./tests/cscli/
simulation_path: ./tests/non_exist.yaml
log_mode: stdout
log_level: info
profiling: true
apimode: true
daemon: true
prometheus: true
#for prometheus agent / golang debugging
http_listen: 127.0.0.1:6060
plugin:
backend: "./tests/plugins/backend"
max_records_age: 720h

View file

@ -0,0 +1 @@
test: ""

View file

@ -0,0 +1,18 @@
working_dir: ./tests/
data_dir: ./tests/
config_dir: ./tests/
pid_dir: ./tests/
log_dir: ./tests/
cscli_dir: ./tests/cscli/
simulation_path: ./tests/simulation.yaml
log_mode: stdout
log_level: info
profiling: true
apimode: true
daemon: true
prometheus: true
#for prometheus agent / golang debugging
http_listen: 127.0.0.1:6060
plugin:
backend: "./tests/plugins/backend"
max_records_age: 720h

View file

@ -0,0 +1,4 @@
simulation: off
# exclusions:
# - crowdsecurity/ssh-bf

View file

@ -1,135 +0,0 @@
package exprhelpers
import (
"log"
"testing"
"github.com/antonmedv/expr"
"github.com/stretchr/testify/require"
"gotest.tools/assert"
)
var (
TestFolder = "tests"
)
func TestRegexpInFile(t *testing.T) {
if err := Init(); err != nil {
log.Fatalf(err.Error())
}
err := FileInit(TestFolder, "test_data_re.txt", "regex")
if err != nil {
log.Fatalf(err.Error())
}
tests := []struct {
filter string
result bool
err error
}{
{
filter: "RegexpInFile('crowdsec', 'test_data_re.txt')",
result: false,
err: nil,
},
{
filter: "RegexpInFile('Crowdsec', 'test_data_re.txt')",
result: true,
err: nil,
},
{
filter: "RegexpInFile('test Crowdsec', 'test_data_re.txt')",
result: true,
err: nil,
},
{
filter: "RegexpInFile('test CrowdSec', 'test_data_re.txt')",
result: true,
err: nil,
},
}
for _, test := range tests {
compiledFilter, err := expr.Compile(test.filter, expr.Env(GetExprEnv(map[string]interface{}{})))
if err != nil {
log.Fatalf(err.Error())
}
log.Printf("Running filter : %s", test.filter)
result, err := expr.Run(compiledFilter, GetExprEnv(map[string]interface{}{}))
if err != nil {
log.Fatalf(err.Error())
}
assert.Equal(t, test.result, result)
}
}
func TestFile(t *testing.T) {
if err := Init(); err != nil {
log.Fatalf(err.Error())
}
err := FileInit(TestFolder, "test_data.txt", "")
if err != nil {
log.Fatalf(err.Error())
}
tests := []struct {
filter string
result bool
err error
}{
{
filter: "'Crowdsec' in File('test_data.txt')",
result: true,
err: nil,
},
{
filter: "'CrowdSecurity' in File('test_data.txt')",
result: false,
err: nil,
},
{
filter: "'Crowdsecurity' in File('test_data.txt')",
result: true,
err: nil,
},
{
filter: "'test' in File('test_data.txt')",
result: false,
err: nil,
},
}
for _, test := range tests {
compiledFilter, err := expr.Compile(test.filter, expr.Env(GetExprEnv(map[string]interface{}{})))
if err != nil {
log.Fatalf(err.Error())
}
log.Printf("Running filter : %s", test.filter)
result, err := expr.Run(compiledFilter, GetExprEnv(map[string]interface{}{}))
if err != nil {
log.Fatalf(err.Error())
}
assert.Equal(t, test.result, result)
}
}
func TestIpInRange(t *testing.T) {
env := map[string]interface{}{
"ip": "192.168.0.1",
"ipRange": "192.168.0.0/24",
"IpInRange": IpInRange,
}
code := "IpInRange(ip, ipRange)"
log.Printf("Running filter : %s", code)
program, err := expr.Compile(code, expr.Env(env))
require.NoError(t, err)
output, err := expr.Run(program, env)
require.NoError(t, err)
require.Equal(t, true, output)
}

View file

@ -0,0 +1,206 @@
package exprhelpers
import (
"log"
"testing"
"github.com/antonmedv/expr"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
var (
TestFolder = "tests"
)
func TestRegexpInFile(t *testing.T) {
if err := Init(); err != nil {
log.Fatalf(err.Error())
}
err := FileInit(TestFolder, "test_data_re.txt", "regex")
if err != nil {
log.Fatalf(err.Error())
}
tests := []struct {
name string
filter string
result bool
err error
}{
{
name: "RegexpInFile() test: lower case word in data file",
filter: "RegexpInFile('crowdsec', 'test_data_re.txt')",
result: false,
err: nil,
},
{
name: "RegexpInFile() test: Match exactly",
filter: "RegexpInFile('Crowdsec', 'test_data_re.txt')",
result: true,
err: nil,
},
{
name: "RegexpInFile() test: match with word before",
filter: "RegexpInFile('test Crowdsec', 'test_data_re.txt')",
result: true,
err: nil,
},
{
name: "RegexpInFile() test: match with word before and other case",
filter: "RegexpInFile('test CrowdSec', 'test_data_re.txt')",
result: true,
err: nil,
},
}
for _, test := range tests {
compiledFilter, err := expr.Compile(test.filter, expr.Env(GetExprEnv(map[string]interface{}{})))
if err != nil {
log.Fatalf(err.Error())
}
result, err := expr.Run(compiledFilter, GetExprEnv(map[string]interface{}{}))
if err != nil {
log.Fatalf(err.Error())
}
if isOk := assert.Equal(t, test.result, result); !isOk {
t.Fatalf("test '%s' : NOK", test.name)
}
}
}
func TestFile(t *testing.T) {
if err := Init(); err != nil {
log.Fatalf(err.Error())
}
err := FileInit(TestFolder, "test_data.txt", "")
if err != nil {
log.Fatalf(err.Error())
}
tests := []struct {
name string
filter string
result bool
err error
}{
{
name: "File() test: word in file",
filter: "'Crowdsec' in File('test_data.txt')",
result: true,
err: nil,
},
{
name: "File() test: word in file but different case",
filter: "'CrowdSecurity' in File('test_data.txt')",
result: false,
err: nil,
},
{
name: "File() test: word not in file",
filter: "'test' in File('test_data.txt')",
result: false,
err: nil,
},
{
name: "File() test: filepath provided doesn't exist",
filter: "'test' in File('non_existing_data.txt')",
result: false,
err: nil,
},
}
for _, test := range tests {
compiledFilter, err := expr.Compile(test.filter, expr.Env(GetExprEnv(map[string]interface{}{})))
if err != nil {
log.Fatalf(err.Error())
}
result, err := expr.Run(compiledFilter, GetExprEnv(map[string]interface{}{}))
if err != nil {
log.Fatalf(err.Error())
}
if isOk := assert.Equal(t, test.result, result); !isOk {
t.Fatalf("test '%s' : NOK", test.name)
}
log.Printf("test '%s' : OK", test.name)
}
}
func TestIpInRange(t *testing.T) {
tests := []struct {
name string
env map[string]interface{}
code string
result bool
err string
}{
{
name: "IpInRange() test: basic test",
env: map[string]interface{}{
"ip": "192.168.0.1",
"ipRange": "192.168.0.0/24",
"IpInRange": IpInRange,
},
code: "IpInRange(ip, ipRange)",
result: true,
err: "",
},
{
name: "IpInRange() test: malformed IP",
env: map[string]interface{}{
"ip": "192.168.0",
"ipRange": "192.168.0.0/24",
"IpInRange": IpInRange,
},
code: "IpInRange(ip, ipRange)",
result: false,
err: "",
},
{
name: "IpInRange() test: malformed IP range",
env: map[string]interface{}{
"ip": "192.168.0.0/255",
"ipRange": "192.168.0.0/24",
"IpInRange": IpInRange,
},
code: "IpInRange(ip, ipRange)",
result: false,
err: "",
},
}
for _, test := range tests {
program, err := expr.Compile(test.code, expr.Env(test.env))
require.NoError(t, err)
output, err := expr.Run(program, test.env)
require.NoError(t, err)
require.Equal(t, test.result, output)
log.Printf("test '%s' : OK", test.name)
}
}
func TestAtof(t *testing.T) {
testFloat := "1.5"
expectedFloat := 1.5
if Atof(testFloat) != expectedFloat {
t.Fatalf("Atof should returned 1.5 as a float")
}
log.Printf("test 'Atof()' : OK")
}
func TestUpper(t *testing.T) {
testStr := "test"
expectedStr := "TEST"
if Upper(testStr) != expectedStr {
t.Fatalf("Upper() should returned 1.5 as a float")
}
log.Printf("test 'Upper()' : OK")
}

View file

@ -0,0 +1,49 @@
package exprhelpers
import (
"log"
"testing"
"github.com/stretchr/testify/assert"
)
func TestJsonExtract(t *testing.T) {
if err := Init(); err != nil {
log.Fatalf(err.Error())
}
err := FileInit(TestFolder, "test_data_re.txt", "regex")
if err != nil {
log.Fatalf(err.Error())
}
tests := []struct {
name string
jsonBlob string
targetField string
expectResult string
}{
{
name: "basic json extract",
jsonBlob: `{"test" : "1234"}`,
targetField: "test",
expectResult: "1234",
},
{
name: "basic json extract with non existing field",
jsonBlob: `{"test" : "1234"}`,
targetField: "non_existing_field",
expectResult: "",
},
}
for _, test := range tests {
result := JsonExtract(test.jsonBlob, test.targetField)
isOk := assert.Equal(t, test.expectResult, result)
if !isOk {
t.Fatalf("test '%s' failed", test.name)
}
log.Printf("test '%s' : OK", test.name)
}
}