github.com/Jeffail/benthos/v3@v3.65.0/internal/bloblang/package_test.go (about)

     1  package bloblang
     2  
     3  import (
     4  	"sync"
     5  	"testing"
     6  
     7  	"github.com/Jeffail/benthos/v3/internal/bloblang/mapping"
     8  	"github.com/Jeffail/benthos/v3/internal/bloblang/query"
     9  	"github.com/Jeffail/benthos/v3/lib/message"
    10  	"github.com/stretchr/testify/assert"
    11  	"github.com/stretchr/testify/require"
    12  )
    13  
    14  func TestMappings(t *testing.T) {
    15  	tests := map[string]struct {
    16  		mapping           string
    17  		input             interface{}
    18  		output            interface{}
    19  		assignmentTargets []mapping.TargetPath
    20  		queryTargets      []query.TargetPath
    21  	}{
    22  		"basic query": {
    23  			mapping: `root = this.foo
    24  			let bar = $baz | this.bar.baz`,
    25  			input: map[string]interface{}{
    26  				"foo": "bar",
    27  			},
    28  			output: "bar",
    29  			assignmentTargets: []mapping.TargetPath{
    30  				mapping.NewTargetPath(mapping.TargetValue),
    31  				mapping.NewTargetPath(mapping.TargetVariable, "bar"),
    32  			},
    33  			queryTargets: []query.TargetPath{
    34  				query.NewTargetPath(query.TargetValue, "foo"),
    35  				query.NewTargetPath(query.TargetVariable, "baz"),
    36  				query.NewTargetPath(query.TargetValue, "bar", "baz"),
    37  			},
    38  		},
    39  		"complex query": {
    40  			mapping: `root = match this.foo {
    41  				this.bar == "bruh" => this.baz.buz,
    42  				_ => $foo
    43  			}`,
    44  			input: map[string]interface{}{
    45  				"foo": map[string]interface{}{
    46  					"bar": "bruh",
    47  					"baz": map[string]interface{}{
    48  						"buz": "the result",
    49  					},
    50  				},
    51  			},
    52  			output: "the result",
    53  			assignmentTargets: []mapping.TargetPath{
    54  				mapping.NewTargetPath(mapping.TargetValue),
    55  			},
    56  			queryTargets: []query.TargetPath{
    57  				query.NewTargetPath(query.TargetValue, "foo", "bar"),
    58  				query.NewTargetPath(query.TargetValue, "foo", "baz", "buz"),
    59  				query.NewTargetPath(query.TargetVariable, "foo"),
    60  				query.NewTargetPath(query.TargetValue, "foo"),
    61  			},
    62  		},
    63  		"long assignment": {
    64  			mapping: `root.foo.bar = "this"
    65  			root.foo = "that"
    66  			root.baz.buz.0.bev = "then this"`,
    67  			output: map[string]interface{}{
    68  				"foo": "that",
    69  				"baz": map[string]interface{}{
    70  					"buz": map[string]interface{}{
    71  						"0": map[string]interface{}{
    72  							"bev": "then this",
    73  						},
    74  					},
    75  				},
    76  			},
    77  			assignmentTargets: []mapping.TargetPath{
    78  				mapping.NewTargetPath(mapping.TargetValue, "foo", "bar"),
    79  				mapping.NewTargetPath(mapping.TargetValue, "foo"),
    80  				mapping.NewTargetPath(mapping.TargetValue, "baz", "buz", "0", "bev"),
    81  			},
    82  		},
    83  	}
    84  
    85  	for name, test := range tests {
    86  		test := test
    87  		t.Run(name, func(t *testing.T) {
    88  			m, err := GlobalEnvironment().NewMapping(test.mapping)
    89  			require.NoError(t, err)
    90  
    91  			assert.Equal(t, test.assignmentTargets, m.AssignmentTargets())
    92  
    93  			_, targets := m.QueryTargets(query.TargetsContext{
    94  				Maps: map[string]query.Function{},
    95  			})
    96  			assert.Equal(t, test.queryTargets, targets)
    97  
    98  			res, err := m.Exec(query.FunctionContext{
    99  				MsgBatch: message.New(nil),
   100  				Vars:     map[string]interface{}{},
   101  			}.WithValue(test.input))
   102  			require.NoError(t, err)
   103  			assert.Equal(t, test.output, res)
   104  		})
   105  	}
   106  }
   107  
   108  func TestMappingParallelExecution(t *testing.T) {
   109  	tests := map[string]struct {
   110  		mapping string
   111  		input   interface{}
   112  		output  interface{}
   113  	}{
   114  		"basic query using vars": {
   115  			mapping: `let tmp = this.foo.uppercase()
   116  			root.first = $tmp
   117  			let tmp = this.foo.lowercase()
   118  			root.second = $tmp`,
   119  			input: map[string]interface{}{
   120  				"foo": "HELLO world",
   121  			},
   122  			output: map[string]interface{}{
   123  				"first":  "HELLO WORLD",
   124  				"second": "hello world",
   125  			},
   126  		},
   127  	}
   128  
   129  	for name, test := range tests {
   130  		test := test
   131  		t.Run(name, func(t *testing.T) {
   132  			m, err := GlobalEnvironment().NewMapping(test.mapping)
   133  			require.NoError(t, err)
   134  
   135  			startChan := make(chan struct{})
   136  
   137  			var wg sync.WaitGroup
   138  			for i := 0; i < 10; i++ {
   139  				wg.Add(1)
   140  				go func() {
   141  					defer wg.Done()
   142  					<-startChan
   143  
   144  					for j := 0; j < 100; j++ {
   145  						part := message.NewPart(nil)
   146  						require.NoError(t, part.SetJSON(test.input))
   147  
   148  						msg := message.New(nil)
   149  						msg.Append(part)
   150  
   151  						p, err := m.MapPart(0, msg)
   152  						require.NoError(t, err)
   153  
   154  						res, err := p.JSON()
   155  						require.NoError(t, err)
   156  
   157  						assert.Equal(t, test.output, res)
   158  					}
   159  				}()
   160  			}
   161  
   162  			close(startChan)
   163  			wg.Wait()
   164  		})
   165  	}
   166  }