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