github.com/Jeffail/benthos/v3@v3.65.0/lib/input/reader/amqp_1_test.go (about)

     1  package reader
     2  
     3  import (
     4  	"context"
     5  	"flag"
     6  	"fmt"
     7  	"os"
     8  	"regexp"
     9  	"strings"
    10  	"sync"
    11  	"testing"
    12  	"time"
    13  
    14  	"github.com/Azure/go-amqp"
    15  	"github.com/Jeffail/benthos/v3/lib/log"
    16  	"github.com/Jeffail/benthos/v3/lib/metrics"
    17  	"github.com/Jeffail/benthos/v3/lib/response"
    18  	"github.com/Jeffail/benthos/v3/lib/types"
    19  	"github.com/stretchr/testify/assert"
    20  	"github.com/stretchr/testify/require"
    21  )
    22  
    23  func TestAMQP1Integration(t *testing.T) {
    24  	if m := flag.Lookup("test.run").Value.String(); m == "" || regexp.MustCompile(strings.Split(m, "/")[0]).FindString(t.Name()) == "" {
    25  		t.Skip("Skipping as execution was not requested explicitly using go test -run ^TestIntegration$")
    26  	}
    27  
    28  	if testing.Short() {
    29  		t.Skip("Skipping integration test in short mode")
    30  	}
    31  
    32  	url := os.Getenv("TEST_SB_URL")
    33  	sourceAddress := os.Getenv("TEST_SB_SOURCE_ADDRESS")
    34  	if url == "" || sourceAddress == "" {
    35  		t.Skip("Skipping because of missing TEST_SB_URL or TEST_SB_SOURCE_ADDRESS. Those should be point to Azure Service Bus configured with Message lock duration to 5 seconds.")
    36  	}
    37  
    38  	t.Run("TestAMQP1Connected", func(t *testing.T) {
    39  		testAMQP1Connected(url, sourceAddress, t)
    40  	})
    41  	t.Run("TestAMQP1Disconnected", func(t *testing.T) {
    42  		testAMQP1Disconnected(url, sourceAddress, t)
    43  	})
    44  }
    45  
    46  func testAMQP1Connected(url, sourceAddress string, t *testing.T) {
    47  	ctx := context.Background()
    48  
    49  	conf := NewAMQP1Config()
    50  	conf.URL = url
    51  	conf.SourceAddress = sourceAddress
    52  	conf.AzureRenewLock = true
    53  
    54  	m, err := NewAMQP1(conf, log.Noop(), metrics.Noop())
    55  	require.NoError(t, err)
    56  
    57  	err = m.ConnectWithContext(ctx)
    58  	require.NoError(t, err)
    59  
    60  	defer func() {
    61  		m.CloseAsync()
    62  		err := m.WaitForClose(time.Second)
    63  		assert.NoError(t, err)
    64  	}()
    65  
    66  	client, err := amqp.Dial(url)
    67  	require.NoError(t, err)
    68  	defer client.Close()
    69  
    70  	session, err := client.NewSession()
    71  	require.NoError(t, err)
    72  	defer session.Close(ctx)
    73  
    74  	sender, err := session.NewSender(
    75  		amqp.LinkTargetAddress("/test"),
    76  	)
    77  	require.NoError(t, err)
    78  	defer sender.Close(ctx)
    79  
    80  	N := 10
    81  	wg := sync.WaitGroup{}
    82  
    83  	testMsgs := map[string]bool{}
    84  	for i := 0; i < N; i++ {
    85  		wg.Add(1)
    86  
    87  		str := fmt.Sprintf("hello world: %v", i)
    88  		testMsgs[str] = true
    89  		go func(testStr string) {
    90  			defer wg.Done()
    91  
    92  			contentType := "plain/text"
    93  			contentEncoding := "utf-8"
    94  			createdAt := time.Date(2020, time.January, 30, 1, 0, 0, 0, time.UTC)
    95  			err := sender.Send(ctx, &amqp.Message{
    96  				Properties: &amqp.MessageProperties{
    97  					ContentType:     &contentType,
    98  					ContentEncoding: &contentEncoding,
    99  					CreationTime:    &createdAt,
   100  				},
   101  				Data: [][]byte{[]byte(str)},
   102  			})
   103  			require.NoError(t, err)
   104  		}(str)
   105  	}
   106  
   107  	for i := 0; i < N; i++ {
   108  		actM, ackFn, err := m.ReadWithContext(ctx)
   109  		assert.NoError(t, err)
   110  		wg.Add(1)
   111  
   112  		go func() {
   113  			defer wg.Done()
   114  
   115  			assert.True(t, testMsgs[string(actM.Get(0).Get())], "Unexpected message")
   116  			assert.Equal(t, "plain/text", actM.Get(0).Metadata().Get("amqp_content_type"))
   117  			assert.Equal(t, "utf-8", actM.Get(0).Metadata().Get("amqp_content_encoding"))
   118  
   119  			time.Sleep(6 * time.Second) // Simulate long processing before ack so message lock expires and lock renewal is requires
   120  
   121  			assert.NoError(t, ackFn(ctx, response.NewAck()))
   122  		}()
   123  	}
   124  	wg.Wait()
   125  
   126  	readCtx, cancel := context.WithTimeout(ctx, 3*time.Second)
   127  	defer cancel()
   128  	_, _, err = m.ReadWithContext(readCtx)
   129  	assert.Error(t, err, "got unexpected message (redelivery?)")
   130  
   131  }
   132  
   133  func testAMQP1Disconnected(url, sourceAddress string, t *testing.T) {
   134  	ctx := context.Background()
   135  
   136  	conf := NewAMQP1Config()
   137  	conf.URL = url
   138  	conf.SourceAddress = sourceAddress
   139  	conf.AzureRenewLock = true
   140  
   141  	m, err := NewAMQP1(conf, log.Noop(), metrics.Noop())
   142  	require.NoError(t, err)
   143  
   144  	err = m.ConnectWithContext(ctx)
   145  	require.NoError(t, err)
   146  
   147  	wg := sync.WaitGroup{}
   148  	wg.Add(1)
   149  	go func() {
   150  		m.CloseAsync()
   151  		err := m.WaitForClose(time.Second)
   152  		require.NoError(t, err)
   153  		wg.Done()
   154  	}()
   155  
   156  	if _, _, err = m.ReadWithContext(ctx); err != types.ErrTypeClosed && err != types.ErrNotConnected {
   157  		t.Errorf("Wrong error: %v != %v", err, types.ErrTypeClosed)
   158  	}
   159  
   160  	wg.Wait()
   161  }