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

     1  package reader
     2  
     3  import (
     4  	"flag"
     5  	"fmt"
     6  	"regexp"
     7  	"strings"
     8  	"sync"
     9  	"testing"
    10  	"time"
    11  
    12  	"github.com/Jeffail/benthos/v3/lib/log"
    13  	"github.com/Jeffail/benthos/v3/lib/metrics"
    14  	"github.com/Jeffail/benthos/v3/lib/types"
    15  	"github.com/ory/dockertest/v3"
    16  	amqp "github.com/rabbitmq/amqp091-go"
    17  )
    18  
    19  func TestAMQPIntegration(t *testing.T) {
    20  	if m := flag.Lookup("test.run").Value.String(); m == "" || regexp.MustCompile(strings.Split(m, "/")[0]).FindString(t.Name()) == "" {
    21  		t.Skip("Skipping as execution was not requested explicitly using go test -run ^TestIntegration$")
    22  	}
    23  
    24  	if testing.Short() {
    25  		t.Skip("Skipping integration test in short mode")
    26  	}
    27  
    28  	pool, err := dockertest.NewPool("")
    29  	if err != nil {
    30  		t.Skipf("Could not connect to docker: %s", err)
    31  	}
    32  	pool.MaxWait = time.Second * 30
    33  
    34  	resource, err := pool.Run("rabbitmq", "latest", nil)
    35  	if err != nil {
    36  		t.Fatalf("Could not start resource: %s", err)
    37  	}
    38  	defer func() {
    39  		if err = pool.Purge(resource); err != nil {
    40  			t.Logf("Failed to clean up docker resource: %v", err)
    41  		}
    42  	}()
    43  
    44  	url := fmt.Sprintf("amqp://guest:guest@localhost:%v/", resource.GetPort("5672/tcp"))
    45  
    46  	if err = pool.Retry(func() error {
    47  		client, err := amqp.Dial(url)
    48  		if err == nil {
    49  			var mChan *amqp.Channel
    50  			if mChan, err = client.Channel(); err == nil {
    51  				err = mChan.ExchangeDeclare(
    52  					"test-exchange", // name of the exchange
    53  					"direct",        // type
    54  					true,            // durable
    55  					false,           // delete when complete
    56  					false,           // internal
    57  					false,           // noWait
    58  					nil,             // arguments
    59  				)
    60  			}
    61  			client.Close()
    62  		}
    63  
    64  		return err
    65  	}); err != nil {
    66  		t.Fatalf("Could not connect to docker resource: %s", err)
    67  	}
    68  
    69  	t.Run("TestAMQPConnect", func(te *testing.T) {
    70  		testAMQPConnect(url, te)
    71  	})
    72  	t.Run("TestAMQPDisconnect", func(te *testing.T) {
    73  		testAMQPDisconnect(url, te)
    74  	})
    75  }
    76  
    77  func testAMQPConnect(url string, t *testing.T) {
    78  	exchange := "test-exchange"
    79  	key := "benthos-key"
    80  
    81  	conf := NewAMQPConfig()
    82  	conf.URL = url
    83  	conf.QueueDeclare.Enabled = true
    84  	conf.BindingsDeclare = append(conf.BindingsDeclare, AMQPBindingConfig{
    85  		Exchange:   exchange,
    86  		RoutingKey: key,
    87  	})
    88  
    89  	m, err := NewAMQP(conf, log.Noop(), metrics.Noop())
    90  	if err != nil {
    91  		t.Fatal(err)
    92  	}
    93  
    94  	if err = m.Connect(); err != nil {
    95  		t.Fatal(err)
    96  	}
    97  
    98  	defer func() {
    99  		m.CloseAsync()
   100  		if cErr := m.WaitForClose(time.Second); cErr != nil {
   101  			t.Error(cErr)
   102  		}
   103  	}()
   104  
   105  	var mIn *amqp.Connection
   106  	if mIn, err = amqp.Dial(url); err != nil {
   107  		t.Fatal(err)
   108  	}
   109  
   110  	var mChan *amqp.Channel
   111  	if mChan, err = mIn.Channel(); err != nil {
   112  		t.Fatalf("AMQP Channel: %s", err)
   113  	}
   114  
   115  	defer func() {
   116  		mChan.Close()
   117  		mIn.Close()
   118  	}()
   119  
   120  	N := 10
   121  
   122  	wg := sync.WaitGroup{}
   123  	wg.Add(N)
   124  
   125  	testMsgs := map[string]struct{}{}
   126  	for i := 0; i < N; i++ {
   127  		str := fmt.Sprintf("hello world: %v", i)
   128  		testMsgs[str] = struct{}{}
   129  		go func(testStr string) {
   130  			if pErr := mChan.Publish(exchange, key, false, false, amqp.Publishing{
   131  				Headers: amqp.Table{
   132  					"foo": "bar",
   133  					"root": amqp.Table{
   134  						"foo": "bar2",
   135  					},
   136  				},
   137  				ContentType:     "application/octet-stream",
   138  				ContentEncoding: "",
   139  				Body:            []byte(testStr),
   140  				DeliveryMode:    amqp.Transient, // 1=non-persistent, 2=persistent
   141  				Priority:        0,              // 0-9
   142  			}); pErr != nil {
   143  				t.Error(pErr)
   144  			}
   145  			wg.Done()
   146  		}(str)
   147  	}
   148  
   149  	lMsgs := len(testMsgs)
   150  	for lMsgs > 0 {
   151  		var actM types.Message
   152  		actM, err = m.Read()
   153  		if err != nil {
   154  			t.Error(err)
   155  		} else {
   156  			act := string(actM.Get(0).Get())
   157  			if _, exists := testMsgs[act]; !exists {
   158  				t.Errorf("Unexpected message: %v", act)
   159  			}
   160  			delete(testMsgs, act)
   161  			if act = actM.Get(0).Metadata().Get("foo"); act != "bar" {
   162  				t.Errorf("Wrong metadata returned: %v != bar", act)
   163  			}
   164  			if act = actM.Get(0).Metadata().Get("root_foo"); act != "bar2" {
   165  				t.Errorf("Wrong metadata returned: %v != bar2", act)
   166  			}
   167  		}
   168  		if err = m.Acknowledge(nil); err != nil {
   169  			t.Error(err)
   170  		}
   171  		lMsgs = len(testMsgs)
   172  	}
   173  
   174  	wg.Wait()
   175  }
   176  
   177  func testAMQPBatch(url string, t *testing.T) {
   178  	exchange := "test-exchange"
   179  	key := "benthos-key"
   180  
   181  	conf := NewAMQPConfig()
   182  	conf.URL = url
   183  	conf.QueueDeclare.Enabled = true
   184  	conf.MaxBatchCount = 10
   185  	conf.BindingsDeclare = append(conf.BindingsDeclare, AMQPBindingConfig{
   186  		Exchange:   exchange,
   187  		RoutingKey: key,
   188  	})
   189  
   190  	m, err := NewAMQP(conf, log.Noop(), metrics.Noop())
   191  	if err != nil {
   192  		t.Fatal(err)
   193  	}
   194  
   195  	if err = m.Connect(); err != nil {
   196  		t.Fatal(err)
   197  	}
   198  
   199  	defer func() {
   200  		m.CloseAsync()
   201  		if cErr := m.WaitForClose(time.Second); cErr != nil {
   202  			t.Error(cErr)
   203  		}
   204  	}()
   205  
   206  	var mIn *amqp.Connection
   207  	if mIn, err = amqp.Dial(url); err != nil {
   208  		t.Fatal(err)
   209  	}
   210  
   211  	var mChan *amqp.Channel
   212  	if mChan, err = mIn.Channel(); err != nil {
   213  		t.Fatalf("AMQP Channel: %s", err)
   214  	}
   215  
   216  	defer func() {
   217  		mChan.Close()
   218  		mIn.Close()
   219  	}()
   220  
   221  	N := 10
   222  
   223  	wg := sync.WaitGroup{}
   224  	wg.Add(N)
   225  
   226  	testMsgs := map[string]struct{}{}
   227  	for i := 0; i < N; i++ {
   228  		str := fmt.Sprintf("hello world: %v", i)
   229  		testMsgs[str] = struct{}{}
   230  		go func(testStr string) {
   231  			if pErr := mChan.Publish(exchange, key, false, false, amqp.Publishing{
   232  				Headers: amqp.Table{
   233  					"foo": "bar",
   234  					"root": amqp.Table{
   235  						"foo": "bar2",
   236  					},
   237  				},
   238  				ContentType:     "application/octet-stream",
   239  				ContentEncoding: "",
   240  				Body:            []byte(testStr),
   241  				DeliveryMode:    1, // 1=non-persistent, 2=persistent
   242  				Priority:        0, // 0-9
   243  			}); pErr != nil {
   244  				t.Error(pErr)
   245  			}
   246  			wg.Done()
   247  		}(str)
   248  	}
   249  
   250  	wg.Wait()
   251  
   252  	lMsgs := len(testMsgs)
   253  	for lMsgs > 0 {
   254  		var actM types.Message
   255  		actM, err = m.Read()
   256  		if err != nil {
   257  			t.Error(err)
   258  		} else {
   259  			act := string(actM.Get(0).Get())
   260  			if _, exists := testMsgs[act]; !exists {
   261  				t.Errorf("Unexpected message: %v", act)
   262  			}
   263  			delete(testMsgs, act)
   264  			if act = actM.Get(0).Metadata().Get("foo"); act != "bar" {
   265  				t.Errorf("Wrong metadata returned: %v != bar", act)
   266  			}
   267  			if act = actM.Get(0).Metadata().Get("root_foo"); act != "bar2" {
   268  				t.Errorf("Wrong metadata returned: %v != bar2", act)
   269  			}
   270  		}
   271  		if err = m.Acknowledge(nil); err != nil {
   272  			t.Error(err)
   273  		}
   274  		lMsgs = len(testMsgs)
   275  	}
   276  }
   277  
   278  func testAMQPDisconnect(url string, t *testing.T) {
   279  	conf := NewAMQPConfig()
   280  	conf.URL = url
   281  	conf.QueueDeclare.Enabled = true
   282  
   283  	m, err := NewAMQP(conf, log.Noop(), metrics.Noop())
   284  	if err != nil {
   285  		t.Fatal(err)
   286  	}
   287  
   288  	if err = m.Connect(); err != nil {
   289  		t.Fatal(err)
   290  	}
   291  
   292  	wg := sync.WaitGroup{}
   293  	wg.Add(1)
   294  	go func() {
   295  		m.CloseAsync()
   296  		if cErr := m.WaitForClose(time.Second); cErr != nil {
   297  			t.Error(cErr)
   298  		}
   299  		wg.Done()
   300  	}()
   301  
   302  	if _, err = m.Read(); err != types.ErrTypeClosed && err != types.ErrNotConnected {
   303  		t.Errorf("Wrong error: %v != %v", err, types.ErrTypeClosed)
   304  	}
   305  
   306  	wg.Wait()
   307  }