github.com/moleculer-go/moleculer@v0.3.3/transit/amqp/amqp_test.go (about)

     1  package amqp
     2  
     3  import (
     4  	"sort"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/moleculer-go/moleculer"
     9  	"github.com/moleculer-go/moleculer/broker"
    10  	. "github.com/onsi/ginkgo"
    11  	. "github.com/onsi/gomega"
    12  )
    13  
    14  var queues = []string{
    15  	// RPC
    16  	"MOL.DISCONNECT.test-rpc-client",
    17  	"MOL.DISCONNECT.test-rpc-worker1",
    18  	"MOL.DISCONNECT.test-rpc-worker2",
    19  	"MOL.DISCONNECT.test-rpc-worker3",
    20  	"MOL.DISCOVER.test-rpc-client",
    21  	"MOL.DISCOVER.test-rpc-worker1",
    22  	"MOL.DISCOVER.test-rpc-worker2",
    23  	"MOL.DISCOVER.test-rpc-worker3",
    24  	"MOL.EVENT.test-rpc-client",
    25  	"MOL.EVENT.test-rpc-worker1",
    26  	"MOL.EVENT.test-rpc-worker2",
    27  	"MOL.EVENT.test-rpc-worker3",
    28  	"MOL.HEARTBEAT.test-rpc-client",
    29  	"MOL.HEARTBEAT.test-rpc-worker1",
    30  	"MOL.HEARTBEAT.test-rpc-worker2",
    31  	"MOL.HEARTBEAT.test-rpc-worker3",
    32  	"MOL.INFO.test-rpc-client",
    33  	"MOL.INFO.test-rpc-worker1",
    34  	"MOL.INFO.test-rpc-worker2",
    35  	"MOL.INFO.test-rpc-worker3",
    36  	"MOL.PING.test-rpc-client",
    37  	"MOL.PING.test-rpc-worker1",
    38  	"MOL.PING.test-rpc-worker2",
    39  	"MOL.PING.test-rpc-worker3",
    40  	"MOL.PONG.test-rpc-client",
    41  	"MOL.PONG.test-rpc-worker1",
    42  	"MOL.PONG.test-rpc-worker2",
    43  	"MOL.PONG.test-rpc-worker3",
    44  	"MOL.REQ.test-rpc-client",
    45  	"MOL.REQ.test-rpc-worker1",
    46  	"MOL.REQ.test-rpc-worker2",
    47  	"MOL.REQ.test-rpc-worker3",
    48  	"MOL.RES.test-rpc-client",
    49  	"MOL.RES.test-rpc-worker1",
    50  	"MOL.RES.test-rpc-worker2",
    51  	"MOL.RES.test-rpc-worker3",
    52  	// Emit
    53  	"MOL.DISCONNECT.test-emit-event-pub",
    54  	"MOL.DISCONNECT.test-emit-event-sub1",
    55  	"MOL.DISCONNECT.test-emit-event-sub2",
    56  	"MOL.DISCONNECT.test-emit-event-sub3",
    57  	"MOL.DISCOVER.test-emit-event-pub",
    58  	"MOL.DISCOVER.test-emit-event-sub1",
    59  	"MOL.DISCOVER.test-emit-event-sub2",
    60  	"MOL.DISCOVER.test-emit-event-sub3",
    61  	"MOL.EVENT.test-emit-event-pub",
    62  	"MOL.EVENT.test-emit-event-sub1",
    63  	"MOL.EVENT.test-emit-event-sub2",
    64  	"MOL.EVENT.test-emit-event-sub3",
    65  	"MOL.HEARTBEAT.test-emit-event-pub",
    66  	"MOL.HEARTBEAT.test-emit-event-sub1",
    67  	"MOL.HEARTBEAT.test-emit-event-sub2",
    68  	"MOL.HEARTBEAT.test-emit-event-sub3",
    69  	"MOL.INFO.test-emit-event-pub",
    70  	"MOL.INFO.test-emit-event-sub1",
    71  	"MOL.INFO.test-emit-event-sub2",
    72  	"MOL.INFO.test-emit-event-sub3",
    73  	"MOL.PING.test-emit-event-pub",
    74  	"MOL.PING.test-emit-event-sub1",
    75  	"MOL.PING.test-emit-event-sub2",
    76  	"MOL.PING.test-emit-event-sub3",
    77  	"MOL.PONG.test-emit-event-pub",
    78  	"MOL.PONG.test-emit-event-sub1",
    79  	"MOL.PONG.test-emit-event-sub2",
    80  	"MOL.PONG.test-emit-event-sub3",
    81  	"MOL.REQ.test-emit-event-pub",
    82  	"MOL.REQ.test-emit-event-sub1",
    83  	"MOL.REQ.test-emit-event-sub2",
    84  	"MOL.REQ.test-emit-event-sub3",
    85  	"MOL.RES.test-emit-event-pub",
    86  	"MOL.RES.test-emit-event-sub1",
    87  	"MOL.RES.test-emit-event-sub2",
    88  	"MOL.RES.test-emit-event-sub3",
    89  	// Broadcast
    90  	"MOL.REQ.test-broadcast-event-pub",
    91  	"MOL.REQ.test-broadcast-event-sub1",
    92  	"MOL.REQ.test-broadcast-event-sub2",
    93  	"MOL.REQ.test-broadcast-event-sub3",
    94  	"MOL.RES.test-broadcast-event-pub",
    95  	"MOL.RES.test-broadcast-event-sub1",
    96  	"MOL.RES.test-broadcast-event-sub2",
    97  	"MOL.RES.test-broadcast-event-sub3",
    98  	"MOL.DISCONNECT.test-broadcast-event-pub",
    99  	"MOL.DISCONNECT.test-broadcast-event-sub1",
   100  	"MOL.DISCONNECT.test-broadcast-event-sub2",
   101  	"MOL.DISCONNECT.test-broadcast-event-sub3",
   102  	"MOL.DISCOVER.test-broadcast-event-pub",
   103  	"MOL.DISCOVER.test-broadcast-event-sub1",
   104  	"MOL.DISCOVER.test-broadcast-event-sub2",
   105  	"MOL.DISCOVER.test-broadcast-event-sub3",
   106  	"MOL.EVENT.test-broadcast-event-pub",
   107  	"MOL.EVENT.test-broadcast-event-sub1",
   108  	"MOL.EVENT.test-broadcast-event-sub2",
   109  	"MOL.EVENT.test-broadcast-event-sub3",
   110  	"MOL.HEARTBEAT.test-broadcast-event-pub",
   111  	"MOL.HEARTBEAT.test-broadcast-event-sub1",
   112  	"MOL.HEARTBEAT.test-broadcast-event-sub2",
   113  	"MOL.HEARTBEAT.test-broadcast-event-sub3",
   114  	"MOL.INFO.test-broadcast-event-pub",
   115  	"MOL.INFO.test-broadcast-event-sub1",
   116  	"MOL.INFO.test-broadcast-event-sub2",
   117  	"MOL.INFO.test-broadcast-event-sub3",
   118  	"MOL.PING.test-broadcast-event-pub",
   119  	"MOL.PING.test-broadcast-event-sub1",
   120  	"MOL.PING.test-broadcast-event-sub2",
   121  	"MOL.PING.test-broadcast-event-sub3",
   122  	"MOL.PONG.test-broadcast-event-pub",
   123  	"MOL.PONG.test-broadcast-event-sub1",
   124  	"MOL.PONG.test-broadcast-event-sub2",
   125  	"MOL.PONG.test-broadcast-event-sub3",
   126  }
   127  var exchanges = []string{
   128  	"MOL.DISCONNECT",
   129  	"MOL.DISCOVER",
   130  	"MOL.HEARTBEAT",
   131  	"MOL.INFO",
   132  	"MOL.PING",
   133  }
   134  
   135  var _ = Describe("Test AMQPTransporter", func() {
   136  	// Delete all queues and exchanges before and after suite
   137  	BeforeSuite(func() {
   138  		purge(queues, exchanges, true)
   139  	})
   140  	AfterSuite(func() {
   141  		purge(queues, exchanges, true)
   142  	})
   143  
   144  	// Clear all queues between each test.
   145  	AfterEach(func() {
   146  		purge(queues, exchanges, false)
   147  	})
   148  
   149  	Describe("Test AMQPTransporter RPC with built-in balancer", func() {
   150  		var logs []map[string]interface{}
   151  
   152  		client := createNode("test-rpc", "client", nil)
   153  		worker1 := createActionWorker(1, &logs)
   154  		worker2 := createActionWorker(2, &logs)
   155  		worker3 := createActionWorker(3, &logs)
   156  
   157  		brokers := []*broker.ServiceBroker{client, worker1, worker2, worker3}
   158  
   159  		callShortDelay := func() chan moleculer.Payload {
   160  			return client.Call("test.hello", map[string]interface{}{"delay": 20})
   161  		}
   162  
   163  		BeforeEach(func() {
   164  			for _, bkr := range brokers {
   165  				bkr.Start()
   166  			}
   167  			time.Sleep(time.Second)
   168  		})
   169  		AfterEach(func() {
   170  			for _, bkr := range brokers {
   171  				logs = nil
   172  				bkr.Stop()
   173  			}
   174  			time.Sleep(time.Second)
   175  		})
   176  
   177  		It("Only one node should receive any given request", func() {
   178  			// Ensure that messages are not broadcast to individual queues.
   179  			result := <-callShortDelay()
   180  			Expect(result.Error()).Should(Succeed())
   181  			Expect(logs).Should(HaveLen(2))
   182  			Expect(filter(&logs, "receive")).Should(HaveLen(1))
   183  			Expect(filter(&logs, "respond")).Should(HaveLen(1))
   184  		})
   185  
   186  		It("Should load balance requests to available nodes.", func() {
   187  			// Ensure that messages are evenly distributed
   188  			wg := sync.WaitGroup{}
   189  			res := make([]int, 12)
   190  			for i := 0; i < 12; i++ {
   191  				wg.Add(1)
   192  				go func(index int) {
   193  					payload := <-callShortDelay()
   194  					res[index] = payload.Get("worker").Int()
   195  					wg.Done()
   196  				}(i)
   197  			}
   198  
   199  			wg.Wait()
   200  
   201  			Expect(res).Should(HaveLen(12))
   202  			Expect(res).Should(SatisfyAll(
   203  				ContainElement(1),
   204  				ContainElement(2),
   205  				ContainElement(3),
   206  			))
   207  			Expect(res).Should(WithTransform(
   208  				func(workers []int) []int {
   209  					sort.Ints(workers)
   210  					return workers
   211  				}, Equal([]int{
   212  					1, 1, 1, 1,
   213  					2, 2, 2, 2,
   214  					3, 3, 3, 3,
   215  				}),
   216  			))
   217  		})
   218  
   219  		It("Nodes should only receive one request at a time by default", func() {
   220  			// Ensure that prefetch is working. This relies on message acking happening after the action
   221  			// handler runs.
   222  			worker2.Stop()
   223  			worker3.Stop()
   224  
   225  			wg := sync.WaitGroup{}
   226  			for i := 0; i < 3; i++ {
   227  				wg.Add(1)
   228  				go func(index int) {
   229  					<-callShortDelay()
   230  					wg.Done()
   231  				}(i)
   232  			}
   233  			wg.Wait()
   234  
   235  			for idx, cur := range logs {
   236  				// All requests should be handled by singe node
   237  				Expect(cur["worker"]).Should(Equal(1))
   238  
   239  				// Order should go from old -> new
   240  				if idx+1 < len(logs) {
   241  					Expect(cur["timestamp"]).Should(BeTemporally("<=", logs[idx+1]["timestamp"].(time.Time)))
   242  				}
   243  
   244  				// If receive and respond don't alternate requests are concurrent
   245  				if idx%2 == 0 {
   246  					Expect(cur["type"]).Should(Equal("receive"))
   247  				} else {
   248  					Expect(cur["type"]).Should(Equal("respond"))
   249  				}
   250  			}
   251  		})
   252  	})
   253  
   254  	XDescribe("Test AMQPTransporter event emit with built-in balancer", func() {
   255  		var logs []string
   256  
   257  		pub := createEmitWorker("pub", "emit-handler", &logs)
   258  		sub1 := createEmitWorker("sub1", "emit-handler", &logs)
   259  		sub2 := createEmitWorker("sub2", "emit-handler", &logs)
   260  		sub3 := createEmitWorker("sub3", "other-handler", &logs)
   261  
   262  		brokers := []*broker.ServiceBroker{pub, sub1, sub2, sub3}
   263  
   264  		// Reset Flow array and start services
   265  		BeforeEach(func() {
   266  			logs = nil
   267  
   268  			for _, bkr := range brokers {
   269  				bkr.Start()
   270  			}
   271  			time.Sleep(time.Second)
   272  		})
   273  
   274  		// Stop services and clear queues
   275  		AfterEach(func() {
   276  			for _, bkr := range brokers {
   277  				bkr.Stop()
   278  			}
   279  			logs = nil
   280  			time.Sleep(time.Second)
   281  		})
   282  
   283  		It("should send emit event to only one service", func() {
   284  			for i := 0; i < 6; i++ {
   285  				pub.Emit("hello.world2", map[string]interface{}{"testing": true})
   286  			}
   287  
   288  			time.Sleep(2 * time.Second)
   289  
   290  			Expect(logs).Should(HaveLen(12))
   291  			Expect(logs).Should(SatisfyAll(
   292  				ContainElement("pub"),
   293  				// TODO: should uncomment when 'preferLocal' registry parameter will be exposed to config
   294  				//ContainElement("sub1"),
   295  				//ContainElement("sub2"),
   296  				ContainElement("sub3"),
   297  			))
   298  			Expect(logs).Should(WithTransform(
   299  				func(items []string) []string {
   300  					var result []string
   301  					for _, item := range items {
   302  						if item == "sub3" {
   303  							result = append(result, item)
   304  						}
   305  					}
   306  
   307  					return result
   308  				},
   309  				HaveLen(6),
   310  			))
   311  		})
   312  	})
   313  
   314  	Describe("Test AMQPTransporter event broadcast with built-in balancer", func() {
   315  		var logs []string
   316  
   317  		pub := createBroadcastWorker("pub", &logs)
   318  		sub1 := createBroadcastWorker("sub1", &logs)
   319  		sub2 := createBroadcastWorker("sub2", &logs)
   320  		sub3 := createBroadcastWorker("sub3", &logs)
   321  
   322  		BeforeEach(func() {
   323  			logs = nil
   324  
   325  			go func() {
   326  				time.Sleep(1500 * time.Millisecond)
   327  				sub3.Start()
   328  			}()
   329  
   330  			pub.Start()
   331  			sub1.Start()
   332  			sub2.Start()
   333  
   334  			time.Sleep(time.Second)
   335  		})
   336  
   337  		AfterEach(func() {
   338  			pub.Stop()
   339  			sub1.Stop()
   340  			sub2.Stop()
   341  			sub3.Stop()
   342  		})
   343  
   344  		It("Should send an event to all subscribed nodes.", func() {
   345  			pub.Broadcast("hello.world", map[string]interface{}{"testing": true})
   346  
   347  			time.Sleep(2 * time.Second)
   348  
   349  			Expect(logs).Should(HaveLen(3))
   350  			Expect(logs).Should(SatisfyAll(
   351  				ContainElement("pub"),
   352  				ContainElement("sub1"),
   353  				ContainElement("sub2"),
   354  			))
   355  		}, 10)
   356  	})
   357  })