github.com/Jeffail/benthos/v3@v3.65.0/public/service/processor_test.go (about)

     1  package service
     2  
     3  import (
     4  	"context"
     5  	"errors"
     6  	"testing"
     7  	"time"
     8  
     9  	"github.com/Jeffail/benthos/v3/lib/message"
    10  	"github.com/Jeffail/benthos/v3/lib/metrics"
    11  	"github.com/Jeffail/benthos/v3/lib/processor"
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  )
    15  
    16  type fnProcessor struct {
    17  	fn     func(context.Context, *Message) (MessageBatch, error)
    18  	closed bool
    19  }
    20  
    21  func (p *fnProcessor) Process(ctx context.Context, msg *Message) (MessageBatch, error) {
    22  	return p.fn(ctx, msg)
    23  }
    24  
    25  func (p *fnProcessor) Close(ctx context.Context) error {
    26  	p.closed = true
    27  	return nil
    28  }
    29  
    30  func TestProcessorAirGapShutdown(t *testing.T) {
    31  	rp := &fnProcessor{}
    32  	agrp := newAirGapProcessor("foo", rp, metrics.Noop())
    33  
    34  	err := agrp.WaitForClose(time.Millisecond * 5)
    35  	assert.EqualError(t, err, "action timed out")
    36  	assert.False(t, rp.closed)
    37  
    38  	agrp.CloseAsync()
    39  	err = agrp.WaitForClose(time.Millisecond * 5)
    40  	assert.NoError(t, err)
    41  	assert.True(t, rp.closed)
    42  }
    43  
    44  func TestProcessorAirGapOneToOne(t *testing.T) {
    45  	agrp := newAirGapProcessor("foo", &fnProcessor{
    46  		fn: func(c context.Context, m *Message) (MessageBatch, error) {
    47  			if b, err := m.AsBytes(); err != nil || string(b) != "unchanged" {
    48  				return nil, errors.New("nope")
    49  			}
    50  			m.SetBytes([]byte("changed"))
    51  			return MessageBatch{m}, nil
    52  		},
    53  	}, metrics.Noop())
    54  
    55  	msg := message.New([][]byte{[]byte("unchanged")})
    56  	msgs, res := agrp.ProcessMessage(msg)
    57  	require.Nil(t, res)
    58  	require.Len(t, msgs, 1)
    59  	assert.Equal(t, 1, msgs[0].Len())
    60  	assert.Equal(t, "changed", string(msgs[0].Get(0).Get()))
    61  	assert.Equal(t, "unchanged", string(msg.Get(0).Get()))
    62  }
    63  
    64  func TestProcessorAirGapOneToError(t *testing.T) {
    65  	agrp := newAirGapProcessor("foo", &fnProcessor{
    66  		fn: func(c context.Context, m *Message) (MessageBatch, error) {
    67  			_, err := m.AsStructured()
    68  			return nil, err
    69  		},
    70  	}, metrics.Noop())
    71  
    72  	msg := message.New([][]byte{[]byte("not a structured doc")})
    73  	msgs, res := agrp.ProcessMessage(msg)
    74  	require.Nil(t, res)
    75  	require.Len(t, msgs, 1)
    76  	assert.Equal(t, 1, msgs[0].Len())
    77  	assert.Equal(t, "not a structured doc", string(msgs[0].Get(0).Get()))
    78  	assert.Equal(t, "not a structured doc", string(msgs[0].Get(0).Get()))
    79  	assert.Equal(t, "invalid character 'o' in literal null (expecting 'u')", processor.GetFail(msgs[0].Get(0)))
    80  }
    81  
    82  func TestProcessorAirGapOneToMany(t *testing.T) {
    83  	agrp := newAirGapProcessor("foo", &fnProcessor{
    84  		fn: func(c context.Context, m *Message) (MessageBatch, error) {
    85  			if b, err := m.AsBytes(); err != nil || string(b) != "unchanged" {
    86  				return nil, errors.New("nope")
    87  			}
    88  			second := m.Copy()
    89  			third := m.Copy()
    90  			m.SetBytes([]byte("changed 1"))
    91  			second.SetBytes([]byte("changed 2"))
    92  			third.SetBytes([]byte("changed 3"))
    93  			return MessageBatch{m, second, third}, nil
    94  		},
    95  	}, metrics.Noop())
    96  
    97  	msg := message.New([][]byte{[]byte("unchanged")})
    98  	msgs, res := agrp.ProcessMessage(msg)
    99  	require.Nil(t, res)
   100  	require.Len(t, msgs, 1)
   101  	assert.Equal(t, 3, msgs[0].Len())
   102  	assert.Equal(t, "changed 1", string(msgs[0].Get(0).Get()))
   103  	assert.Equal(t, "changed 2", string(msgs[0].Get(1).Get()))
   104  	assert.Equal(t, "changed 3", string(msgs[0].Get(2).Get()))
   105  	assert.Equal(t, "unchanged", string(msg.Get(0).Get()))
   106  }
   107  
   108  //------------------------------------------------------------------------------
   109  
   110  type fnBatchProcessor struct {
   111  	fn     func(context.Context, MessageBatch) ([]MessageBatch, error)
   112  	closed bool
   113  }
   114  
   115  func (p *fnBatchProcessor) ProcessBatch(ctx context.Context, msg MessageBatch) ([]MessageBatch, error) {
   116  	return p.fn(ctx, msg)
   117  }
   118  
   119  func (p *fnBatchProcessor) Close(ctx context.Context) error {
   120  	p.closed = true
   121  	return nil
   122  }
   123  
   124  func TestBatchProcessorAirGapShutdown(t *testing.T) {
   125  	rp := &fnBatchProcessor{}
   126  	agrp := newAirGapBatchProcessor("foo", rp, metrics.Noop())
   127  
   128  	err := agrp.WaitForClose(time.Millisecond * 5)
   129  	assert.EqualError(t, err, "action timed out")
   130  	assert.False(t, rp.closed)
   131  
   132  	agrp.CloseAsync()
   133  	err = agrp.WaitForClose(time.Millisecond * 5)
   134  	assert.NoError(t, err)
   135  	assert.True(t, rp.closed)
   136  }
   137  
   138  func TestBatchProcessorAirGapOneToOne(t *testing.T) {
   139  	agrp := newAirGapBatchProcessor("foo", &fnBatchProcessor{
   140  		fn: func(c context.Context, msgs MessageBatch) ([]MessageBatch, error) {
   141  			if b, err := msgs[0].AsBytes(); err != nil || string(b) != "unchanged" {
   142  				return nil, errors.New("nope")
   143  			}
   144  			msgs[0].SetBytes([]byte("changed"))
   145  			return []MessageBatch{{msgs[0]}}, nil
   146  		},
   147  	}, metrics.Noop())
   148  
   149  	msg := message.New([][]byte{[]byte("unchanged")})
   150  	msgs, res := agrp.ProcessMessage(msg)
   151  	require.Nil(t, res)
   152  	require.Len(t, msgs, 1)
   153  	assert.Equal(t, 1, msgs[0].Len())
   154  	assert.Equal(t, "changed", string(msgs[0].Get(0).Get()))
   155  	assert.Equal(t, "unchanged", string(msg.Get(0).Get()))
   156  }
   157  
   158  func TestBatchProcessorAirGapOneToError(t *testing.T) {
   159  	agrp := newAirGapBatchProcessor("foo", &fnBatchProcessor{
   160  		fn: func(c context.Context, msgs MessageBatch) ([]MessageBatch, error) {
   161  			_, err := msgs[0].AsStructured()
   162  			return nil, err
   163  		},
   164  	}, metrics.Noop())
   165  
   166  	msg := message.New([][]byte{[]byte("not a structured doc")})
   167  	msgs, res := agrp.ProcessMessage(msg)
   168  	require.Nil(t, res)
   169  	require.Len(t, msgs, 1)
   170  	assert.Equal(t, 1, msgs[0].Len())
   171  	assert.Equal(t, "not a structured doc", string(msgs[0].Get(0).Get()))
   172  	assert.Equal(t, "not a structured doc", string(msgs[0].Get(0).Get()))
   173  	assert.Equal(t, "invalid character 'o' in literal null (expecting 'u')", processor.GetFail(msgs[0].Get(0)))
   174  }
   175  
   176  func TestBatchProcessorAirGapOneToMany(t *testing.T) {
   177  	agrp := newAirGapBatchProcessor("foo", &fnBatchProcessor{
   178  		fn: func(c context.Context, msgs MessageBatch) ([]MessageBatch, error) {
   179  			if b, err := msgs[0].AsBytes(); err != nil || string(b) != "unchanged" {
   180  				return nil, errors.New("nope")
   181  			}
   182  			second := msgs[0].Copy()
   183  			third := msgs[0].Copy()
   184  			msgs[0].SetBytes([]byte("changed 1"))
   185  			second.SetBytes([]byte("changed 2"))
   186  			third.SetBytes([]byte("changed 3"))
   187  			return []MessageBatch{{msgs[0], second}, {third}}, nil
   188  		},
   189  	}, metrics.Noop())
   190  
   191  	msg := message.New([][]byte{[]byte("unchanged")})
   192  	msgs, res := agrp.ProcessMessage(msg)
   193  	require.Nil(t, res)
   194  	require.Len(t, msgs, 2)
   195  	assert.Equal(t, "unchanged", string(msg.Get(0).Get()))
   196  
   197  	assert.Equal(t, 2, msgs[0].Len())
   198  	assert.Equal(t, "changed 1", string(msgs[0].Get(0).Get()))
   199  	assert.Equal(t, "changed 2", string(msgs[0].Get(1).Get()))
   200  
   201  	assert.Equal(t, 1, msgs[1].Len())
   202  	assert.Equal(t, "changed 3", string(msgs[1].Get(0).Get()))
   203  }